feat: whyrating - initial project from turbostarter boilerplate

This commit is contained in:
Alejandro Gutiérrez
2026-02-04 01:54:52 +01:00
commit 5cdc07cd39
1618 changed files with 338230 additions and 0 deletions

View File

@@ -0,0 +1,86 @@
---
title: Blog
description: Learn how to manage your blog content.
url: /docs/web/cms/blog
---
# Blog
TurboStarter comes with a pre-configured blog implementation that allows you to manage your blog content.
## Creating a new blog post
To create a new blog post, you need to create a new directory (its name will be used as the slug of the blog post) with `.mdx` files in the `packages/cms/src/collections/blog/content` directory. Each file in this directory should be named after the locale it belongs to (e.g `en.mdx`, `es.mdx`, etc.).
The file will start with a [frontmatter](https://mdxjs.com/guides/frontmatter/) block, which is a yaml-like block that contains metadata about the post. The frontmatter block should be surrounded by three dashes (`---`).
```mdx title="packages/cms/src/collections/blog/content/my-first-blog-post/en.mdx"
---
title: Quick Tips to Improve Your Skills Right Away
description: Whether you're learning a new technical skill or working on personal development, these quick tips can help you improve right away. Learn how to break down your goals, practice consistently, and track your progress using Markdown.
publishedAt: 2023-04-19
tags: [learning, skills, progress]
thumbnail: https://images.unsplash.com/photo-1483639130939-150975af84e5?q=80&w=2370&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D
status: published
---
```
Let's break down the frontmatter fields:
* `title`: The title of the blog post (it will be also used to generate a slug for the blog post)
* `description`: The description of the blog post
* `publishedAt`: The date when the blog post was published
* `tags`: The tags of the blog post
* `thumbnail`: The thumbnail of the blog post
* `status`: The status of the blog post (could be `published` or `draft`)
After the frontmatter block, you can add the content of the blog post:
```mdx title="packages/cms/src/collections/blog/content/my-first-blog-post/en.mdx"
# Quick Tips to Improve Your Skills Right Away
Awesome paragraph!
[Link](https://www.turbostarter.dev)
<Callout>This is a callout component.</Callout>
...
```
You can consume the content the same as it's described in [Content Collections](/docs/web/cms/content-collections).
## BONUS: Using custom components
As you're using MDX, you can use **any React component** in your blog posts. Just define it as a normal React component and pass it to `<MdxContent />` in `components` prop:
```tsx title="apps/web/src/app/content/page.tsx"
import { MyComponent } from "~/modules/common/my-component";
export default function Page() {
return (
<MDXContent
code={data.body}
components={{ ...defaultMdxComponents, MyComponent }}
/>
);
}
```
Then, you would be able to use it in your document content and it will rendered on the page as a result:
```mdx title="packages/cms/src/collections/blog/content/my-first-blog-post/en.mdx"
...
# Heading
Excellent paragraph!
<MyComponent />
1. First item
2. Second item
3. Third item
```
TurboStarter ships with a set of default components that you can use in your blog posts, e.g. `<Callout />`, `<Card />` etc. Use them or define your own to make your blog posts more engaging.

View File

@@ -0,0 +1,98 @@
---
title: Content Collections
description: Get started with Content Collections.
url: /docs/web/cms/content-collections
---
# Content Collections
By default, TurboStarter uses [Content Collections](https://www.content-collections.dev/) to store and retrieve content from the MDX files.
Content from there is used to populate data in the following places:
* **Blog**
* **Legal pages**
* **Documentation**
<Callout title="Why content-collections?">
It is a great alternative to headless CMS like Contentful or Prismic based on MDX (a more powerful version of markdown). It is free, open source and the content is located right in your repository.
</Callout>
Of course, you can add more collections and views, as it's very flexible.
## Defining new collection
To define a new collection, you need to create a new file in the `packages/cms/src/collections` directory:
```ts title="packages/cms/src/collections/legal/index.ts"
import { defineCollection } from "@content-collections/core";
export const legal = defineCollection({
name: "legal",
directory: "src/collections/legal/content",
include: "**/*.mdx",
schema: (z) => ({
title: z.string(),
description: z.string(),
}),
transform: async (doc, context) => {
const mdx = await transformMDX(doc, context);
return {
...mdx,
slug: doc._meta.directory,
locale: doc._meta.fileName.split(".")[0],
};
},
});
```
Then it's passed to the config in `packages/cms/content-collections.ts` file which is used to generate types and parse content from MDX files.
```tsx title="packages/cms/content-collections.ts"
import { defineConfig } from "@content-collections/core";
import { legal } from "./src/collections/legal";
export default defineConfig({
collections: [legal],
});
```
When you run a development server, content collections will be automatically rebuilt (in `.content-collections` directory) and you will be able to import the content and metadata of each file in your application.
<Callout title="It's fully type-safe!">
By exporting the generated content you get fully type-safe API to interact
with the content. We can have type safety on the data that we're receiving
from the MDX files.
</Callout>
## Using content collections
To get some content from `@turbostarter/cms` package, you need to use the exposed API that we described in the [Overview section](/docs/web/cms/overview#api):
```tsx title="apps/web/src/app/[locale](marketing)/legal/[slug]/page.tsx"
import { content } from "@turbostarter/cms";
export default async function Page({
params,
}: {
params: Promise<{ slug: string; locale: string }>;
}) {
const item = getContentItemBySlug({
collection: CollectionType.LEGAL,
slug: (await params).slug,
locale: (await params).locale,
});
return <h1>{title}</h1>;
}
```
Voila! You can now access the content from the MDX files.
<Cards>
<Card title="Content Collections" description="content-collections.dev" href="https://www.content-collections.dev/" />
<Card title="MDX" description="mdxjs.com" href="https://mdxjs.com/" />
</Cards>

View File

@@ -0,0 +1,67 @@
---
title: Overview
description: Manage your content in TurboStarter.
url: /docs/web/cms/overview
---
# Overview
TurboStarter implements a CMS interface that abstracts the implementation from where you store your data. It provides a simple API to interact with your data, and it's easy to extend and customize.
By default, the starter kit ships with these implementations in place:
1. [Content Collections](https://www.content-collections.dev/) - a headless CMS that uses [MDX](https://mdxjs.com/) files to store your content.
The implementation is available under `@turbostarter/cms` package, here we'll go over how to use it.
## API
The CMS package provides a simple, unified API to interact with the content. It's the same for all the providers, so you can easily use it with any of the implementations without changing the code.
### Fetching content items
To fetch items from your colletions, you can use the `getContentItems` function.
```ts
import { getContentItems } from "@turbostarter/cms";
const { items, count } = getContentItems({
collection: CollectionType.BLOG,
tags: [ContentTag.SKILLS],
sortBy: "publishedAt",
sortOrder: SortOrder.DESCENDING,
status: ContentStatus.PUBLISHED,
locale: "en",
});
```
It accepts an object with the following properties:
* `collection`: The collection to fetch the items from.
* `tags`: The tags to filter the items by.
* `sortBy`: The field to sort the items by.
* `sortOrder`: The order to sort the items in.
* `status`: The status of the items to fetch. It can be `published` or `draft`. By default, only `published` items are fetched.
* `locale`: The locale to fetch the items in. By default, all locales are fetched.
### Fetching a single content item
To fetch a single content item, you can use the `getContentItemBySlug` function.
```ts
import { getContentItemBySlug } from "@turbostarter/cms";
const item = getContentItemBySlug({
collection: CollectionType.BLOG,
slug: "my-first-blog-post",
status: ContentStatus.PUBLISHED,
locale: "en",
});
```
It accepts an object with the following properties:
* `collection`: The collection to fetch the item from.
* `slug`: The slug of the item to fetch.
* `status`: The status of the item to fetch. It can be `published` or `draft`. By default, only `published` items are fetched.
* `locale`: The locale to fetch the item in. By default, all locales are fetched.