# Nuxt Content v3 Welcome to Nuxt Content v3, a major upgrade that brings enhanced performance and innovative features to your Nuxt projects. This latest iteration of our Git-based CMS is optimized for modern application development. ## What's New? ### Content Collections Collections organize related items within your project, helping you manage large datasets more efficiently. Key benefits include: - **Structured Data**: Configure database architecture and define collections in [`content.config.ts`](https://content.nuxt.com/docs/collections/define#defining-collections) - **Type-safe Queries**: Direct TypeScript integration across all utilities - **Automatic Validation**: Ensure data consistency across frontmatter fields and data files (json, yml...) - **Advanced Query Builder**: Filter, sort, and paginate your collections with ease - **Studio Integration**: Enhanced form generation and optimal editing experience through [Studio](https://nuxt.studio){rel=""nofollow""} Learn more about [Content Collections](https://content.nuxt.com/docs/collections/define). ### Improved Performance A significant challenge in v2 was the large bundle size needed for storing files, particularly affecting serverless deployments. V3 addresses this by transitioning to SQL-based storage in production. This switch requires zero configuration, supporting development mode, static generation, server hosting, serverless and edge deployments. ::prose-note The new database system enhances the way your data files are stored and structured, ensuring better performance and scalability. This update is entirely behind the scenes and does not affect the file types you can use in Content ( `yml` , `json` , and `markdown` ). :: Benefits include: - **Optimized Queries**: SQL storage enables ultra-fast data retrieval - **Universal Compatibility**: Our adapter-based system integrates SQL databases across all deployment modes ([server](https://content.nuxt.com/docs/deploy/server), [serverless](https://content.nuxt.com/docs/deploy/serverless) and [static](https://content.nuxt.com/docs/deploy/static)). We welcome community contributions for additional adapters. ### TypeScript Integration The new collections system provides automatic TypeScript types for all your data. Every utility and API is strongly typed based on your collection definitions, ensuring robust type safety throughout development. ### Nuxt Studio Integration :badge[Soon]{color="neutral"} [Nuxt Studio](https://nuxt.studio){rel=""nofollow""} and v3 are designed to complement each other perfectly. The [studio module](https://github.com/nuxt-content/nuxt-studio){rel=""nofollow""} is creating an ideal environment where developers can focus on code while team members manage content through an intuitive interface. --- We're excited for you to explore these new capabilities. Dive into our documentation to learn more about integrating the module and implementing best practices in your next project. ## Content V2 Migration Learn how to migrate from Content v2 to v3 in the [migration guide](https://content.nuxt.com/docs/getting-started/migration). # Installation ## Install the Package Choose your preferred package manager to install Nuxt Content v3: ::code-group ```bash [pnpm] pnpm add @nuxt/content ``` ```bash [yarn] yarn add @nuxt/content ``` ```bash [npm] npm install @nuxt/content ``` ```bash [bun] bun add @nuxt/content ``` :: ## Register the Module Add the Nuxt Content module to your `nuxt.config.ts`: ```ts [nuxt.config.ts] export default defineNuxtConfig({ modules: ['@nuxt/content'] }) ``` ## Automatic Setup When starting a new Nuxt project with the `create-nuxt` CLI, you can simply select `@nuxt/content` from the interactive module selector. This will automatically install and register the module for you. ::code-group ```bash [npm] npm create nuxt ``` ```bash [yarn] yarn create nuxt ``` ```bash [pnpm] pnpm create nuxt ``` ```bash [bun] bun create nuxt ``` ```bash [deno] deno -A npm:create-nuxt@latest ``` :: ::note{color="warning"} When you run your project in Node.js, Nuxt Content will ask you about the database connector to use. You can choose to install `better-sqlite3` or `sqlite3` package. :br If you don't want to install any package, you can use native SQLite from Node.js\@v22.5.0 or newer. Checkout [`experimental.nativeSqlite` configuration](https://content.nuxt.com/docs/getting-started/configuration#experimentalnativesqlite-deprecated-use-sqliteconnector). :: ::note{color="warning"} If you use **pnpm v10+**, dependency build scripts are not executed by default. :br Since `better-sqlite3` and `sqlite3` rely on a postinstall/build step to generate native bindings, you may encounter errors such as: :br `Could not locate the bindings file` :br To resolve this, you can approve the required build scripts by running: :::code-group ```bash [pnpm] pnpm approve-builds ``` ::: Alternatively, if you need a non-interactive setup (for example in CI), you can explicitly allow native builds by adding the following configuration to your project root `package.json`: ```json [package.json] { "pnpm": { "onlyBuiltDependencies": [ "better-sqlite3", "sqlite3" ] } } ``` :: ## Create your First Collection Create a `content.config.ts` file in your project root directory: ```ts [content.config.ts] import { defineContentConfig, defineCollection } from '@nuxt/content' export default defineContentConfig({ collections: { content: defineCollection({ type: 'page', source: '**/*.md' }) } }) ``` This configuration creates a default `content` collection that processes all Markdown files located in the `content` folder of your project. You can customize the collection settings based on your needs. ::tip The `type: page` means there is a 1-to-1 relationship between content files and pages on your site. :: ::note{to="https://content.nuxt.com/docs/collections/define"} Learn more in our **Collections guide** . :: ## Create your First Markdown Page Create a `content/index.md` file in your project root directory: ```md [content/index.md] # My First Page Here is some content. ``` Read more about writing [Markdown pages](https://content.nuxt.com/docs/files/markdown). ## Display your Page Create a `pages/index.vue` file and display the page content: ```vue [pages/index.vue] ``` ::note{icon="i-lucide-info"} If you are installing Nuxt Content in a new Nuxt project and you didn't have `pages` directory, you also need to update the `app.vue` file to allow rendering the pages by adding the `NuxtPage` component. (If you already have some pages in your project, you are good to go.) ```vue [app.vue] ``` :: ::tip{icon="i-lucide-rocket"} That's it! You've now created your first Nuxt Content page. :: # Configuration To configure the content module and customize its behavior, you can use the `content` property in your `nuxt.config`: ```ts [nuxt.config.ts] export default defineNuxtConfig({ content: { // Options } }) ``` ::note{to="https://github.com/nuxt-modules/mdc#configurations"} In addition to configuring via `content.markdown` , you can use Markdown Components (MDC) to customize the rendering of Markdown elements with `mdc` property. :: ## `build` Nuxt Content read and parse all the available contents at build time. This option gives you control over parsing contents. ### `markdown` Configure markdown parser. #### `toc` ::code-group ```ts [Default] toc: { depth: 2, searchDepth: 2 } ``` ```ts [Signature] type Toc = { depth: number searchDepth: number } ``` :: Control behavior of Table of Contents generation. Value: - `depth`: Maximum heading depth to include in the table of contents. - `searchDepth`: Maximum depth of nested tags to search for heading. ```ts [nuxt.config.ts] export default defineNuxtConfig({ content: { build: { markdown: { toc: { depth: 3, // include h3 headings } } } } }) ``` #### `remarkPlugins` ::code-group ```ts [Default] remarkPlugins: {} ``` ```ts [Signature] type RemarkPlugins = Record ``` :: A list of [remark](https://github.com/remarkjs/remark){rel=""nofollow""} plugins to use. ```ts [nuxt.config.ts] export default defineNuxtConfig({ content: { build: { markdown: { // Object syntax can be used to override default options remarkPlugins: { // Override remark-emoji options 'remark-emoji': { options: { emoticon: true } }, // Disable remark-gfm 'remark-gfm': false, // Add remark-oembed 'remark-oembed': { // Options } }, } } } }) ``` #### `rehypePlugins` ::code-group ```ts [Default] rehypePlugins: {} ``` ```ts [Signature] type RehypePlugins = object ``` :: A list of [rehype](https://github.com/remarkjs/remark-rehype){rel=""nofollow""} plugins to use. ```ts [nuxt.config.ts] export default defineNuxtConfig({ content: { build: { markdown: { // Object syntax can be used to override default options rehypePlugins: { 'rehype-figure': { } }, } } } }) ``` #### `contentHeading` ::code-group ```ts [Default] contentHeading: true ``` ```ts [Signature] type ContentHeading = boolean ``` :: Setting this option to `false` disables the automatic generation of `title` and `description` fields that are normally extracted from the first H1 heading and the paragraphs that follow it. #### `highlight` ::code-group ```ts [Default] highlight: false ``` ```ts [Signature] type Highlight = false | object ``` :: Nuxt Content uses [Shiki](https://github.com/shikijs/shiki){rel=""nofollow""} to provide syntax highlighting for [`ProsePre`](https://content.nuxt.com/docs/components/prose#prosepre) and [`ProseCode`](https://content.nuxt.com/docs/components/prose#prosecode). | Option | Type | Description | | ------- | -------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- | | `theme` | `ShikiTheme` or `Record` | The [color theme](https://github.com/shikijs/shiki/blob/main/docs/themes.md){rel=""nofollow""} to use. | | `langs` | `ShikiLang[]` | The [loaded languages](https://github.com/shikijs/shiki/blob/main/docs/languages.md){rel=""nofollow""} available for highlighting. | - `highlight.theme` Theme can be specified by a single string but also supports an object with multiple themes. This option is compatible with [Color Mode module](https://color-mode.nuxtjs.org/){rel=""nofollow""}. If you are using multiple themes, it's recommended to always have a `default` theme specified. ```ts [nuxt.config.ts] export default defineNuxtConfig({ content: { build: { markdown: { highlight: { // Theme used in all color schemes. theme: 'github-light', // OR theme: { // Default theme (same as single string) default: 'github-light', // Theme used if `html.dark` dark: 'github-dark', // Theme used if `html.sepia` sepia: 'monokai' } } } } } }) ``` - `highlight.langs` By default, the module loads a couple of languages for syntax highlighting: ```ts [Default] ['json', 'js', 'ts', 'html', 'css', 'vue', 'shell', 'mdc', 'md', 'yaml'] ``` If you plan to use code samples of other languages, you need to define the language in these options. ```ts [nuxt.config.ts] export default defineNuxtConfig({ content: { build: { markdown: { highlight: { langs: [ 'c', 'cpp', 'java' ] } } } } }) ``` If you wish to add highlighting for an unsupported language, you can do so by loading the grammar file for the language. ```ts [nuxt.config.ts] import { readFileSync } from 'node:fs' export default defineNuxtConfig({ content: { build: { markdown: { highlight: { langs: [ // Read more about Shiki languages: https://shiki.style/guide/load-lang JSON.parse( readFileSync('./shiki/languages/gdscript.tmLanguage.json', 'utf-8'), ), ] } } } } }) ``` Read more about adding languages in the [Shiki documentation](https://github.com/shikijs/shiki/blob/main/docs/languages.md#adding-grammar){rel=""nofollow""}. ### `pathMeta` Content module uses files path to generate the slug, default title and content order, you can customize this behavior with `pathMeta` option. #### `pathMeta.forceLeadingSlash` If set to `true`, the path will be prefixed with a leading slash. Default value is `true`. #### `pathMeta.slugifyOptions` Content module uses [slugify](https://github.com/simov/slugify){rel=""nofollow""} to generate the slug, you can customize the behavior of slugify with this option. Checkout [slugify options](https://github.com/simov/slugify#options){rel=""nofollow""} for more information. ### `transformers` Nuxt Content has specific transformers for each content type to parse the raw content and prepare it for querying and rendering. Using this option you can define custom transformers to support new content types or improve functionalities of supported content types. ::code-group ```ts [nuxt.config.ts] export default defineNuxtConfig({ content: { build: { transformers: [ '~/transformers/title-suffix', ], }, }, }) ``` ```ts [~/transformers/title-suffix.ts] import { defineTransformer } from '@nuxt/content' export default defineTransformer({ name: 'title-suffix', extensions: ['.md'], transform(file) { return { ...file, title: file.title + ' (suffix)', } }, }) ``` :: Read more about transformers in the [Transformers](https://content.nuxt.com/docs/advanced/transformers) documentation. ## `database` By default Nuxt Content uses a local SQLite database to store and query content. If you like to use another database or you plan to deploy on Cloudflare Workers, you can modify this option. Here is the list of supported database adapters: ### `SQLite` If you want to change the default database location and move it to elsewhere you can use `sqlite` adapter to do so. This is the default value to the `database` option. Depending on your runtime-environment different sqlite adapters will be used (Node: better-sqlite-3, Bun: bun\:sqlite). ```ts [nuxt.config.ts] export default defineNuxtConfig({ content: { database: { type: 'sqlite', filename: 'SQLITE_DB_LOCATION' } } }) ``` If you can't use a normal file-backed SQLite database (for example due to read-only filesystems or platform limitations), you can run SQLite entirely in memory. Nuxt Content will restore the database from the generated dump on first query. On serverless platforms this database will be recreated on each cold start; prerender as many routes as possible to avoid repeated runtime initialization. ```ts [nuxt.config.ts] export default defineNuxtConfig({ content: { database: { type: 'sqlite', filename: ':memory:' } } }) ``` ### `D1` If you plan to deploy your application to Cloudflare workers, you need to use the `d1` database adapter. Create a `d1` binding in the Cloudflare dashboard and fill in the `bindingName` field. ```ts [nuxt.config.ts] export default defineNuxtConfig({ content: { database: { type: 'd1', bindingName: 'CF_BINDING_NAME' } } }) ``` ### `Postgres` If you plan to deploy your application using PostgreSQL database you need to use the `postgresql` database adapter. First, make sure to install the `pg` package: ```bash [Terminal] npm i pg ``` Then, configure the `postgresql` adapter in your `nuxt.config.ts`: ```ts [nuxt.config.ts] export default defineNuxtConfig({ content: { database: { type: 'postgresql', url: process.env.POSTGRES_URL, /* Other options for `pg` */ } } }) ``` ### `LibSQL` If you plan to deploy your application using a LibSQL database you need to use the `libsql` database adapter. First, make sure to install the `@libsql/client` package: ```bash [Terminal] npm i @libsql/client ``` Then, configure the `libsql` adapter in your `nuxt.config.ts`: ```ts [nuxt.config.ts] export default defineNuxtConfig({ content: { database: { type: 'libsql', url: process.env.TURSO_DATABASE_URL, authToken: process.env.TURSO_AUTH_TOKEN, } } }) ``` ::note The most popular LibSQL hosting services is [Turso](https://turso.tech/){rel=""nofollow""} . :: ### `PGlite` If you plan to deploy your application using a PGlite database you need to use the `pglite` database adapter. First, make sure to install the `@electric-sql/pglite` package: ```bash [Terminal] npm i @electric-sql/pglite ``` Then, configure the `pglite` adapter in your `nuxt.config.ts`: ```ts [nuxt.config.ts] export default defineNuxtConfig({ content: { database: { type: 'pglite', dataDir: '.data/content/pglite' } } }) ``` ::note We recommend to only use PGlite in development. :: ## `renderer` Configure content renderer. ### `anchorLinks` ::code-group ```ts [Default] { h2: true, h3: true, h4: true } ``` ```ts [Signature] type AnchorLinks = boolean | Record<'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6', boolean> ``` :: Control anchor link generation, by default it generates anchor links for `h2`, `h3` and `h4` heading Value: - `false`: will disable link generation. - `true`: will enable link generation for all headings. ### `alias` ::code-group ```ts [Default] alias: {} ``` ```ts [Signature] type Alias = Record ``` :: Aliases will be used to replace markdown components and render custom components instead of default ones. ```ts [nuxt.config.ts] export default defineNuxtConfig({ content: { renderer: { alias: { p: 'MyCustomParagraph' } } } }) ``` ## `watch` ```ts [Default] watch: { enabled: true } ``` Controls whether content hot reloading is enabled during development. **Options:** - `enabled` (`boolean`): Enable or disable hot reloading when editing content files. - `true` (default): Automatically reloads content changes in your application during development. - `false`: Disables hot reloading; changes require a manual refresh. ::callout The content watcher only runs in development and leverages the Vite dev server to detect content updates and send events to your application for live updates. :: ## `experimental` Experimental features that are not yet stable. ### `experimental.sqliteConnector` SQLite connectors have limitations in different environments. Some work in serverless environments, while others do not. Nuxt Content supports three different SQLite connectors to cover all environments: - `better-sqlite3`: Works in all Node environments, GitHub CI, Vercel CI and production, Cloudflare CI pipelines, etc. (Does **not** work in WebContainers and StackBlitz) - `sqlite3`: Works in Node environments, GitHub CI, and StackBlitz. (Does **not** work in Vercel or Cloudflare) - `native`: As of Node.js v22.5.0, the `node:sqlite` module is available natively in Node.js. This connector works in all Node environments with Node.js version 22.5.0 or newer. ```ts [nuxt.config.ts] export default defineNuxtConfig({ content: { experimental: { sqliteConnector: 'native' }, }, }); ``` ### `experimental.nativeSqlite` (deprecated, use `sqliteConnector`) As of Node.js v22.5.0, the `node:sqlite` module is available natively in Node.js. This allows Nuxt Content to use SQLite as a database without the need for an external package. ```ts [nuxt.config.ts] export default defineNuxtConfig({ content: { experimental: { nativeSqlite: true }, }, }); ``` ::prose-note This feature is only available in Node.js v22.5.0 and above. Enabling this feature in older version will not do anything. :: # Migration Nuxt Content v3 has been rebuilt from the ground up, resulting in a new library with enhanced capabilities. While we've redesigned concepts and components in a similar way as Content v2, breaking changes are inevitable. Don't worry, you don't need to modify your content files. We made sure that Content v3 handles content in the same way as Content v2. ## Changes ### Vue utils - `queryContent()` API is replaced with new `queryCollection()` ::prose-tip The new API is backed by SQL and content queries happens within a specific collection. :: - `fetchContentNavigation()` API is replaced with new `queryCollectionNavigation()` - Surroundings now has its own separate API `queryCollectionItemSurroundings()` - Document driven mode is dropped: `Markdown` files will not convert to Nuxt pages automatically, you need to create pages, [check this section to see how](https://content.nuxt.com/docs/components/content-renderer#example-usage). - `useContent()` composable is removed - `searchContent()` is dropped in favor of the new `queryCollectionSearchSections` API - Full text search can easily be done using the `queryCollectionSearchSections` API, [check this section to see how](https://content.nuxt.com/docs/advanced/fulltext-search) ### Components - All content should be rendered using `` component. ``, ``, `` and `` components are dropped in v3. - `` and `` components are not supported in v3. Instead components can simply use Vue's native `` component ::prose-note `` and `` was initially pro to manipulate content before rendering and removing wrapping paragraphs from slot content. This unwrapping behavior is now supported via `mdc-unwrap` attribute in `` component. Example: `` :: - Components created under the `components/content` directory are no longer automatically registered as global components. If you use [dynamic rendering](https://vuejs.org/guide/essentials/component-basics.html#dynamic-components){rel=""nofollow""} to render these components outside markdown files, you must manually register them in your Nuxt app. Check out the [Nuxt - Custom Components Directories](https://nuxt.com/docs/guide/directory-structure/components#custom-directories){rel=""nofollow""} documentation for more information on how to do so. ### Types - `import type { NavItem } from '@nuxt/content/dist/runtime/types'` is replaced with `import type { ContentNavigationItem } from '@nuxt/content'` ### General - `_dir.yml` files are renamed to `.navigation.yml` - There is no source option in module options, instead you can define [multiple sources](https://content.nuxt.com/docs/collections/sources) for your collections in `content.config.ts`. - Document `._path` is now renamed to `.path`, likewise all internal fields with `_` prefix are removed or renamed. - `useContentHelpers()` is removed - Module does not ignore dot files by default, you can ignore them by adding `ignore: ['**/.*']` in `exclude` options of your collection source. - Due to SQL limitations, sort order now uses alphabetical order instead for numerical order. Check out the [Ordering Files](https://content.nuxt.com/docs/collections/types#ordering-files) section for more information. - Module options have changed from v2. Check out [configuration page](https://content.nuxt.com/docs/getting-started/configuration) for details. ## Implement Document Driven mode in v3 Implementing document driven mode in Content v3 is quite easy. All you need is to create a catch-all page in Nuxt and fetch contents based on route path. ```vue [pages/[...slug\\].vue] ``` ## Converting `queryContent` to `queryCollections` As we mentioned above, `queryContent` is dropped in favor of new collection based `queryCollection`. There are two main differences between these two: 1. `queryCollection` is building a query for an SQL database. 2. `queryCollection` does the search only inside the specified collection. You should know the collection's name (key on config). ```ts [Find content with path] // Content v2 const v2Query = await queryContent(route.path).findOne() // Content v3 - don't forget to create `content` collection in `content.config.ts` const v3Query = await queryCollection('content').path(route.path).first() ``` ```ts [Find contents with custom filter] // Content v2 const v2Query = await queryContent() .where({ path: /^\/hello\/.*/ }) .find() // Content v3 - don't forget to create `content` collection in `content.config.ts` const v3Query = await queryCollection('content') .where('path', 'LIKE', '/hello%') .first() ``` ::prose-note{to="https://content.nuxt.com/docs/collections/define"} Check the dedicated section for more info about collections :: ## Convert `queryContent().findSurround()` Surround now has its own separate API. ```ts const targetPath = '/docs' // Content v2 const v2Surround = await queryContent(targetPath) .only(['title', 'description', 'navigation']) .findSurround(withoutTrailingSlash(route.path)) // Content v3 - don't forget to create `content` collection in `content.config.ts` const v3Surround = await queryCollectionItemSurroundings( 'content', targetPath, { fields: ['title', 'description', 'navigation'] } ) ``` ::prose-note Check the dedicated section for more information about the :: ## Consolidate `ProsePre`, `ProseCode`, and `ProseCodeInline` components Many `ProsePre` components are thin wrappers around the `ProseCode` component. We've consolidated these three components into two components. There is now no difference between `ProsePre` and multi-line code blocks. 1. MDC will now map and parse single backticks `` ` `` as `ProseCode` instead of `ProseCodeInline`. 2. MDC will now map and parse block code starting with three backticks` ``` ` as `ProsePre` component. **Suggested Changes:** 1. Your current `ProseCode` logic should be moved to `ProsePre` 2. Rename your `ProseCodeInline` components to `ProseCode` ## `_dir.yml` files are renamed to `.navigation.yml` In Content v3, we renamed `_dir.yml` to `.navigation.yml`. The new name better reflects the purpose of these files. :br Module uses these files to gather information about directories for generating navigation. Note that in order to make these files available for Module, you should define your collection's source in a way that includes these files. For example `source: '**'` and `source: '**/*.{md,yml}'` will include these files in collection, but `source: '**/*.md'` will not include them. ## Ignore dot files By default, Content v3 does not ignore dot files. If you want to ignore them, you can add `ignore: ['**/.*']` in the `exclude` option of your collection source. ```ts defineCollection({ source: { include: '**', exclude: ['**/.*'] } }) ``` Note that the above pattern will also exclude `.navigation.yml` file from collection. If you use `.navigation.yml` and want to keep them you can use `**/.(!(navigation.yml))` pattern to exclude all dot files except `.navigation.yml`. ```ts defineCollection({ source: { include: '**', exclude: ['**/.!(navigation.yml)'] } }) ``` # Define Content Collections The Nuxt Content module automatically parses any content files within the `content/` directory located at the root of your Nuxt application. This setup allows you to freely structure the folder to suit your project's needs. For better organization, consider using Content Collections, which let you categorize and manage content more effectively. These collections are defined in a `content.config.ts` file. ::warning If no `content.config.ts` file is present, all files within the content folder are parsed and imported by default. However, once a config file is added, only files matching the specified path patterns defined in collections will be imported. :: ## What are Content Collections? Content Collections organize related items within your Nuxt Content project. They provide a structured way to manage your content, making it easier to query, display, and maintain your site's data. Key features include: - **Logical Grouping**: Group similar content together, such as blog posts, product pages, or documentation articles - **Shared Configuration**: Apply common settings and validations across all items within a collection - **Improved Querying**: Fetch and filter related content items efficiently - **Automatic Type Inference**: Get type safety and autocompletion in your development environment - **Flexible Structure**: Organize collections by content type, category, or any other logical grouping that suits your needs ## Defining Collections Create a `content.config.ts` file in your project's root directory. This special file configures your collections database, utility types, and content handling. Here's a basic example: ```ts [content.config.ts] import { defineCollection, defineContentConfig } from '@nuxt/content' export default defineContentConfig({ collections: { docs: defineCollection({ // Specify the type of content in this collection type: 'page', // Load every file inside the `content` directory source: '**', }) } }) ``` ::warning Currently, a document is designed to be present in only one collection at a time. If a file is referenced in multiple collections, live reload will not work correctly. To avoid this, it is recommended to use the `exclude` attribute to explicitly exclude a document from other collections using appropriate regex patterns. This topic is still under discussion in this issue: [nuxt/content#2966](https://github.com/nuxt/content/issues/2966){rel=""nofollow""}. :: ### Collection Schema Schemas enforce data consistency within a collection and serve as the source of truth for TypeScript types. On top of the built-in fields, you can define a schema by adding the `schema` property to your collection by using a [`zod`](https://zod.dev){rel=""nofollow""} schema: ```ts [content.config.ts] import { defineCollection, defineContentConfig } from '@nuxt/content' import { z } from 'zod' export default defineContentConfig({ collections: { blog: defineCollection({ type: 'page', source: 'blog/*.md', // Define custom schema for docs collection schema: z.object({ tags: z.array(z.string()), image: z.string(), date: z.date() }) }) } }) ``` ::note `@nuxt/content` exposes a `z` object that contains a set of Zod schemas for common data types. Check [Zod’s README](https://github.com/colinhacks/zod){rel=""nofollow""} for complete documentation on how Zod works and what features are available. :: ::tip You can define as many collections as you want to organize different types of content. :: ### Database Indexes Optimize query performance by defining indexes on collection columns. Indexes are especially useful for fields used in filtering, sorting, or lookups. ```ts [content.config.ts] import { defineCollection, defineContentConfig } from '@nuxt/content' import { z } from 'zod' export default defineContentConfig({ collections: { products: defineCollection({ type: 'data', source: 'products/*.json', schema: z.object({ sku: z.string(), price: z.number(), category: z.string(), inStock: z.boolean(), }), indexes: [ // Single column indexes { columns: ['category'] }, { columns: ['price'] }, // Composite index for category + price filtering { columns: ['category', 'price'] }, // Unique index to ensure SKU uniqueness { columns: ['sku'], unique: true }, // Custom index name (optional) { columns: ['inStock', 'category'], name: 'idx_stock_category' }, ], }), }, }) ``` ::note Indexes are created automatically when the database schema is generated. They work across all supported databases: SQLite, Cloudflare D1, PostgreSQL, LibSQL, and PGlite. :: ::tip{icon="i-ph-lightbulb"} **Cloudflare D1 Cost Optimization** : With indexes, a `WHERE` clause on an indexed column counts as only 1 row read when there's a single match. Without an index, D1 counts all rows scanned in the table, significantly increasing your read costs. Indexes can dramatically reduce your D1 billing. :: **Index Configuration Options:** - **`columns`** (required): Array of column names to include in the index - **`unique`** (optional): Set to `true` to create a unique index (default: `false`) - **`name`** (optional): Custom index name. If omitted, auto-generates as `idx_{collection}_{column1}_{column2}` **Performance Tips:** - Index columns used in `where()` queries for faster filtering - Index columns used in `sort()` for optimized sorting - Use composite indexes for queries that filter/sort by multiple columns - Unique indexes automatically enforce data uniqueness constraints ## Querying Collections Use the [`queryCollection`](https://content.nuxt.com/docs/utils/query-collection) util to fetch one or all items from a collection: ```vue [pages/blog.vue] ``` ::note{to="https://content.nuxt.com/docs/utils/query-collection"} Learn more about the available query options in our `queryCollections` API documentation. :: ## defineCollection() The `defineCollection` function defines a collection in your content configuration. Here's its TypeScript signature: ```ts function defineCollection(collection: Collection): DefinedCollection type Collection = { // Determines how content is processed type: 'page' | 'data' // Specifies content location source?: string | CollectionSource // Zod schema for content validation and typing schema?: ZodObject // Database indexes for query optimization indexes?: CollectionIndex[] } type CollectionIndex = { // Column names to include in the index columns: string[] // Optional custom index name name?: string // Whether this is a unique index (default: false) unique?: boolean } ``` ::note{to="https://content.nuxt.com/docs/collections/types"} Learn more about collection types. :: ```ts type CollectionSource = { // Glob pattern for content matching include: string // .path prefix (only applies to 'page' type) prefix?: string // Glob patterns to exclude content exclude?: string[] // Root directory for content matching cwd?: string // Remote git repository URL (e.g., https://github.com/nuxt/content) repository?: string // Authentication token for private repositories (e.g., GitHub personal access token) authToken?: string } ``` ::note{to="https://content.nuxt.com/docs/collections/sources"} Learn more about collection sources. :: The function returns the defined collection object. # Collection Types In Nuxt Content, you can specify a type for each collection, depending on the intended purpose of the collection files. Collections can be defined as either **page** or **data** types. For both types, built-in fields are generated. Every collection includes these default fields: - `id`: Unique content identifier - `stem`: File path without extension (used for sorting and location) - `extension`: File extension - `meta`: Custom fields not defined in the collection schema ## Page type ```ts defineCollection({ source: '**/*.md', type: 'page' }) ``` ::tip Use the **page** type if there is a 1-to-1 relationship between content files and pages on your site. :: ### Path generation Nuxt Content will automatically generate a path for each file in the collection, making it easy to create URL mappings. Here are examples of generated paths based on file structure: | File | Path | | -------------------------------- | --------------------- | | `content/index.md` | `/` | | `content/about.md` | `/about` | | `content/blog/index.md` | `/blog` | | `content/blog/hello.md` | `/blog/hello` | | `content/1.guide/2.installation` | `/guide/installation` | ::note You can use the helper [`queryCollection('COLLECTION').path('PATH')`](https://content.nuxt.com/docs/utils/query-collection) to retrieve content by a specific path. :: ### Schema Overrides When you use the **page** type, Nuxt Content generates several standard fields that are commonly used for web pages. These fields provide structure and are **automatically** applied to the collection’s schema: - `path`: Generated route path - `title`: Page title - `description`: Page description - `seo`: SEO metadata (to be used with Nuxt's `useSeoMeta` composable) - `body`: Page content parsed as AST - `navigation`: Page navigation configuration (for [queryCollectionNavigation](https://content.nuxt.com/docs/utils/query-collection-navigation)) Here is the corresponding schema applied: ```ts path: z.string(), title: z.string(), description: z.string(), seo: z.intersection( z.object({ title: z.string().optional(), description: z.string().optional(), meta: z.array(z.record(z.string(), z.any())).optional(), link: z.array(z.record(z.string(), z.any())).optional(), }), z.record(z.string(), z.any()), ).optional().default({}), body: z.object({ type: z.string(), children: z.any(), toc: z.any(), }), navigation: z.union([ z.boolean(), z.object({ title: z.string(), description: z.string(), icon: z.string(), }), ]).default(true), ``` ::note You can override any of these fields by defining them in the collection’s schema. :: ## Data type ```ts defineCollection({ source: 'authors/**.yml', type: 'data' }) ``` The data type is useful for content that doesn't directly correspond to a webpage but instead represents structured data you might want to query and display within your application. With data collections, you have complete control over the schema, allowing you to define custom structures. ::note There's no strict relationship between collection type and file extension. For instance, a **page** collection can use [Markdown](https://content.nuxt.com/docs/files/markdown) or [YAML](https://content.nuxt.com/docs/files/yaml) or [JSON](https://content.nuxt.com/docs/files/json) files, and **data** collections can use any of these formats as well. :: ## Ordering Files For both types, you may want to control the display order in lists. Use numeric prefixes in file and directory names to specify an order. Nuxt Content will use these numbers when ordering content lists. ::note Nuxt Content uses alphabetical order for sorting, so if you want to use numerical order, you need to prefix single digit numbers with `0` . For example, without the `0` prefix, `10.foo.md` would come before `2.bar.md` . :: ```text [Directory structure] content/ 1.frameworks/ 1.vue.md 2.nuxt.md ... 2.examples/ 01.nuxthub.md 02.vercel.md 03.netlify.md 04.heroku.md ... 10.cloudflare.md index.md ``` ::warning Separate number from file name using `.` character. Using any other separator will not work. :: # Collection Sources Nuxt Content provides several ways to import content files into your collection. You can configure the source by using the `source` property within `defineCollection`: ```ts [content.config.ts] import { defineCollection, defineContentConfig } from '@nuxt/content' export default defineContentConfig({ collections: { docs: defineCollection({ source: '**', type: 'page' }) } }) ``` ## `source` The `source` property can be defined as either a string (following a glob pattern) or an object, allowing more detailed source configuration for your target directory and files within the content folder. **Example:** - `source: '**'` includes all files within the content directory and its subdirectories. - `source: '**/*.md'`includes all `Markdown` files within the content directory and its subdirectories. - `source: 'docs/**/*.yml'` includes all `YML` files within the `content/docs` and its subdirectories. - `source: '**/*.{json,yml}'` includes `JSON` or `YML` file within the content directory and all its subdirectories. - `source: '*.json'` includes only `JSON` files located directly within the content directory, excluding any subdirectories. ### `include` (required) Glob pattern of your target repository and files in the content folder. ### `exclude` Glob patterns to exclude content from the import. ### `prefix` This configuration only applied for **page** type with 1-to-1 relationship between content files and pages on your site. It represents the path prefix (base URL) of the corresponding page on the website. ::prose-warning The `prefix` must start by a leading `/` . :: By default, module extracts the static prefix of `source`(or `source.include`) and uses it as a prefix for content paths. For example, if you define `/en/**` source, module will auto-fill the `prefix` with `/en`. You can manually provide a prefix to override this behavior. The prefix can be removed by setting `prefix: '/'` in the collection source. ```ts defineCollection({ type: "page", source: { include: "en/**", exclude: ["en/index.md"], prefix: '/' } }) ``` ### `cwd` Root directory for content matching. **Example:** If you want to include files from a folder outside the content directory, set the absolute path of that folder to the `cwd` property. ```ts source: { cwd: path.resolve('packages/my-pkg/docs'), include: '**/*.md', } ``` ### `repository` External source representing a remote git repository URL (e.g., {rel=""nofollow""}), or an object containing Git branch / tag information, or optionally authentication When defining an external source you must also define the `include` option. `include` pattern is essential for the module to know which files to use for the collection. ```js import { defineCollection, defineContentConfig } from '@nuxt/content' export default defineContentConfig({ collections: { docs: defineCollection({ type: 'page', source: { repository: 'https://github.com/nuxt/content', include: 'docs/content/**', }, }) } }) ``` #### `branch` / `tag` This option allows for cloning a Git repository by its tag or branch. **Example:** If you want to clone by a tag, make the `repository` attribute an object, with the `url` of your repository, and set the `tag` attribute. ```js import { defineCollection, defineContentConfig } from '@nuxt/content' export default defineContentConfig({ collections: { docs: defineCollection({ type: 'page', source: { repository: { url: 'https://github.com/nuxt/content', tag: 'v1' }, include: 'docs/content/**', }, }) } }) ``` **Example:** If you want to clone by a remote branch, make the `repository` attribute an object, with the `url` of your repository, and set the `branch` attribute. ```js import { defineCollection, defineContentConfig } from '@nuxt/content' export default defineContentConfig({ collections: { docs: defineCollection({ type: 'page', source: { repository: { url: 'https://github.com/nuxt/content', branch: 'dev' }, include: 'docs/content/**', }, }) } }) ``` #### `auth` This option allows for basic and token-based authentication for Git repositories. ::warning{icon="i-lucide-shield-alert"} Never commit authentication tokens or credentials directly in your code. Use environment variables or other secure methods to provide these values at runtime. :: **Example:** If you want to use basic authentication (e.g. for BitBucket repositories), you can use: ```ts defineCollection({ type: 'page', source: { repository: { url: 'https://bitbucket.org/username/repo', auth: { username: 'username', password: 'password', }, }, }, }) ``` **Example:** If you need to use authentication tokens (e.g. for Github, Gitlab, some Forgejo providers), you can do the following: ```ts defineCollection({ type: 'page', source: { repository: { url: 'https://github.com/username/repo', auth: { username: 'username', token: 'password', }, }, }, }) ``` # Schema Validators Nuxt Content supports defining collection schemas with multiple validators. Out of the box, this includes popular libraries like **Zod v3 / v4** and **Valibot** (examples below). The system is extensible and can support other validators via JSON Schema adapters. Schemas enforce data consistency and drive generated types and Studio forms. ## Using Zod v3 ### Install ```bash pnpm add -D zod zod-to-json-schema # or npm i -D zod zod-to-json-schema ``` Prefer importing `z` directly from `zod`. ```ts [content.config.ts] import { defineContentConfig, defineCollection, property } from '@nuxt/content' import { z } from 'zod' // or 'zod/v3' if your setup exposes this subpath export default defineContentConfig({ collections: { blog: defineCollection({ type: 'page', source: 'blog/*.md', schema: z.object({ title: z.string(), description: z.string().optional(), date: z.date(), draft: z.boolean().default(false), tags: z.array(z.string()).optional(), image: z.object({ src: property(z.string()).editor({ input: 'media' }), alt: z.string() }) }) }) } }) ``` ::note Dates are serialised as ISO strings under the hood (JSON Schema `format: date-time` ). :: ::warning The `z` re-export from `@nuxt/content` is deprecated and will be removed in a future release. Import `z` from `zod` (or `zod/v3` ) instead. :: ## Using Zod v4 Zod v4 provides a native JSON Schema export. No `zod-to-json-schema` dependency is required. ### Install Zod ```bash pnpm add -D zod # or npm i -D zod ``` ```ts [content.config.ts] import { defineContentConfig, defineCollection, property } from '@nuxt/content' import { z } from 'zod/v4' export default defineContentConfig({ collections: { docs: defineCollection({ type: 'page', source: 'docs/**/*.md', schema: z.object({ title: z.string(), description: z.string().optional(), updatedAt: z.date(), draft: z.boolean().optional(), tags: z.array(z.string()).optional(), hero: z.object({ image: property(z.string()).editor({ input: 'media' }), caption: z.string().optional() }) }) }) } }) ``` ## Using Valibot Use Valibot primitives to define your schema. ### Install Valibot ```bash pnpm add -D valibot @valibot/to-json-schema # or npm i -D valibot @valibot/to-json-schema ``` ```ts [content.config.ts] import { defineContentConfig, defineCollection, property } from '@nuxt/content' import { object, string, boolean, array, date, optional } from 'valibot' export default defineContentConfig({ collections: { docs: defineCollection({ type: 'page', source: 'docs/**/*.md', schema: object({ title: string(), description: optional(string()), updatedAt: date(), draft: optional(boolean()), tags: optional(array(string())), hero: object({ image: property(string()).editor({ input: 'media' }), caption: optional(string()) }) }) }) } }) ``` ## Choosing a validator - **Zod v3**: battle-tested, rich ecosystem; great DX with re-exported `z`. - **Valibot**: lightweight and fast; bring your own importer from `valibot`. Only install and use the validator you need. Nuxt Content auto-detects supported validators that are installed. ## Support for other validators Nuxt Content converts your collection schema to JSON Schema Draft-07 internally. If your preferred validator can be transformed to Draft-07 (or has a compatible adapter), it can be supported. Currently, Zod (v3 and v4) and Valibot are auto-detected. If you’d like first-class support for another validator, consider opening an issue or PR in the [Nuxt Content repository](https://github.com/nuxt/content){rel=""nofollow""}. ## Editor metadata (optional) You can enrich fields for Studio via `property(...).editor({ ... })` with both validators. See the Studio docs for mapping details. ::tip{to="https://nuxt.studio/content#form-editor"} Learn how editor metadata maps to form inputs in Studio. :: # Inherit Schema from a Vue Component You can reuse a Vue component's props as part of your collection schema. This helps keep your content model aligned with your UI, reduces duplication, and prevents drift. ## How it works Nuxt Content provides a `property()` helper that augments your validator and adds the following utility: - **inherit(path)**: replace the current object schema with the props JSON Schema inferred from a Vue component at `path` Under the hood, Nuxt Content reads the component's props (via `nuxt-component-meta`) and converts them to JSON Schema, then merges them into your collection schema. ## Example ```ts [content.config.ts] import { defineContentConfig, defineCollection, property } from '@nuxt/content' import { z } from 'zod' export default defineContentConfig({ collections: { pages: defineCollection({ type: 'page', source: '**/*.md', schema: z.object({ // Reuse props from a local component hero: property(z.object({})).inherit('app/components/HeroSection.vue'), // Reuse props from a dependency (path is resolved like an import) button: property(z.object({})).inherit('@nuxt/ui/components/Button.vue') }) }) } }) ``` ## Notes - The argument to `inherit()` is resolved like a module path. You can pass a relative path from project root or a package path. - `inherit()` expects to be used on an object field (e.g., `property(z.object({}))`). - Nested usage is supported: you can place inherited objects inside other objects and arrays; Nuxt Content recursively replaces `$content.inherit` markers. - If the component cannot be resolved, the schema falls back to the original object definition. ::tip Pair `inherit()` with `editor(...)` for better Studio forms if you need custom inputs on top of the component's props. :: # Markdown ## Usage ### Define a Collection ```ts [content.config.ts] import { defineCollection, defineContentConfig } from '@nuxt/content' import { z } from 'zod' export default defineContentConfig({ collections: { blog: defineCollection({ type: 'page', source: 'blog/*.md', schema: z.object({ date: z.string() }) }) } }) ``` ::note{to="https://content.nuxt.com/docs/collections/types#page-type"} Learn more about the `page` collection type. :: ### Create `.md` files Create blog posts in `content/blog/` directory. ::code-group ```md [foo.md] --- date: 2020-11-11 --- # Foo This is Foo blog post. ``` ```md [bar.md] --- date: 2024-12-12 --- Hello I am bar. Nice to meet you. ``` :: ### Query Markdown Files Now we can query blog posts: ```ts // Get the foo post const fooPost = await queryCollection('blog').path('/blog/foo').first() // Find all posts const allPosts = await queryCollection('blog').order('date', 'DESC').all() ``` ### Display Markdown To display the content of a markdown file, you can use the [``](https://content.nuxt.com/docs/components/content-renderer) component. ```vue [blog/[slug\\].vue] ``` ::note Read more about the [``](https://content.nuxt.com/docs/components/content-renderer) component and [`Prose Components`](https://content.nuxt.com/docs/components/prose) . :: ## Frontmatter Frontmatter is a convention of Markdown-based CMS to provide meta-data to pages, like description or title. In Nuxt Content, the frontmatter uses the YAML syntax with `key: value` pairs. These data are available when rendering the content and can store any information that you would need. ### Syntax You can declare a frontmatter block at the top of the Markdown files in the `content/` directory with the `---` identifier. ```md [content/index.md] --- title: 'Title of the page' description: 'meta description of the page' --- ``` ```ts [example.ts] const home = await queryCollection('content').path('/').first() console.log(home.title) // => 'Title of the page' console.log(home.description) // => 'meta description of the page' console.log(home.body) // => AST object of the page content ``` ### Native parameters | | | | | | ------------- | --------- | ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------- | | Key | Type | Default | Description | | `title` | `string` | First `

` of the page | Title of the page, will also be injected in metas | | `description` | `string` | First `

` of the page | Description of the page, will be shown below the title and injected into the metas | | `navigation` | `boolean` | `true` | Define if the page is included in [`queryCollectionNavigation`](https://content.nuxt.com/docs/utils/query-collection-navigation) return value. | ::warning Additional parameters that you have defined in your frontmatter block need to be defined in your schema (see the date parameter in the example at top of this page) to be able to use them for querying. :: ## MDC Syntax We created the MDC syntax to supercharge Markdown and give you the ability to integrate Vue components with slots and props inside your Markdown. ::note{to="https://remark-mdc.nuxt.space/#syntax"} Explore the full MDC documentation. :: ::callout --- icon: i-simple-icons-visualstudiocode to: https://marketplace.visualstudio.com/items?itemName=Nuxt.mdc --- Install the **MDC VS Code extension** to get proper syntax highlighting for the MDC syntax. :: ### Vue Components You can use any Vue component in your Markdown files. We have a special syntax to make it easier to use components in your Markdown files. ```mdc [content/index.md] ::component-name Default slot content :: ``` ::warning Components that are used in Markdown have to be marked as `global` in your Nuxt app if you don't use the `components/content/` directory. Visit [Nuxt 3 docs](https://nuxt.com/docs/guide/directory-structure/components){rel=""nofollow""} to learn more. :: #### Block Components Block components are components that accept Markdown content or another component as a slot. The component must contain at least one `` component to accept formatted text. In a markdown file, use the component with the `::` identifier. ::code-group ```mdc [index.md] ::card The content of the card :: ``` ```html [Card.vue] ``` :::code-preview{icon="i-lucide-eye" label="Preview"} ::::example-card The content of the card :::: ::: :: #### Slots A component's slots can accept content or another components. - **Default slot** renders the top-level content inside the block component or with `#default` - **Named slots** use the `#` identifier to render the corresponding content. ::code-group ```mdc [index.md] ::hero My Page Title #description This will be rendered inside the `description` slot. :: ``` ```html [Hero.vue] ``` :::code-preview{icon="i-lucide-eye" label="Preview"} ::::example-hero My Page Title #description This will be rendered inside the `description` slot. :::: ::: :: ::note Read more about the [``](https://content.nuxt.com/docs/components/slot) component. :: ::tip You can use Markdown inside your components slots: :::code-group ```mdc [index.md] ::my-title A [rich text](/) will be **rendered** by the component. :: ``` ```html [MyTitle.vue] ``` ::::code-preview{icon="i-lucide-eye" label="Preview"} :::::example-title A [rich text](https://content.nuxt.com) will be **rendered** by the component. ::::: :::: ::: :: #### Props There are two ways to pass props to components using MDC. ##### **Inline method** The `{}` identifier passes props to components in a terse way by using a `key=value` syntax. ::code-group ```mdc [index.md] ::alert{type="warning"} The **alert** component. :: ``` ```vue [Alert.vue] ``` :::code-preview{icon="i-lucide-eye" label="Preview"} ::::example-alert{type="warning"} The **alert** component. :::: ::: :: Multiple props can be separated with a space: ```mdc ::alert{type="warning" icon="exclamation-circle"} Oops! An error occurred :: ``` The `v-bind` shorthand `:` can be also be used to bind a prop to a value in the frontmatter. ```mdc --- type: "warning" --- ::alert{:type="type"} Your warning :: ``` If you want to pass arrays or objects as props to components you can pass them as JSON string and prefix the prop key with a colon to automatically decode the JSON string. Note that in this case you should use single quotes for the value string so you can use double quotes to pass a valid JSON string: ::code-group ```mdc [array.md] ::dropdown{:items='["Nuxt", "Vue", "React"]'} :: ``` ```mdc [number-array.md] ::dropdown{:items='[1,2,3.5]'} :: ``` ```mdc [object.md] ::chart{:options='{"responsive": true, "scales": {"y": {"beginAtZero": true}}}'} :: ``` :: ##### **YAML method** The YAML method uses the `---` identifier to declare one prop per line, that can be useful for readability. ::code-group ```mdc [index.md] ::icon-card --- icon: IconNuxt description: Harness the full power of Nuxt and the Nuxt ecosystem. title: Nuxt Architecture. --- :: ``` ```html [IconCard.vue] ``` :::code-preview{icon="i-lucide-eye" label="Preview"} :example-icon-card{description="Harness the full power of Nuxt and the Nuxt ecosystem." icon="i-simple-icons-nuxtdotjs" title="Nuxt Architecture."} ::: :: ### Attributes Attributes are useful for highlighting and modifying part of paragraph. The syntax is nearly similar to inline components and markdown links syntax. Possible values are all named attributes, classes with the notation `.class-name` and an ID with `#id-name`. ::code-group ```mdc [index.md] Hello [World]{style="color: green;" .custom-class #custom-id}! ``` :::code-preview{icon="i-lucide-eye" label="Preview"} Hello [World]{#custom-id .custom-class style="color: green;"} ! ::: :: In addition to mdc components and `span`, attribute syntax will work on images, links, inline `code`, \**bold*\* and \_italic\_ text. ::code-group ```md [index.md] Attributes work on: - [Attributes](#attributes){style="background-color: green;"}, `code`{style="color: cyan;"}, - _italic_{style="background-color: yellow; color:black;"} and **bold**{style="background-color: lightgreen;"} texts. ``` :::code-preview{prose label="Preview"} Attributes work on: - [Attributes](https://content.nuxt.com/#attributes){style="background-color: green;"}, `code`, - *italic* and **bold** texts. ::: :: ### Binding Data You can bind data within your Markdown document using the `{{ $doc.variable || 'defaultValue' }}` syntax. These values can be defined in the YAML frontmatter at the top of the document, within each MDC component, or injected using the `data` prop of the `` component. #### Define in YAML ```mdc --- title: 'Title of the page' description: 'meta description of the page' customVariable: 'Custom Value' --- # The Title is {{ $doc.title }} and customVariable is {{ $doc.customVariable || 'defaultValue' }} ``` #### Define in external with `` ```html [test.vue] ``` ```mdc [test.md] # Hello {{ $doc.name || 'World' }} ``` ## Prose Components In Nuxt Content, the prose represents HTML tags generated by the Markdown syntax, such as heading levels and links. For each HTML tag, a Vue component is used, allowing you to override them if needed, for example `

` becomes ``. If you want to customize a Prose component, here are the recommended steps: - Check out the original [component sources](https://github.com/nuxt-modules/mdc/blob/main/src/runtime/components/prose){rel=""nofollow""}. - Use the exact same props. - In your `components/content/` directory, give it the same name. - Make it yours 🚀. ::note{to="https://content.nuxt.com/docs/components/prose"} Read the complete Prose reference in the Prose Components section. :: ## Code Highlighting Nuxt Content uses [Shiki](https://github.com/shikijs/shiki){rel=""nofollow""}, which colors tokens with VSCode themes. Code highlighting works both on [`ProsePre`](https://content.nuxt.com/docs/components/prose#prosepre) and [`ProseCode`](https://content.nuxt.com/docs/components/prose#prosecodeinline). Each line of a code block gets its line number in the `line` attribute so lines can be labeled or individually styled. ::callout [Read the API reference to configure or entirely disable syntax highlighting.](https://content.nuxt.com/docs/getting-started/configuration) :: ## Images You can add images to your `public` directory: ```bash [Directory structure] content/ index.md public/ image.png nuxt.config.ts package.json ``` And then use them in your markdown files in the `content` directory as such: ```md [content/index.md] ![my image](/image.png) ``` ## Excerpt Content excerpt or summary can be extracted from the content using `` as a divider. ```md [content/index.md] --- title: Introduction --- Learn how to use `@nuxt/content`. Full amount of content beyond the more divider. ``` Description property will contain the excerpt content unless defined within the frontmatter props. If there is no `` divider in the text then excerpt is undefined. ::tip You should define the `excerpt` field in the collection schema if you want to use the excerpt feature. ```ts [content.config.ts] const content = defineCollection({ type: 'page', source: '**', schema: z.object({ excerpt: z.object({ type: z.string(), children: z.any(), }), }), }) ``` Read more about the [collection schema](https://content.nuxt.com/docs/collections/define#collection-schema). :: Example variables will be injected into the document: ```json { "excerpt": Object "body": Object // ... other keys } ``` # YAML ## Define Collection ```ts [content.config.ts] import { defineCollection, defineContentConfig } from '@nuxt/content' import { z } from 'zod' export default defineContentConfig({ collections: { authors: defineCollection({ type: 'data', source: 'authors/**.yml', schema: z.object({ name: z.string(), avatar: z.string(), url: z.string() }) }) } }) ``` ## Create `.yml` files Create authors files in `content/authors/` directory. ::code-group ```yaml [farnabaz.yml] name: Ahad Birang avatar: https://avatars.githubusercontent.com/u/2047945?v=4 url: https://github.com/farnabaz ``` ```yaml [larbish.yml] name: Baptiste Leproux avatar: https://avatars.githubusercontent.com/u/7290030?v=4 url: https://github.com/larbish ``` :: ## Query Data Now we can query authors: ```vue ``` # JSON ## Define Collection ```ts [content.config.ts] import { defineCollection, defineContentConfig } from '@nuxt/content' import { z } from 'zod' export default defineContentConfig({ collections: { authors: defineCollection({ type: 'data', source: 'authors/**.json', schema: z.object({ name: z.string(), avatar: z.string(), url: z.string() }) }) } }) ``` ## Create `.json` files Create authors files in `content/authors/` directory. ::code-group ```json [farnabaz.json] { "name": "Ahad Birang", "avatar": "https://avatars.githubusercontent.com/u/2047945?v=4", "url": "https://github.com/farnabaz" } ``` ```json [larbish.json] { "name": "Baptiste Leproux", "avatar": "https://avatars.githubusercontent.com/u/7290030?v=4", "url": "https://github.com/larbish" } ``` :: ::warning Each file in `data` collection should contain only one object, therefore having top level array in a JSON file will cause invalid result in query time. :: ## Query Data Now we can query authors: ```vue ``` # CSV ## Single-file source When you point a collection to a single CSV file (instead of a glob), Nuxt Content **treats each data row as a separate item** in the collection. - **Define the collection**: set `source` to the path of a single `.csv` file. - **Item generation**: each data row becomes an item with the row’s fields at the top level (no `body` array). - **IDs**: item IDs are suffixed with `#`, where `#1` is the first data row after the header. ```ts [content.config.ts] import { defineCollection, defineContentConfig } from '@nuxt/content' import { z } from 'zod' export default defineContentConfig({ collections: { people: defineCollection({ type: 'data', source: 'org/people.csv', schema: z.object({ name: z.string(), email: z.string().email() }) }) } }) ``` ```csv [content/org/people.csv] name,email Alice,alice@example.com Bob,bob@example.com ``` Each row produces its own item. For example, the first data row will have an ID ending with `#1` and the second with `#2`. You can query by any column: ```ts const { data: alice } = await useAsyncData('alice', () => queryCollection('people') .where('email', '=', 'alice@example.com') .first() ) const { data: allPeople } = await useAsyncData('all-people', () => queryCollection('people') .order('name', 'ASC') .all() ) ``` ::note - The header row is required and is not turned into an item. - With a single-file source, items contain row fields at the top level (no `body`). - If you prefer treating each CSV file as a single item containing all rows in `body`, use a glob source like `org/**.csv` instead of a single file. :: ## Multiple-files source If you uses `*/**.csv` as source in configuration, Nuxt Content will treat them differently from single-file collections. :br**Each file(not row) will be treated as an item**, rows will be parsed into `body` field in item object as an array. ```ts [content.config.ts] import { defineCollection, defineContentConfig } from '@nuxt/content' import { z } from 'zod' export default defineContentConfig({ collections: { charts: defineCollection({ type: 'data', source: 'charts/**.csv', schema: z.object({ // Body is important in CSV files, without body field you cannot access to data array body: z.array(z.object({ label: z.string(), value: z.number() })) }) }) } }) ``` Create chart files in `content/charts/` directory. ::code-group ```csv [content/charts/chart1.csv] label,value A,100 B,200 C,300 ``` ```csv [content/charts/chart2.csv] label,value Foo,123 Bar,456 Baz,789 ``` :: ::warning Each CSV file should have a header row that defines the column names, which will be used as object keys when parsed. :: Now we can query charts: ```vue ``` ## Configuration You can configure how CSV files are parsed in your `nuxt.config.ts`: ```ts [nuxt.config.ts] export default defineNuxtConfig({ content: { build: { csv: { // Convert CSV data to JSON objects json: true, // Specify custom delimiter (default is ',') delimiter: ',' } } } }) ``` With `json: true` in the configuration, each row will be converted to a JavaScript object with the header row used as keys: ```json [ { "id": "1", "name": "John Doe", "email": "john@example.com" }, { "id": "2", "name": "Jane Smith", "email": "jane@example.com" } ] ``` ## Custom Delimiters If your CSV files use a different delimiter, you can specify it in the configuration: ```ts [nuxt.config.ts] export default defineNuxtConfig({ content: { build: { csv: { delimiter: ';' // Use semicolon as delimiter } } } }) ``` This would parse CSV files like: ```csv [semicolon-data.csv] id;name;email 1;John Doe;john@example.com 2;Jane Smith;jane@example.com ``` ::note The CSV parser can be disabled by setting `csv: false` in the configuration if you don't need CSV support. :: # queryCollection ## Usage Use the auto-imported `queryCollection` to find contents inside a collection. Here we assume that you have defined `docs` collection inside `content.config.ts`. If you have not defined any collection, check [How to define a collection](https://content.nuxt.com/docs/collections/define#defining-collections). ```vue [pages/[...slug\\].vue] ``` ::tip The `queryCollection` utility is available in both Vue and Nitro. Checkout [Server Usage](https://content.nuxt.com/#server-usage) for more details on how to use it on the server side. :: ## API ### Type ```ts function queryCollection(collection: T): CollectionQueryBuilder interface CollectionQueryBuilder { where(field: keyof T | string, operator: SQLOperator, value?: unknown): CollectionQueryBuilder andWhere(groupFactory: QueryGroupFunction): CollectionQueryBuilder orWhere(groupFactory: QueryGroupFunction): CollectionQueryBuilder order(field: keyof T, direction: 'ASC' | 'DESC'): CollectionQueryBuilder // ... other methods } ``` ### `queryCollection(collection: CollectionName)` Create a query builder to search in the specific collection. - Parameter: - `collection`: The key of defined collection in `content.config.ts` ### `path(path: string)` Search for contents that have specific `path`. (`path` is an special field in `page` collections which generates based on fs path and can be use as route to render the content) - Parameter: - `path`: The path string to match. ```ts const route = useRoute() const { data } = await useAsyncData(route.path, () => { return queryCollection('docs').path(route.path).first() }) ``` ### `select(...fields: keyof Collection)` Select specific fields from the collection to be returned in the query result. - Parameters: - `...fields`: A list of field names to select from the collection. ```ts const route = useRoute() const { data } = await useAsyncData(route.path, () => { return queryCollection('docs') .select('path', 'title', 'description') .first() }) ``` ### `where(field: keyof Collection | string, operator: SqlOperator, value?: unknown)` Add a condition to the query to filter results based on a specific field. - Parameters: - `field`: The field to filter on - `operator`: The SQL operator to use for comparison. Possible values include: - `'='`: Equal to - `'>'`: Greater than - `'<'`: Less than - `'<>'`: Not equal to - `'IN'`: In a list of values - `'BETWEEN'`: Between two values - `'NOT BETWEEN'`: Not between two values - `'IS NULL'`: Is null - `'IS NOT NULL'`: Is not null - `'LIKE'`: Matches a pattern - `'NOT LIKE'`: Does not match a pattern - `value`: The value to compare against. The type depends on the operator used. ```ts const route = useRoute() const { data } = await useAsyncData(route.path, () => { return queryCollection('docs') .where('date', '<', '2024-04-04') .where('category', '=', 'news') .all() }) // Generated SQL // SELECT * FROM docs WHERE date < '2024-04-04' AND category = 'news' ``` ### `andWhere(groupFactory: QueryGroupFunction)` Add an AND condition group to the query. This allows for more complex query conditions. - Parameter: - `groupFactory`: A function that receives a query builder and can add multiple conditions that will be grouped together with AND ```ts const { data } = await useAsyncData('recent-docs', () => { return queryCollection('docs') .where('published', '=', true) .andWhere(query => query.where('date', '>', '2024-01-01').where('category', '=', 'news')) .all() }) // Generated SQL // SELECT * FROM docs WHERE published = true AND (date > '2024-01-01' AND category = 'news') ``` ### `orWhere(groupFactory: QueryGroupFunction)` Add an OR condition group to the query. This allows for alternative conditions. - Parameter: - `groupFactory`: A function that receives a query builder and can add multiple conditions that will be grouped together with OR ```ts const { data } = await useAsyncData('featured-docs', () => { return queryCollection('docs') .where('published', '=', true) .orWhere(query => query.where('featured', '=', true).where('priority', '>', 5)) .all() }) // Generated SQL // SELECT * FROM docs WHERE published = true AND (featured = true OR priority > 5) ``` ### `order(field: keyof Collection, direction: 'ASC' | 'DESC')` Order the query results based on a specific field. - Parameters: - `field`: The field to order by. - `direction`: The direction of ordering, either 'ASC' for ascending or 'DESC' for descending. ```ts const route = useRoute() const { data } = await useAsyncData(route.path, () => { return queryCollection('docs') .order('date', 'DESC') .all() }) ``` ### `limit(limit: number)` Limit the number of results returned by the query. - Parameter: - `limit`: The maximum number of results to return. ```ts const route = useRoute() const { data } = await useAsyncData(route.path, () => { return queryCollection('docs') .limit(10) .all() }) ``` ### `skip(skip: number)` Skip a specified number of results in the query. - Parameter: - `skip`: The number of results to skip. ```ts const route = useRoute() const { data } = await useAsyncData(route.path, () => { return queryCollection('docs') // Skip first 5 items .skip(5) .all() }) ``` ### `all()` Execute the query and return all matching results. - Returns: A Promise that resolves to an array of all matching documents. ```ts const route = useRoute() const { data } = await useAsyncData(route.path, () => { return queryCollection('docs').all() }) ``` ### `first()` Execute the query and return the first matching result. - Returns: A Promise that resolves to the first matching document, or `null` if no documents match. ```ts const route = useRoute() const { data } = await useAsyncData(route.path, () => { return queryCollection('docs').first() }) ``` ### `count()` Count the number of matched collection entries based on the query. ```ts const route = useRoute() const { data } = await useAsyncData(route.path, () => { return queryCollection('docs') // Count matches .count() }) // Returns 5 // number of matches ``` You can also use `count()` with other methods defined above such as `where()` in order to apply additional conditions within the collection query. ```ts const route = useRoute() const { data } = await useAsyncData(route.path, () => { return queryCollection('docs') .where('date', '<', '2024-04-04') // Count matches .count() }) // Returns 3 // number of matches for the provided query ``` ## Examples Here is a complete example of how to fetch a list of documents in the `docs` collections. ```vue [index.vue] ``` ## Server Usage Nuxt Content provides a similar utility to query collections on the server side. The only difference is that you need to pass `event` as the first argument to the `queryCollection` function. ```ts [server/api/[slug\\].ts] export default eventHandler(async (event) => { const { slug } = getRouterParams(event) const page = await queryCollection(event, 'docs').path(slug).first() return page }) ``` ::note Make sure to create `server/tsconfig.json` file with the following content to avoid type error. ```json { "extends": "../.nuxt/tsconfig.server.json" } ``` :: # queryCollectionNavigation ## Usage Use the auto-imported `queryCollectionNavigation` to generate a navigation tree for a specific collection. This is particularly useful for creating dynamic navigation menus or sidebars based on your content structure. The function returns a chainable promise that allows you to add additional query conditions: ```vue [pages/[...slug\\].vue] ``` ::tip The `queryCollectionNavigation` utility is available in both Vue and Nitro. Checkout [Server Usage](https://content.nuxt.com/#server-usage) for more details on how to use it on the server side. :: ### Navigation metadata with `.navigation.yml` You can add metadata to a directory using a `.navigation.yml` file. ```yml [.navigation.yml] title: Getting Started icon: i-lucide-square-play ``` ## Type ```ts function queryCollectionNavigation( collection: T, fields?: Array ): ChainablePromise interface ChainablePromise extends Promise { where(field: keyof PageCollections[T] | string, operator: SQLOperator, value?: unknown): ChainablePromise andWhere(groupFactory: QueryGroupFunction): ChainablePromise orWhere(groupFactory: QueryGroupFunction): ChainablePromise order(field: keyof PageCollections[T], direction: 'ASC' | 'DESC'): ChainablePromise } ``` ## API ### `queryCollectionNavigation(collection: CollectionName, extraField: keyof Collection)` Generate a navigation tree for the specified collection. - Parameters: - `collection`: The key of the defined collection in `content.config.ts`. - `extraFields`: (Optional) An array of additional fields to include in the navigation items. (By default `title` and `path` are included in the navigation items.) - Returns: A chainable promise that resolves to a navigation tree structure. The promise includes methods for adding query conditions: - `where(field, operator, value)`: Add a WHERE condition - `andWhere(groupFactory)`: Add a grouped AND condition - `orWhere(groupFactory)`: Add a grouped OR condition - `order(field, direction)`: Add an ORDER BY clause The navigation tree is generated based on the directory structure and ordering happens based on files [ordering](https://content.nuxt.com/docs/collections/types#ordering-files) ## Examples Basic usage without additional query conditions: ```vue [pages/[...slug\\].vue] ``` Example with additional query conditions and extra fields: ```vue [pages/[...slug\\].vue] ``` ## Server Usage Nuxt Content provides a similar utility to query collections on the server side. The only difference is that you need to pass `event` as the first argument to the `queryCollectionNavigation` function. ```ts [server/api/navigation.ts] export default eventHandler(async (event) => { const navigation = await queryCollectionNavigation(event, 'docs') return navigation }) ``` ::note Make sure to create `server/tsconfig.json` file with the following content to avoid type error. ```json { "extends": "../.nuxt/tsconfig.server.json" } ``` :: --- ## Extra utilities to work with navigation Content module provides some extra utilities to simplify common use cases like building breadcrumb navigation. ### `findPageHeadline(navigation, path, options?)` Returns the headline (name of the parent folder) for a given path within a navigation tree. Useful for displaying section titles or contextual navigation headers. - `navigation`: The navigation tree (array of ContentNavigationItem). - `path`: The current page path. - `options`(optional): - `indexAsChild`: Treat index pages as children. **Example:** ```ts import { findPageHeadline } from '@nuxt/content/utils' const headline = findPageHeadline(navigation, '/docs/guide/getting-started') // headline is a string that contains the name of the parent folder ``` ### `findPageBreadcrumb(navigation, path, options?)` Returns the breadcrumb trail (array of navigation items) for a given path within a navigation tree. Useful for building breadcrumb navigation components. - `navigation`: The navigation tree (array of ContentNavigationItem). - `path`: The current page path. - `options`(optional): - `current`: Include the current page in the breadcrumb. - `indexAsChild`: Treat index pages as children. **Example:** ```ts import { findPageBreadcrumb } from '@nuxt/content/utils' const breadcrumb = findPageBreadcrumb(navigation, '/docs/guide/getting-started') // breadcrumb is an array of navigation items leading to the current page ``` ### `findPageChildren(navigation, path, options?)` Finds and returns the direct children of a given path in the navigation tree. - `navigation`: The navigation tree (array of ContentNavigationItem). - `path`: The parent path to find children for. - `options`(optional): - `indexAsChild`: Treat index pages as children. **Example:** ```ts import { findPageChildren } from '@nuxt/content/utils' const children = findPageChildren(navigation, '/docs/guide') // children is an array of navigation items under '/docs/guide' ``` ### `findPageSiblings(navigation, path, options?)` Returns the sibling navigation items for a given path (i.e., other items with the same parent). - `navigation`: The navigation tree (array of ContentNavigationItem). - `path`: The current page path. - `options`(optional): - `indexAsChild`: Treat index pages as children. **Example:** ```ts import { findPageSiblings } from '@nuxt/content/utils' const siblings = findPageSiblings(navigation, '/docs/guide/getting-started') // siblings is an array of navigation items that share the same parent as the current page ``` # queryCollectionItemSurroundings ## Usage Use the auto-imported `queryCollectionItemSurroundings` to find the previous and next items relative to a specific content item in a collection. This is particularly useful for creating navigation between related content pages. The function returns a chainable promise that allows you to add additional query conditions: ```vue [pages/[...slug\\].vue] ``` ::tip The `queryCollectionItemSurroundings` utility is available in both Vue and Nitro. Checkout [Server Usage](https://content.nuxt.com/#server-usage) for more details on how to use it on the server side. :: ## Type ```ts function queryCollectionItemSurroundings( collection: T, path: string, opts?: SurroundOptions ): ChainablePromise interface ChainablePromise extends Promise { where(field: keyof PageCollections[T] | string, operator: SQLOperator, value?: unknown): ChainablePromise andWhere(groupFactory: QueryGroupFunction): ChainablePromise orWhere(groupFactory: QueryGroupFunction): ChainablePromise order(field: keyof PageCollections[T], direction: 'ASC' | 'DESC'): ChainablePromise } ``` ## API ### `queryCollectionItemSurroundings(collection: CollectionName, path: string, opts?: SurroundOptions)` Find the surrounding items (previous and next) for a specific content item in a collection. - Parameters: - `collection`: The key of the defined collection in `content.config.ts`. - `path`: The path of the current content item. - `opts`: (Optional) An object with the following properties: - `before`: (Optional) The number of items to fetch before the current item. Default is 1. - `after`: (Optional) The number of items to fetch after the current item. Default is 1. - `fields`: (Optional) An array of additional fields to include in the surrounding items. - Returns: A chainable promise that resolves to an array containing the surrounding items. The promise includes methods for adding query conditions: - `where(field, operator, value)`: Add a WHERE condition - `andWhere(groupFactory)`: Add a grouped AND condition - `orWhere(groupFactory)`: Add a grouped OR condition - `order(field, direction)`: Add an ORDER BY clause The final result will be an array with the following structure: - `[previousItem, nextItem]` if using default options - `[...previousItems, ...nextItems]` if using custom `before` and `after` values Each item in the array is of type `ContentNavigationItem` or `null` if there is no item in that position. ## Examples Basic usage without additional query conditions: ```vue [pages/[...slug\\].vue] ``` Example with additional query conditions: ```vue [pages/[...slug\\].vue] ``` ## Server Usage Nuxt Content provides a similar utility to query collections on the server side. The only difference is that you need to pass `event` as the first argument to the `queryCollectionItemSurroundings` function. ```ts [server/api/surroundings.ts] export default eventHandler(async (event) => { const surroundings = await queryCollectionItemSurroundings(event, 'docs', '/foo') return surroundings }) ``` ::note Make sure to create `server/tsconfig.json` file with the following content to avoid type error. ```json { "extends": "../.nuxt/tsconfig.server.json" } ``` :: # queryCollectionSearchSections ## Usage Use the auto-imported `queryCollectionSearchSections` to generate searchable sections from a specific collection. This is particularly useful for creating advanced search functionality or content discovery features in your application. ```vue [app.vue] ``` ::tip The `queryCollectionSearchSections` utility is available in both Vue and Nitro. Checkout [Server Usage](https://content.nuxt.com/#server-usage) for more details on how to use it on the server side. :: ## Type ```ts function queryCollectionSearchSections(collection: T, opts?: { ignoredTags?: string[], minHeading?: string, maxHeading?: string, extraFields?: Array }): ChainablePromise interface ChainablePromise extends Promise { where(field: keyof PageCollections[T] | string, operator: SQLOperator, value?: unknown): ChainablePromise andWhere(groupFactory: QueryGroupFunction): ChainablePromise orWhere(groupFactory: QueryGroupFunction): ChainablePromise order(field: keyof PageCollections[T], direction: 'ASC' | 'DESC'): ChainablePromise } ``` ## API ### `queryCollectionSearchSections(collection: CollectionName, options?: SearchSectionsOptions)` Generate searchable sections from the specified collection. - Parameters: - `collection`: The key of the defined collection in `content.config.ts`. - `options`: (Optional) An object with the following properties: - `ignoredTags`: An array of tag names to ignore when generating sections. Default is an empty array. - `minHeading`: Minimum heading level to split on (e.g., `'h2'`). Default is `'h1'`. - `maxHeading`: Maximum heading level to split on (e.g., `'h3'`). Default is `'h6'`. - `extraFields`: An array of additional fields from the collection items to include in the section objects. - Returns: A Promise that resolves to an array of searchable sections. Each section is an object with the following properties: - `id`: A unique identifier for the section. - `title`: The title of the section (usually the heading text). - `titles`: An array of parent section titles, representing the hierarchy. - `content`: The textual content of the section. - `level`: The heading level (1-6) of the section, where 1 is the highest level. ## Example Here's an example of how to use `queryCollectionSearchSections` to create searchable sections from the 'docs' collection: ```vue [pages/[...slug\\].vue] ``` ## Server Usage Nuxt Content provides a similar utility to query collections on the server side. The only difference is that you need to pass `event` as the first argument to the `queryCollectionSearchSections` function. ```ts [server/api/search-sections.ts] export default eventHandler(async (event) => { const sections = await queryCollectionSearchSections(event, 'docs') return sections }) ``` ::note Make sure to create `server/tsconfig.json` file with the following content to avoid type error. ```json { "extends": "../.nuxt/tsconfig.server.json" } ``` :: # ContentRenderer The `` component renders a document coming from a query with [`queryCollection()`](https://content.nuxt.com/docs/utils/query-collection). ::note This component **only works** with `Markdown` files. :: ## Props | Prop | Default | Type | Description | | ------------ | ----------- | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | | `value` | `{}` | `ParsedContent` | The document to render. | | `tag` | `'div'` | `string` | The tag to use for the renderer element if it is used. | | `excerpt` | `false` | `boolean` | Whether to render the excerpt only without the rest of the content. | | `components` | `{}` | `object` | The map of custom components to use for rendering. This prop will pass to the markdown renderer and will not affect other file types. | | `data` | `{}` | `object` (required) | A map of variables to inject into the markdown content for later use in binding variables. | | `prose` | `undefined` | `boolean` | Whether or not to render Prose components instead of HTML tags. | | `class` | `undefined` | `string` or `object` | Root tag to use for rendering. | | `unwrap` | `false` | `boolean` or `string` | Tags to unwrap separated by spaces. Example: `'ul li'`. | ## Example Usage ```vue [pages/[...slug\\].vue] ``` ## Handling Missing Pages If the queried content is **missing**, you can display a **custom fallback message**. ```vue [pages/[...slug\\].vue] ``` # Slot When you write contents and paragraphs inside a component with the MDC syntax, you can use Vue's `` component to render the content. ## Usage If you don't want to modify the rendered content, simply use Vue's `` component. ```vue [components/content/Callout.vue] ``` Now let's use it in Markdown: ```mdc [content/index.md] ::callout This is a callout. :: ``` The rendered HTML will be: ```html

This is a callout.

``` This usage would be similar to using the native `` component. ### Unwrapping The `mdc-unwrap` prop allows you to remove one or multiple wrapping elements from the rendered content. This is useful when you want to extract the content nested in native Markdown syntax. Each specified tag will get removed from AST. Let's unwrap the `

` element from the previous example: ```vue [components/content/Callout.vue] ``` Now the rendered HTML will be: ```html

This is a callout.
``` ### Named Slots The `name` prop allows you to bind a slot by its name. This is useful when you want to render a slot that is not the default one. Let's improve our `Callout` component to have a `title` slot: ```vue [components/content/Callout.vue] ``` Now let's use it in Markdown: ```mdc [content/index.md] ::callout #title Please be careful! #default Using MDC & Vue components is addictive. :: ``` This will result into: ```html

Please be careful!

Using MDC & Vue components is addictive.

``` When not using the `title` slot, the `h2` element will not be rendered. ## Props - `mdc-unwrap`: Whether to unwrap the content or not. This is useful when you want to extract the content nested in native Markdown syntax. Each specified tag will get removed from AST. - **Type:** `boolean` or `string` - **Default:** `false` - **Example:** `'p'` or `'ul li'` # Prose Components Prose components are replacements for HTML typography tags. Prose components provide a simple way to customize content UI. To overwrite a prose component, create a component with the same name in your project `components/content/` directory (ex: `components/content/ProseA.vue`). ::note Prose components are originally part of [`@nuxtjs/mdc`](https://github.com/nuxt-modules/mdc){rel=""nofollow""} . :: ## `ProseA` ::code-group ```md [Code] [Prose Components](/docs/components/prose) ``` :::code-preview{icon="i-lucide-eye" label="Preview"} [Prose Components](https://content.nuxt.com/docs/components/prose) ::: :: ## `ProseBlockquote` ::code-group ```md [Code] > Block quote ``` :::code-preview{icon="i-lucide-eye" label="Preview"} > Block quote ::: :: ## `ProsePre` ::code-group ````md [Code] ```js [file.js]{2} meta-info=val export default () => { console.log('Code block') } ``` ```` :::code-preview{icon="i-lucide-eye" label="Preview"} ```js [file.js] export default () => { console.log('Code block') } ``` ::: :: Component properties will be: ```json { code: "export default () => {\n console.log('Code block')\n}" language: "js" filename: "file.js" highlights: [2] meta: "meta-info=val" } ``` Check out the [highlight options](https://content.nuxt.com/docs/getting-started/configuration#highlight) for more about the syntax highlighting. ::callout{type="warning"} If you want to use `]` in the filename, you need to escape it with 2 backslashes: `\\]` . This is necessary since JS will automatically escape the backslash in a string so `\]` will be resolved as `]` breaking our regex. :: ## `ProseCode` ::code-group ```md [Code] `code` `const code: string = 'highlighted code inline'`{lang="ts"} ``` :::code-preview{icon="i-lucide-eye" label="Preview"} `code` `const code: string = 'highlighted code inline'`{.shiki,shiki-themes,material-theme-lighter,material-theme,material-theme-palenight lang="ts"} ::: :: ## `ProseH1` ::code-group ```md [Code] # H1 Heading ``` :::code-preview{.pt-4 label="Preview"} # H1 Heading ::: :: ## `ProseH2` ::code-group ```md [Code] ## H2 Heading ``` :::code-preview{icon="i-lucide-eye" label="Preview"} ## H2 Heading ::: :: ## `ProseH3` ::code-group ```md [Code] ### H3 Heading ``` :::code-preview{icon="i-lucide-eye" label="Preview"} ### H3 Heading ::: :: ## `ProseH4` ::code-group ```md [Code] #### H4 Heading ``` :::code-preview{icon="i-lucide-eye" label="Preview"} #### H4 Heading ::: :: ## `ProseH5` ::code-group ```md [Code] ##### H5 Heading ``` :::code-preview{icon="i-lucide-eye" label="Preview"} ##### H5 Heading ::: :: ## `ProseH6` ::code-group ```md [Code] ###### H6 Heading ``` :::code-preview{icon="i-lucide-eye" label="Preview"} ###### H6 Heading ::: :: ## `ProseHr` ::code-group ```md [Code] Divider under. --- Divider above. ``` :::code-preview{icon="i-lucide-eye" label="Preview"} Divider under. --- Divider above. ::: :: ## `ProseImg` ::code-group ```md [Code] ![A Cool Image](https://nuxt.com/assets/design-kit/icon-green.png) ``` :::code-preview{icon="i-lucide-eye" label="Preview"} ![A Cool Image](https://nuxt.com/assets/design-kit/icon-green.png) ::: :: ## `ProseUl` ::code-group ```md [Code] - Just - An - Unordered - List ``` :::code-preview{icon="i-lucide-eye" label="Preview"} - Just - An - Unordered - List ::: :: ## `ProseLi` ::code-group ```md [Code] - List element ``` :::code-preview{icon="i-lucide-eye" label="Preview"} - List element ::: :: ## `ProseOl` ::code-group ```md [Code] 1. Foo 2. Bar 3. Baz ``` :::code-preview{icon="i-lucide-eye" label="Preview"} 1. Foo 2. Bar 3. Baz ::: :: ## `ProseP` ::code-group ```md [Code] Just a paragraph. ``` :::code-preview{icon="i-lucide-eye" label="Preview"} Just a paragraph. ::: :: ## `ProseStrong` ::code-group ```md [Code] **Just a strong paragraph.** ``` :::code-preview{icon="i-lucide-eye" label="Preview"} **Just a strong paragraph.** ::: :: ## `ProseEm` ::code-group ```md [Code] _Just an italic paragraph._ ``` :::code-preview{icon="i-lucide-eye" label="Preview"} *Just an italic paragraph.* ::: :: ## `ProseTable` ::code-group ```md [Code] | Key | Type | Description | | --- | --------- | ----------- | | 1 | Wonderful | Table | | 2 | Wonderful | Data | | 3 | Wonderful | Website | ``` :::code-preview{icon="i-lucide-eye" label="Preview"} | Key | Type | Description | | --- | --------- | ----------- | | 1 | Wonderful | Table | | 2 | Wonderful | Data | | 3 | Wonderful | Website | ::: :: ## `ProseTbody` Included in **ProseTable** example. ## `ProseTd` Included in **ProseTable** example. ## `ProseTh` Included in **ProseTable** example. ## `ProseThead` Included in **ProseTable** example. ## `ProseTr` Included in **ProseTable** example. ::callout --- icon: i-simple-icons-github to: https://github.com/nuxt-modules/mdc/tree/main/src/runtime/components/prose --- Checkout the source code for these components on GitHub. :: # Server Hosting ## What is Node.js preset? Node preset is the default preset for Nuxt, when building your project, Nuxt will output a Node.js server that you can run with `node .output/server/index.mjs`. ## Environment requirement If you are using the default `better-sqlite3` module to operate the sqlite database, then you have to deploy to an OS with Glibc version higher than 2.29, eg. Debian 11, Ubuntu 20.04. ::note You can use `ldd --version` to check the Glibc version. Checkout [issue #3248](https://github.com/nuxt/content/issues/3248){rel=""nofollow""} for more details. :: ## Building with Node.js preset Build project with Nuxt build command: ```bash [Terminal] nuxi build ``` When running `nuxi build` with the Node server preset, the result will be an entry point that launches a ready-to-run Node server. ```bash [Terminal] $ node .output/server/index.mjs Listening on http://localhost:3000 ``` ::note The SQLite database will be loaded on the server side when booting the server as well as in the browser for client-side navigation or actions. :: # Static Hosting ## What is Static Hosting? Static hosting is a type of hosting where your website is built and served as static files (HTML, CSS, JS) that can be served by any static file server. Nuxt Content can be deployed to static hosting using Nuxt prerendering. ## Building with SSG To build your app with static site generation, run the following command: ```bash npx nuxi generate ``` ::tip{icon="i-lucide-check"} This command will create a `dist/` directory with your static site. You can upload it to any static hosting service. :: Nuxt will automatically pre-render all pages using an internal crawler, you can customize it's behavior with the `nitro.prerender` options. ::note{to="https://nuxt.com/docs/getting-started/prerendering"} Learn more about pre-rendering in Nuxt. :: ## What about the Database? Nuxt Content will load the database in the browser using [WASM SQLite](https://content.nuxt.com/docs/advanced/database#wasm-sqlite-in-browser), this way, the content queries happening on client-side navigation or actions will run in the browser. # Serverless Hosting ## What is Serverless Hosting? Serverless hosting lets you run code and applications without managing servers directly - you just upload your code and the cloud provider automatically handles all the infrastructure, scaling, and maintenance while charging you only for the actual compute resources you use. **In serverless environments, each user request triggers a fresh instance of your Nuxt server, meaning it starts from scratch every time.** This "stateless" nature means you can't store data in server memory or use file-based databases like SQLite. That's why we need to use external database services (like D1, Turso, or PostgreSQL) that persist data independently of your server instances. ## Deploy with Serverless The module have built-in support for couple of famous serverless platforms. You can deploy your project to them with ease. Checkout the guides for each platform: - [Cloudflare Pages](https://content.nuxt.com/docs/deploy/cloudflare-pages) - [Vercel](https://content.nuxt.com/docs/deploy/vercel) If you like to deploy to other platforms, you can follow below steps to deploy your project. ### 1. Select a database service Before deploying your project, you need to select a database service: ::code-group ```ts [PostgreSQL] // 1. Create a PostgreSQL database // 2. And add the `POSTGRES_URL` to the env variables export default defineNuxtConfig({ content: { database: { type: 'postgresql', url: process.env.POSTGRES_URL } } }) ``` ```ts [Cloudflare D1] // 1. Create a D1 database in your CF account // 2. Link it to your project with the same binding name export default defineNuxtConfig({ content: { database: { type: 'd1', bindingName: '' } } }) ``` ```ts [LibSQL] // 1. Create a LibSQL database on Turso.tech // 2. And add the `TURSO_DATABASE_URL` and `TURSO_AUTH_TOKEN` env variables export default defineNuxtConfig({ modules: ['@nuxt/content'], content: { database: { type: 'libsql', url: process.env.TURSO_DATABASE_URL, authToken: process.env.TURSO_AUTH_TOKEN, } } }) ``` :: ### 2. Deploy your project Nuxt Content uses Nuxt deployment presets to adjust the build process for different hosting platforms. Various serverless platform are supported with zero configuration: - [Cloudflare](https://nuxt.com/deploy/cloudflare){rel=""nofollow""} - [NuxtHub](https://nuxt.com/deploy/nuxthub){rel=""nofollow""} - [Vercel](https://nuxt.com/deploy/vercel){rel=""nofollow""} - [Netlify](https://nuxt.com/deploy/netlify){rel=""nofollow""} All you need to do is to set the build command to: ```bash [Terminal] nuxi build ``` The generated output will be compatible with the selected platform. ::note The linked database will be loaded on the server side when booting the server. In the browser, a [WASM SQLite](https://content.nuxt.com/docs/advanced/database#wasm-sqlite-in-browser) database will be loaded for client-side navigation and actions. :: ::tip If you wish to deploy to AWS Lambda or Azure Static Web Apps, you need to make sure your sqlite file is in `/tmp` since this is the only writeable folder ```ts export default defineNuxtConfig({ modules: ['@nuxt/content'], content: { database: { type: 'sqlite', filename: '/tmp/contents.sqlite' } } }) ``` :: ### 3. Optimize with pre-rendering As each request trigger a fresh instance of your Nuxt server, the performance of your serverless application will be impacted if you don't pre-render some pages. To optimize your serverless application, you can pre-render your pages using the `routeRules` option: ```ts [nuxt.config.ts] export default defineNuxtConfig({ routeRules: { '/': { prerender: true } } }) ``` ::tip{to="https://hub.nuxt.com/docs/recipes/pre-rendering"} We recommend to checkout **NuxtHub's Pre-rendering guide** to learn more about the different strategies to optimize your serverless application, it applies to all serverless platforms. :: # NuxtHub Nuxt Content module has a built-in integration with [NuxtHub](https://hub.nuxt.com){rel=""nofollow""} to deploy your content. To enable NuxtHub integration, you need to install the `@nuxthub/core` module and register it in your `nuxt.config.ts`. More efficiently, you can use `nuxi module` command to do both at once. ```bash [Terminal] npx nuxi module add hub ``` That's it 🎉 ::callout Nuxt Content module automatically reads the NuxtHub database options in order to share the same database to store the Nuxt Content data. :: Checkout the [NuxtHub documentation](https://hub.nuxt.com){rel=""nofollow""} for more information. # Cloudflare Pages ::card Quick Setup 1. Use `nuxi build --preset=cloudflare_pages` to build your app 2. Create and connect a D1 database to your project in the Cloudflare Dashboard using binding name `DB` 3. Deploy/Redeploy your app :: --- The Nuxt Content module has a built-in integration with [Cloudflare Pages](https://pages.cloudflare.com){rel=""nofollow""} to deploy your content. The Module will automatically detect the build target and prepare the necessary configuration for Cloudflare Pages. You can either use the `--preset=cloudflare_pages` option with the `nuxi build` command or use `nuxt.config.ts` to configure the preset. ```ts [nuxt.config.ts] export default defineNuxtConfig({ nitro: { preset: 'cloudflare_pages', }, }); ``` ## D1 Database A D1 database connected to the app is **required** for the Nuxt Content module to work. By default the module uses the binding name **`DB`**. You can override the [database configuration](https://content.nuxt.com/docs/getting-started/configuration#d1) by providing your own in `nuxt.config.ts`. After creating a new Cloudflare Pages project, you need to create a new D1 database and connect it to the project. Make sure to use the same binding name as the module is using. ### Local Preview While `nuxi dev` and `nuxi build` don't require any extra configuration, testing a build locally with `nuxi preview` requires Cloudflare's Wrangler to be configured to provide a temporary, local database for Nuxt Content to bind. This can be done with a `wrangler.jsonc` or `wrangler.toml` file. Because Wrangler creates a local database, `database_name` and `database_id` can safely, but don't need to, match the values in production. ```jsonc [wrangler.jsonc] { "d1_databases": [ { "binding": "DB", "database_name": "example-db", "database_id": "example-db-id" } ] } ``` That's it 🎉 Relevant resources: - [Nuxt Deploy documentation](https://nuxt.com/deploy/cloudflare){rel=""nofollow""} - [Cloudflare D1 documentation](https://developers.cloudflare.com/d1/){rel=""nofollow""} - [Create and bind a D1 database](https://developers.cloudflare.com/d1/get-started/){rel=""nofollow""} - [Cloudflare Pages documentation](https://developers.cloudflare.com/pages/){rel=""nofollow""} # Cloudflare Workers ::card Quick Setup 1. Use `cloudflare_module` preset and compatibility date of `2024-09-19` or later. 2. Create a D1 database and connect it to your project in the Cloudflare Dashboard under the `DB` binding name, and configure the database configuration in the `nuxt.config.ts` file. 3. Build and deploy your app :: --- The Nuxt Content module has a built-in integration with [Cloudflare Workers](https://workers.cloudflare.com){rel=""nofollow""} to deploy your content. The module will automatically detect the build target and prepare the necessary configuration for Cloudflare Workers. All you need to do is create a Cloudflare D1 database and connect it to your project. After creating a D1 database, you should define the database configuration in the `nuxt.config.ts` file with the `nitro.cloudflare.wrangler.d1_databases` option. By default, the module will use the `DB` binding name. You can override the database configuration by providing your own database configuration in `nuxt.config.ts`. ```ts [nuxt.config.ts] export default defineNuxtConfig({ compatibilityDate: "2025-05-15", nitro: { preset: 'cloudflare_module', cloudflare: { deployConfig: true, wrangler: { d1_databases: [ { binding: 'DB', database_name: 'database-name', database_id: '*********-***-****-****-*********' } ] }, }, } }) ``` ::note If you want to use a different binding name, you can override the database configuration by providing your own database configuration in `nuxt.config.ts` . Check out [Database Configuration](https://content.nuxt.com/docs/getting-started/configuration#d1) :: ::note To deploy a Nuxt project to Cloudflare Workers, you need to use a compatibility date of `2024-09-19` or later. :: Build your project with the `nuxi build` command, and you can deploy the project with the `wrangler deploy` command. ```bash npx wrangler deploy ``` That's it! 🎉 Check out: - [Nuxt Deploy documentation](https://nuxt.com/deploy/cloudflare){rel=""nofollow""} - [Cloudflare D1 documentation](https://developers.cloudflare.com/d1/){rel=""nofollow""} - [Cloudflare Workers documentation](https://developers.cloudflare.com/workers/){rel=""nofollow""} # Vercel ::card Quick Setup - Execute `npx vercel deploy` command or go to Vercel dashboard and create a new project using git repository. :: --- Nuxt Content projects can be deployed to Vercel with zero configuration. The module will automatically detect a Vercel environment and will prepare the necessary configuration for deployment. All you need to do is to execute `npx vercel deploy` command or go to Vercel dashboard and create a new project using git repository. That's it 🎉 ::note By default module will use SQlite database in Vercel located at `/tmp` directory. You can override the database configuration by providing your own database configuration. :br There are a couple of database providers that are supported by Vercel. You can use any of them by providing the correct connection string in `nuxt.config.ts`. :: Checkout: - [Nuxt Deploy documentation](https://nuxt.com/deploy/vercel){rel=""nofollow""} - [Vercel documentation](https://vercel.com/docs/deployments/deployment-methods){rel=""nofollow""} # Netlify ::card Quick Setup - Go go Netlify dashboard and create a new project using git repository. - Go to `Site Configuration` under `Dependency management` and change Node Version to `20.x` or higher. - Go to `deploys` and retry last deployment. :: --- Nuxt Content projects can be deployed to Netlify with zero configuration. The module will automatically detects Netlify environment and prepare the necessary configuration for Netlify. All you need to do is to go to Netlify dashboard and create a new project using git repository. ::note By default Netlify uses Node.js 18.x which is not supported by the module. You need to change the Node.js version in `Site Configuration` under `Dependency management` . :: That's it 🎉 Checkout: - [Nuxt Deploy documentation](https://nuxt.com/deploy/netlify){rel=""nofollow""} - [Netlify documentation](https://www.netlify.com/blog/2016/09/29/a-step-by-step-guide-deploying-on-netlify/){rel=""nofollow""} # AWS Amplify ::card Quick Setup - Prepare Sqlite Connector - Option A (recommended on Node.js 22+): Use native `node:sqlite` - Option B (legacy): Install `sqlite3` package in your project. - Go to AWS Amplify dashboard and create a new project using your git repository and deploy the app. :: --- Nuxt Content projects can be deployed to AWS Amplify with zero configuration. The module will automatically detect an AWS Amplify environment and will prepare the necessary configuration for deployment. ## Option A: Use native `node:sqlite` In order to use native `node:sqlite` package, you need to change node version to 22+. This can be easily done in Amplify dashboard via `Build Settings` > `Live Package Updates` > `Package (Node.js version) = 22`. This is also possible via `amplify.yml` inside `preBuild` phase. ```yml frontend: phases: preBuild: commands: - nvm install 22 - nvm use 22 - node -v - npm ci ``` ::warning If you are using SSR for content, Nitro defaults the Amplify runtime to Node 20. Since `node:sqlite` requires Node 22+, you need to explicitly set the runtime version in `nuxt.config.ts`: ```ts nitro: { awsAmplify: { runtime: 'nodejs22.x', }, }, ``` :: ## Option B: Use `sqlite3` All you need to do is to install `sqlite3` package in your project and go to AWS Amplify dashboard and create a new project using git repository. That's it 🎉 ::note By default module will use SQlite database located at `/tmp` directory. You can override the database configuration by providing your own database configuration. :: Checkout: - [Nuxt Deploy documentation](https://nuxt.com/deploy/aws-amplify){rel=""nofollow""} # Docker Docker is a popular containerization platform that allows you to package your application with all its dependencies into a single container. This makes it easy to deploy your Content app on any platform that supports Docker. ## With Node.js image Using Docker's Node.js image, you can deploy your Content app. All you need is to create a Dockerfile and build the image. Here is an example Dockerfile: ```docker [Dockerfile] # Build Stage 1 FROM node:22-alpine AS build WORKDIR /app RUN corepack enable # Copy package.json and your lockfile, here we add pnpm-lock.yaml for illustration COPY package.json pnpm-lock.yaml .npmrc ./ # Install dependencies RUN pnpm i # Copy the entire project COPY . ./ # Build the project RUN pnpm run build # Build Stage 2 FROM node:22-alpine WORKDIR /app # Only `.output` folder is needed from the build stage COPY --from=build /app/.output/ ./ # Change the port and host ENV PORT=80 ENV HOST=0.0.0.0 EXPOSE 80 CMD ["node", "/app/server/index.mjs"] ``` ## With Bun image If you like to use Bun, you can use the official Bun image. Here is an example Dockerfile: ```docker [Dockerfile] # use the official Bun image # see all versions at https://hub.docker.com/r/oven/bun/tags FROM oven/bun:1 AS build WORKDIR /app COPY package.json bun.lock* ./ # use ignore-scripts to avoid building node modules like better-sqlite3 RUN bun install --frozen-lockfile --ignore-scripts # Copy the entire project COPY . . RUN bun --bun run build # copy production dependencies and source code into final image FROM oven/bun:1 AS production WORKDIR /app # Only `.output` folder is needed from the build stage COPY --from=build /app/.output /app # run the app EXPOSE 3000/tcp ENTRYPOINT [ "bun", "--bun", "run", "/app/server/index.mjs" ] ``` # Nuxt i18n module Nuxt Content integrates with `@nuxtjs/i18n` to create multi-language websites. When both modules are configured together, you can organize content by language and automatically serve the correct content based on the user's locale. ## Setup ::prose-steps ### Install the required module ```bash [terminal] npm install @nuxtjs/i18n ``` ### Configure your `nuxt.config.ts` ```ts [nuxt.config.ts] export default defineNuxtConfig({ modules: ['@nuxt/content', '@nuxtjs/i18n'], i18n: { locales: [ { code: 'en', name: 'English', language: 'en-US', dir: 'ltr' }, { code: 'fr', name: 'French', language: 'fr-FR' }, { code: 'fa', name: 'Farsi', language: 'fa-IR', dir: 'rtl' }, ], strategy: 'prefix_except_default', defaultLocale: 'en', } }) ``` ### Define collections for each language Create separate collections for each language in your `content.config.ts`: ```ts [content.config.ts] const commonSchema = ...; export default defineContentConfig({ collections: { // English content collection content_en: defineCollection({ type: 'page', source: { include: 'en/**', prefix: '', }, schema: commonSchema, }), // French content collection content_fr: defineCollection({ type: 'page', source: { include: 'fr/**', prefix: '', }, schema: commonSchema, }), // Farsi content collection content_fa: defineCollection({ type: 'page', source: { include: 'fa/**', prefix: '', }, schema: commonSchema, }), }, }) ``` ### Create dynamic pages Create a catch-all page that fetches content based on the current locale: ```vue [pages/[...slug\\].vue] ``` :: That's it! 🚀 Your multi-language content site is ready. ## Content Structure Organize your content files in language-specific folders to match your collections: ```text content/ en/ index.md about.md blog/ post-1.md fr/ index.md about.md blog/ post-1.md fa/ index.md about.md ``` Each language folder should contain the same structure to ensure content parity across locales. ## Fallback Strategy You can implement a fallback strategy to show content from the default locale when content is missing in the current locale: ```ts [pages/[...slug\\].vue] const { data: page } = await useAsyncData('page-' + slug.value, async () => { const collection = ('content_' + locale.value) as keyof Collections let content = await queryCollection(collection).path(slug.value).first() // Fallback to default locale if content is missing if (!content && locale.value !== 'en') { content = await queryCollection('content_en').path(slug.value).first() } return content }) ``` ::prose-warning Make sure to handle missing content gracefully and provide clear feedback to users when content is not available in their preferred language. :: ## Complete Examples You can see a complete working example: - **Source**: {rel=""nofollow""} - **Live Demo**: {rel=""nofollow""} # Nuxt LLMs module The Nuxt Content module integrates `nuxt-llms` to prepare your content for Large Language Models (LLMs). When `nuxt-llms` is detected, Content module automatically extends the LLMs module and inject collections of type [page](https://content.nuxt.com/docs/collections/types#page-type){rel=""nofollow""} to the LLMs module.🚀 ## Setup ::prose-steps ### Install the required module ```bash [terminal] npm install nuxt-llms ``` ### Configure your `nuxt.config.ts` ```ts [nuxt.config.ts] export default defineNuxtConfig({ modules: ['@nuxt/content', 'nuxt-llms'], llms: { domain: 'https://your-site.com', title: 'Your Site Name', description: 'A brief description of your site', }, }) ``` :: That's it 🚀 `/llms.txt` file is automatically generated and pre-rendered. ## Raw markdown access When `nuxt-llms` is enabled, Nuxt Content also exposes a raw markdown endpoint so you can stream LLM-ready source files without going through the full rendering pipeline. - **Endpoint**: `/raw/.md` (use the same path as the page URL, drop trailing `/index`, and keep the `.md` extension), returning `text/markdown; charset=utf-8`. - **Scope**: only `page` collections are included; exclude specific collections with `llms.contentRawMarkdown.excludeCollections`. Set `llms.contentRawMarkdown = false` to disable the endpoint entirely. - **Output**: if the requested document is missing a top-level heading or description, the route prepends the title and description to the markdown body before returning it. - **llms.txt links**: document links generated in `llms.txt` are automatically rewritten to the `/raw/...md` endpoint (unless the collection is excluded or the feature is disabled) so agents fetch compact markdown instead of full HTML, reducing token usage and improving response speed. Control this with `llms.contentRawMarkdown.rewriteLLMSTxt` (defaults to `true`). ```ts [nuxt.config.ts] export default defineNuxtConfig({ modules: ['@nuxt/content', 'nuxt-llms'], llms: { contentRawMarkdown: { // Optional: prevent specific page collections from being exposed excludeCollections: ['blog'], // Optional: keep llms.txt links pointing to rendered pages rewriteLLMSTxt: false, }, }, }) ``` ## Sections When generating content, you can create custom sections to process your content into LLM-friendly formats. You can create custom sections to the `llms.sections` array and define the `contentCollection` and `contentFilters` option for each section. ::prose-warning If there is no section defined in the `contentCollection` option, the module will only add [page](https://content.nuxt.com/docs/collections/types#page-type){rel=""nofollow""} collections to the LLMs module. :: ### `contentCollection` This option specifies which content collection to use as source. ```ts [nuxt.config.ts] export default defineNuxtConfig({ llms: { sections: [ { title: 'Documentation', description: 'Technical documentation and guides', contentCollection: 'docs', }, ], }, }) ``` ### `contentFilters` This options defines filters to select specific content within the collection. You precisely control which content is included. Each filter consists of: - `field`: The content property to check - `operator`: Comparison operator (`=`, `<>`, `>`, `<`, `LIKE`, `IN`, `NOT IN`, `IS NULL`, `IS NOT NULL`, etc.) - `value`: The value to compare against ```ts [nuxt.config.ts] export default defineNuxtConfig({ llms: { sections: [ { title: 'Documentation', description: 'Technical documentation and guides', contentCollection: 'docs', contentFilters: [ // Only include markdown files { field: 'extension', operator: '=', value: 'md' }, // Only include published content { field: 'draft', operator: '<>', value: true }, // Filter by directory { field: 'path', operator: 'LIKE', value: '/guide%' }, ] }, ], }, }) ``` ::tip{to="https://github.com/nuxtlabs/nuxt-llms"} Checkout the nuxt-llms documentation for more information about the module. :: # Full-Text Search Content module exposes a handy utility [`queryCollectionSearchSections`](https://content.nuxt.com/docs/utils/query-collection-search-sections) to break down content files into searchable sections. This is useful for implementing full-text search in your website. You can use the result of this utility in combination with [Nuxt UI Content Search](https://ui.nuxt.com/pro/components/content-search){rel=""nofollow""} or other search libraries like [Fuse.js](https://fusejs.io/){rel=""nofollow""}, [minisearch](https://lucaong.github.io/minisearch){rel=""nofollow""}, etc. ## Nuxt UI Nuxt UI provides a ready to use component for full-text search. You can use it by passing the result of `queryCollectionSearchSections` to the `files` prop of the component. Read more about [Nuxt UI Content Search](https://ui.nuxt.com/pro/components/content-search){rel=""nofollow""}. ::code-group ```vue [UContentSearchExample.vue] ``` :::code-preview{icon="i-lucide-eye" label="Preview"} :example-fulltext-content-search ::: :: ## MiniSearch example Read more about [minisearch](https://lucaong.github.io/minisearch){rel=""nofollow""}. ::code-group ```vue [MiniSearchExample.vue] ``` :::code-preview{icon="i-lucide-eye" label="Preview"} :example-fulltext-mini-search ::: :: ## Fuse.js example Read more about [Fuse.js](https://fusejs.io){rel=""nofollow""}. ::code-group ```vue [FusejsExample.vue] ``` :::code-preview{icon="i-lucide-eye" label="Preview"} :example-fulltext-fusejs ::: :: # Raw Content There were lots of requests in Content version 2 about accessing contents raw data in production. In Content version 3 it is possible to ship contents raw data to production. In order to ship raw contents to production you need to define `rawbody` field in your collection's schema. That's it. Nuxt Content will detect this magical field in your schema and fill it with the raw content. ```ts [content.config.ts] import { defineCollection, defineContentConfig } from '@nuxt/content' import { z } from 'zod' export default defineContentConfig({ collections: { docs: defineCollection({ source: '**', type: 'page', schema: z.object({ rawbody: z.string() }) }) } }) ``` And you can use `queryCollection()` to fetch the raw content. ```vue [pages/index.vue] ``` In case you don't want to ship raw content of a specific file you can add `rawbody: ''` to frontmatter of that file. The auto filled value of `rawbody` is acting like default value and when you define `rawbody` in the frontmatter it will overwritten. ```md [content.md] --- title: My page rawbody: '' --- ``` ::callout It is important to fill frontmatter fields with a same type of data that is defined in collection schema. In this case `rawbody` is a string, and you should consider passing empty string. Do not use `boolean` or other type of values. :: # Database In Content v3, we have introduced a robust storage layer based on SQLite, which offers a powerful and efficient method for managing content. This marks a significant enhancement over the previous file-based storage system, which was constrained by performance and scalability limitations. > In Content v2, the system read and parsed content during the Nitro runtime, creating a cache file for each content file to store the parsed data. This method introduced considerable overhead to the website's runtime. > > - The I/O time in production was substantial, as the module had to load all cache files to search through the content. > - Additionally, the lack of optimization and compression for the content resulted in a large bundle size, particularly problematic in edge environments. Content management in Content v3 involves three key steps, which are designed to streamline the process and enhance performance. ## Generating Database Dump For each collection in your project, the module reads the content from the defined source and parses it into an Abstract Syntax Tree (AST). It creates a specific table for each collection based on its schema. The parsed content is then inserted into the corresponding table, ensuring that the data structure aligns with the defined schema for optimal querying. Everything is then saved in a dump file. ## Restoring Dump on Cold Start During runtime, when the application executes the first query to retrieve content, the module reads the generated dump from the previous step and restores it into the target database. This process is fast and optimized for each deployment mode and platform. The module employs a special integrity check mechanism to ensure that the database is updated with the latest content. This same integrity check also prevents duplicate imports, maintaining the integrity and accuracy of the data stored. ## WASM SQLite in Browser For client-side navigation, the module utilizes a similar approach. When the application executes the first query for content, it downloads the generated dump from the server and initializes a local SQLite database within the browser. From that point onward, all queries are executed locally without needing to call the server, significantly improving the responsiveness of the application and providing a seamless user experience. This architecture not only enhances performance but also allows for offline capabilities, enabling users to access content even without an active internet connection. The combination of server-side and client-side processing ensures that Nuxt Content v3 is both powerful and flexible, catering to a wide range of use cases and environments. # Tools Nuxt Content uses an **SQLite database (`contents.sqlite`)** to store and query content efficiently. If you're running into **missing content, slow queries, or database issues**, debugging your SQLite database can help! ::callout --- icon: i-simple-icons-visualstudiocode to: https://marketplace.visualstudio.com/items?itemName=alexcvzz.vscode-sqlite --- A simple way to inspect it? **Use the SQLite VS Code extension!** :: ## Install SQLite VS Code Extension 1. Open **Visual Studio Code**. 2. Go to the **Extensions** panel (`Ctrl+Shift+X` / `Cmd+Shift+X` on Mac). 3. Search for **"SQLite"** (by `alexcvzz`) and install it. 4. Open your Nuxt Content database (`.data/content/contents.sqlite`). ::tip{icon="i-lucide-lightbulb"} If you don't see `contents.sqlite`, start your Nuxt app first: ```bash [Terminal] npx nuxi dev ``` :: ## Locate Your SQLite Database Nuxt Content stores its database here: ```bash .data/content/contents.sqlite ``` ::note{to="https://nuxt.com/docs/getting-started/prerendering"} This file is automatically generated when you start your Nuxt app. No need to create it manually! :: ## Open & Explore the Database 1. **Right-click** on `contents.sqlite` in VS Code. 2. Select **"Open Database"**. 3. Expand the **Database Explorer** panel to view tables & data. ![SQLite Explorer in VS Code](https://github.com/user-attachments/assets/c9f22c4c-7a95-43e8-ab03-aa76f2e49c8e) ## Fixing Common Issues ### Content Not Showing? 1. **Check if the database exists** (`.data/content/contents.sqlite`). 2. **Run a cleanup & restart Nuxt**: ```bash \[Terminal] npx nuxi cleanup && npx nuxi dev ``` 3. **Check if content is inside the database** (run an SQL query). ### Manually Reset the Database If things seem **really broken**, try resetting it: 1. **Delete the database file**: ```bash \[Terminal] rm -rf .data/content/contents.sqlite ``` 2. **Run cleanup**to remove old caches: ```bash \[Terminal] npx nuxi cleanup ``` 3. **Restart Nuxt**to generate a fresh database: ```bash \[Terminal] npx nuxi dev ``` ::note{icon="i-lucide-triangle-alert"} Cleaning up will remove cached data. Don't worry—it regenerates automatically! :: ## More Debugging Tools If VS Code isn’t enough, check out: - 🖥️ [**DB Browser for SQLite**](https://sqlitebrowser.org/){rel=""nofollow""} – A visual tool for inspecting & modifying the database. - 🛠️ **SQLite Command Line** – Use `sqlite3 contents.sqlite` to run SQL queries from your terminal. # Hooks ## `content:file:beforeParse`{.shiki,shiki-themes,material-theme-lighter,material-theme,material-theme-palenight lang="ts"} This hook is called before the content is parsed. It can be used to modify the raw content from a `file` before it is transformed or modify the transform options. ```ts export default defineNuxtConfig({ hooks: { 'content:file:beforeParse'(ctx) { // ... } } }) ``` ## `content:file:afterParse`{.shiki,shiki-themes,material-theme-lighter,material-theme,material-theme-palenight lang="ts"} This hook is called after the content is parsed and before it is saved to the database. ```ts export default defineNuxtConfig({ hooks: { 'content:file:afterParse'(ctx) { // ... } } }) ``` ## Example Usage ```ts [nuxt.config.ts] export default defineNuxtConfig({ // ... hooks: { 'content:file:beforeParse'(ctx) { const { file } = ctx; if (file.id.endsWith(".md")) { file.body = file.body.replace(/react/gi, "Vue"); } }, 'content:file:afterParse'(ctx) { const { file, content } = ctx; const wordsPerMinute = 180; const text = typeof file.body === 'string' ? file.body : ''; const wordCount = text.split(/\s+/).length; content.readingTime = Math.ceil(wordCount / wordsPerMinute); } } }) ``` ::note{icon="i-lucide-info"} In the `content:file:afterParse` hook, we added a custom property to our content object. To be able to access that property within our pages using [`queryCollection()`](https://content.nuxt.com/docs/utils/query-collection), we first need to define it in our content schema. ```ts [content.config.ts] export default defineContentConfig({ collections: { content: defineCollection({ type: 'page', source: '**/*.md', schema: z.object({ readingTime: z.number().optional() }) }) } }); ``` :: # Custom Source By default, Nuxt Content provides some built-in sources like local files source and remote Github source. However this is not enough for some cases, for example, you want to fetch data from a remote API. In this case, you can define a custom source to fetch data and use it in your collections. Using `defineCollectionSource`, you can define a custom source. ```ts import { defineCollectionSource } from '@nuxt/content' const hackernewsSource = defineCollectionSource({ getKeys: () => { return fetch('https://hacker-news.firebaseio.com/v0/topstories.json') .then(res => res.json()) .then(data => data.map((key: string) => `${key}.json`)) }, getItem: (key: string) => { const id = key.split('.')[0] return fetch(`https://hacker-news.firebaseio.com/v0/item/${id}.json`) .then(res => res.json()) }, }) ``` Then you can use this source in your collections. ```ts [content.config.ts] import { defineContentConfig, defineCollectionSource, defineCollection } from '@nuxt/content' import { z } from 'zod' const hackernewsSource = defineCollectionSource({ getKeys: () => { return fetch('https://hacker-news.firebaseio.com/v0/topstories.json') .then(res => res.json()) .then(data => data.map((key: string) => `${key}.json`)) }, getItem: (key: string) => { const id = key.split('.')[0] return fetch(`https://hacker-news.firebaseio.com/v0/item/${id}.json`) .then(res => res.json()) }, }) const hackernews = defineCollection({ type: 'data', source: hackernewsSource, schema: z.object({ title: z.string(), date: z.date(), type: z.string(), score: z.number(), url: z.string(), by: z.string(), }), }) export default defineContentConfig({ collections: { hackernews, }, }) ``` # Transformers Transformers in Nuxt Content allow you to programmatically parse, modify, or analyze your content files as they are processed. They are especially useful for: - Adding or modifying fields (e.g., appending to the title, generating slugs) - Extracting metadata (e.g., listing used components) - Enriching content with computed data - Supporting new content types ## Defining a Transformer You can define a transformer using the `defineTransformer` helper from `@nuxt/content`: ```ts [~~/transformers/title-suffix.ts] import { defineTransformer } from '@nuxt/content' export default defineTransformer({ name: 'title-suffix', extensions: ['.md'], // File extensions to apply this transformer to transform(file) { // Modify the file object as needed return { ...file, title: file.title + ' (suffix)', } }, }) ``` ### Transformer Options - `name` (string): A unique name for your transformer. - `extensions` (string [] ): File extensions this transformer should apply to (e.g., `['.md']`). - `transform` (function): The function that receives the file object and returns the modified file. ## Registering Transformers Transformers are registered in your `nuxt.config.ts`: ```ts [nuxt.config.ts] export default defineNuxtConfig({ content: { build: { transformers: [ '~~/transformers/title-suffix', '~~/transformers/my-custom-transformer', ], }, }, }) ``` ## Example: Adding Metadata Transformers can add a `__metadata` field to the file. This field is not stored in the database but can be used for runtime logic. ```ts [~~/transformers/component-metadata.ts] import { defineTransformer } from '@nuxt/content' export default defineTransformer({ name: 'component-metadata', extensions: ['.md'], transform(file) { // Example: Detect if a custom component is used const usesMyComponent = file.body?.includes('') return { ...file, __metadata: { components: usesMyComponent ? ['MyCustomComponent'] : [], }, } }, }) ``` **Note:** The `__metadata` field is only available at runtime and is not persisted in the content database. ## API Reference ```ts interface Transformer { name: string extensions: string[] transform: (file: ContentFile) => ContentFile } ``` - `ContentFile` is the object representing the parsed content file, including frontmatter, body, and other fields. ## Supporting New File Formats with Transformers Transformers are not limited to modifying existing content—they can also be used to add support for new file formats in Nuxt Content. By defining a transformer with a custom `parse` method, you can instruct Nuxt Content how to read and process files with new extensions, such as YAML. ### Example: YAML File Support Suppose you want to support `.yml` and `.yaml` files in your content directory. You can create a transformer that parses YAML frontmatter and body, and registers it for those extensions: ```ts [~~/transformers/yaml.ts] import { defineTransformer } from '@nuxt/content' export default defineTransformer({ name: 'Yaml', extensions: ['.yml', '.yaml'], parse: (file) => { const { id, body } = file // parse the body with your favorite yaml parser const parsed = parseYaml(body) return { ...parsed, id, } }, }) ``` Register your YAML transformer in your Nuxt config just like any other transformer: ```ts export default defineNuxtConfig({ content: { build: { transformers: [ '~~/transformers/yaml', // ...other transformers ], }, }, }) ``` This approach allows you to extend Nuxt Content to handle any custom file format you need. # The git-based CMS for Nuxt projects ::u-page-hero --- orientation: horizontal --- :::code-group ```mdc [content/index.md] --- title: The Mountains Website description: A website about the most iconic mountains in the world. --- ::my-vue-hero-component{orientation="horizontal"} #title Welcome to the Mountains Website. #description This is a description of the Mountains Website. :: This is a paragraph with **bold** and _italic_ text. ``` ```vue [pages/index.vue] ``` ::: #headline :::u-button --- class: mb-3 rounded-full size: sm to: https://nuxt.studio trailing-icon: i-lucide-arrow-right variant: outline --- Nuxt Studio is out ::: #title The [git-based]{.text-primary} :br CMS for Nuxt. #description Nuxt Content is a module for Nuxt that provides a simple way to manage content for your application. It allows developers to write their content in Markdown, YAML or JSON files and then query and display it in their application. #links :::u-button --- label: Get Started size: lg to: https://content.nuxt.com/docs/getting-started/installation trailingIcon: i-lucide-arrow-right --- ::: :u-input-copy{value="npx nuxt module add content"} :: ::u-container{.pb-12.xl:pb-24} :::u-page-grid ::::u-page-feature --- icon: i-lucide-files --- #title{unwrap="p"} File-based CMS #description{unwrap="p"} Write your content in Markdown, YAML, CSV or JSON and query it in your components. :::: ::::u-page-feature --- icon: i-lucide-filter --- #title{unwrap="p"} Query Builder #description{unwrap="p"} Query your content with a MongoDB-like API to fetch the right data at the right time. :::: ::::u-page-feature --- icon: i-lucide-database --- #title{unwrap="p"} SQLite powered #description{unwrap="p"} Add custom fields to your content, making it suitable for various types of projects. :::: ::::u-page-feature --- icon: i-simple-icons-markdown --- #title{unwrap="p"} Markdown with Vue #description{unwrap="p"} Use Vue components in Markdown files, with props, slots and nested components. :::: ::::u-page-feature --- icon: i-lucide-list-minus --- #title{unwrap="p"} Code highlighting #description{unwrap="p"} Display beautiful code blocks on your website with the Shiki integration supporting VS Code themes. :::: ::::u-page-feature --- icon: i-lucide-mouse-pointer-click --- #title{unwrap="p"} Visual Editor #description{unwrap="p"} Let your team edit your Nuxt Content project with Nuxt Studio, our visual editor. :::: ::::u-page-feature --- icon: i-lucide-panel-left --- #title{unwrap="p"} Navigation Generation #description{unwrap="p"} Generate a structured object from your content files and display a navigation menu in minutes. :::: ::::u-page-feature --- icon: i-lucide-heading-1 --- #title{unwrap="p"} Prose Components #description{unwrap="p"} Customize HTML typography tags with Vue components to give your content a consistent style. :::: ::::u-page-feature --- icon: i-lucide-globe --- #title{unwrap="p"} Deploy everywhere #description{unwrap="p"} Nuxt Content works on all hosting providers, static, server, serverless & edge. :::: ::: :: ::u-page-section #title Everything you need for content management #description Combine file-based simplicity with Vue component power. Build content-rich websites, from documentation pages to complex applications. :::div{.hidden.md:block} :u-color-mode-image{.size-full.absolute.top-0.inset-0 dark="/home/features-dark.svg" light="/home/features-light.svg"} ::: :: ::u-page-section --- reverse: true orientation: horizontal --- :::tabs ::::tabs-item{icon="i-lucide-eye" label="Preview"} :::::browser-frame ::::::example-landing-hero --- image: /mountains/everest.jpg --- #title The Everest. #description The Everest is the highest mountain in the world, standing at 8,848 meters above sea level. :::::: ::::: :::: ::::tabs-item{icon="i-simple-icons-markdown" label="content/index.md"} ```mdc [content/index.md] --- title: The Mountains Website description: A website about the most iconic mountains in the world. --- ::landing-hero --- image: /mountains/everest.jpg --- #title The Everest. #description The Everest is the highest mountain in the world, standing at 8,848 meters above sea level. :: ``` :::: ::::tabs-item{icon="i-simple-icons-vuedotjs" label="components/LandingHero.vue"} ```vue [components/LandingHero.vue] ``` :::: ::: #title Markdown meets [Vue]{.text-(--ui-primary)} components #description We created the MDC syntax to let you use Vue components with props and slots inside your Markdown files. #features :::u-page-feature --- icon: i-lucide-list --- #title{unwrap="p"} Specify props with frontmatter syntax ::: :::u-page-feature --- icon: i-lucide-hash --- #title{unwrap="p"} Use components slots with `#` ::: :::u-page-feature --- icon: i-lucide-code-xml --- #title{unwrap="p"} Add any other html attributes ::: #links :::u-button --- color: neutral label: Learn more about MDC to: https://content.nuxt.com/docs/files/markdown#mdc-syntax trailingIcon: i-lucide-arrow-right variant: subtle --- ::: :: ::u-page-section --- orientation: horizontal --- :::tabs ::::tabs-item{icon="i-simple-icons-vuedotjs" label="pages/blog.vue"} ```vue [pages/blog.vue] ``` :::: ::::tabs-item{icon="i-simple-icons-typescript" label="content.config.ts"} ```ts [content.config.ts] import { defineContentConfig, defineCollection } from '@nuxt/content' import { z } from 'zod' export default defineContentConfig({ collections: { blog: defineCollection({ source: 'blog/*.md', type: 'page', // Define custom schema for docs collection schema: z.object({ tags: z.array(z.string()), image: z.string(), date: z.Date() }) }) } }) ``` :::: ::: #title Query with [Type-Safety]{.text-(--ui-secondary)} #description Define your content structure with collections and query them with schema validation and full type-safety. #features :::u-page-feature --- icon: i-lucide-layout-grid --- #title{unwrap="p"} Create collections for similar content files ::: :::u-page-feature --- icon: i-lucide-circle-check --- #title{unwrap="p"} Define schema for the collection frontmatter ::: :::u-page-feature --- icon: i-lucide-text-cursor --- #title{unwrap="p"} Get auto-completion in your Vue files ::: #links :::u-button --- color: neutral label: Learn more about content collections to: https://content.nuxt.com/docs/collections/define trailingIcon: i-lucide-arrow-right variant: subtle --- ::: :: ::u-page-section --- reverse: true orientation: horizontal --- :video{autoplay controls loop src="https://res.cloudinary.com/nuxt/video/upload/v1767647099/studio/studio-demo_eiofld.mp4"} #title{unwrap="p"} Let [anyone edit]{.text-(--ui-primary)} your website #description :::u-button --- color: primary target: _blank to: https://nuxt.studio variant: outline --- Try Nuxt Studio ::: Edit your Nuxt Content website with the **Studio module**, our free and open-source visual interface to edit your content in production. #features :::u-page-feature --- icon: i-lucide-mouse-pointer-click --- #title{unwrap="p"} Live preview of your content directly on your production website ::: :::u-page-feature --- icon: i-lucide-file-text --- #title{unwrap="p"} Visual editor for Markdown, YML and JSON files ::: :::u-page-feature --- icon: i-simple-icons-git --- #title{unwrap="p"} Publish changes directly on your Git provider ::: :: ::u-page-section :::div{.hidden.md:block} :u-color-mode-image{.size-full.absolute.bottom-0.inset-0.z-[-1] dark="/home/cta-dark.svg" light="/home/cta-light.svg"} ::: #title Add a git-based CMS to your Nuxt project. #links :u-button{label="Start reading docs" to="https://content.nuxt.com/docs/getting-started/installation" trailing-icon="i-lucide-arrow-right"} :: # Canvas ::template-core Canvas Portfolio is a fully customizable i18n portfolio template built with Nuxt and Nuxt UI, designed to help you showcase your work, testimonials, and key information with ease. The template integrates with Nuxt Studio module for a smooth editing experience, while leveraging Nuxt Content for content management. Built with performance, flexibility, and responsiveness in mind, Canvas Portfolio provides a robust foundation for developers and creatives alike. - **Modern Components & Layouts** – Includes built-in components. - **Nuxt UI v3** – Utilize pre-built, customizable UI components. - **NuxtHub ready** - Deploy on NuxtHub in seconds. - **Tailwind CSS** – A beautiful, responsive design system. - **Working Contact Form** – Integrated with Resend for easy email handling. - **Multi-language Support** – Powered by Nuxt i18n. - **SEO-Ready** – Open Graph Image (Nuxt OG Image) & Nuxt Robots for automatic robots.txt generation. - **Good practices** – Auto-generated sitemap, optimized images (Nuxt Image), and ESLint (Nuxt config with Flat config). - **Fully Responsive** – Adapts to all modern browsers and devices. - **Minimal & Professional Design** – Clean, elegant, and easy to customize. #right :::template-features --- features: - label: Nuxt Architecture content: Harness the full power of Nuxt 3 and its modules ecosystem. - label: Nuxt Studio ready content: Edit your content with live-preview with Nuxt Studio module. - label: Vue Components content: Insert built-in components (or your own) inside your content. - label: Write Markdown content: Enjoy the ease and simplicity of Markdown and discover MDC syntax. - label: Deploy anywhere content: In one click from Studio or with zero config on Vercel or Netlify. Choose between static generation, on-demand rendering (Node) or edge-side rendering on CloudFlare workers. - label: Extensible content: Customize the whole design, or add components using slots - you can make Content-Wind your own. --- ::: :: # Content Wind ::template-core A lightweight Nuxt theme to build a Markdown driven website, based on Nuxt Content, TailwindCSS and Iconify - Use layouts in Markdown pages - Enjoy meta tag generation - Configurable prose components - Generated navigation from pages - Switch between light & dark mode - Access 100,000 icons from 100+ icon sets - Highlight code blocks with Shiki #right :::template-features --- features: - label: Nuxt Architecture content: Harness the full power of Nuxt 3 and its modules ecosystem. - label: Nuxt Studio ready content: Edit your theme content and appearance with live-preview within Nuxt Studio. - label: Vue Components content: Insert built-in components (or your own) inside your content. - label: Write Markdown content: Enjoy the ease and simplicity of Markdown and discover MDC syntax. - label: Deploy anywhere content: In one click from Studio or with zero config on Vercel or Netlify. Choose between static generation, on-demand rendering (Node) or edge-side rendering on CloudFlare workers. - label: Extensible content: Customize the whole design, or add components using slots - you can make Content-Wind your own. --- ::: :: # Docs ::template-core [Nuxt UI](https://ui.nuxt.com){rel=""nofollow""} is a collection of premium components designed to facilitate the creation of appealing and responsive Nuxt applications in a matter of minutes. The Nuxt UI team is dedicated to deliver the best integration and customization experience, while the Studio team is providing full compatibility with Nuxt Studio. - **Fully customizable**: change the style of any component from your App Config or customize them specifically through the ui prop. - **Write Markdown with ease**: Nuxt UI overrides Nuxt Content prose components to make them awesome but also adds new ones like Callout, CodeGroup, Field, etc. - **Beautiful Typography styles**: Tailwind CSS typography plugin is pre-configured and styled to match Nuxt UI components and colors. - **Full-Text Search out of the box**: Nuxt UI ships with a ready to use command palette component. No need to setup Algolia DocSearch anymore. - **Slots for everything**: Each component leverages the power of Vue's slots to give you the flexibility to build anything. - **Responsive by design**: Nuxt UI components aims to structure your content, they are responsive by design and will adapt to any screen size. #right :::template-features --- features: - label: Nuxt 3 content: Powered by Nuxt 3 for optimal performances and SEO. - label: Markdown content: Write your pages with MDC thanks to Nuxt Content. - label: Nuxt UI content: Offers a very large set of full customizable components. - label: TypeScript content: A fully typed development experience. - label: Nuxt Studio content: Use Nuxt Studio module for fast updates and previews. - label: Search content: A full-text search modal empowered by Fuse.js. --- ::: :: # Docus ::template-core > A beautiful, minimal starter for creating documentation with Docus This is the default Docus starter template that provides everything you need to build beautiful documentation sites with Markdown and Vue components. ## ✨ Features - 🎨 **Beautiful Design** - Clean, modern documentation theme - 📱 **Responsive** - Mobile-first responsive design - 🌙 **Dark Mode** - Built-in dark/light mode support - 🔍 **Search** - Full-text search functionality - 📝 **Markdown Enhanced** - Extended markdown with custom components - 🎨 **Customizable** - Easy theming and brand customization - ⚡ **Fast** - Optimized for performance with Nuxt 4 - 🔧 **TypeScript** - Full TypeScript support ## 🚀 Quick Start ```bash # Install dependencies npm install # Start development server npm run dev ``` Your documentation site will be running at `http://localhost:3000` ## 📁 Project Structure ```text my-docs/ ├── content/ # Your markdown content │ ├── index.md # Homepage │ ├── 1.getting-started/ # Getting started section │ └── 2.essentials/ # Essential documentation ├── public/ # Static assets └── package.json # Dependencies and scripts ``` ## ⚡ Built with This starter comes pre-configured with: - [Nuxt 4](https://nuxt.com){rel=""nofollow""} - The web framework - [Nuxt Content](https://content.nuxt.com/){rel=""nofollow""} - File-based CMS - [Nuxt UI](https://ui.nuxt.com){rel=""nofollow""} - Premium UI components - [Nuxt Image](https://image.nuxt.com/){rel=""nofollow""} - Optimized images - [Tailwind CSS 4](https://tailwindcss.com/){rel=""nofollow""} - Utility-first CSS - [Docus Layer](https://www.npmjs.com/package/docus){rel=""nofollow""} - Documentation theme ## 📖 Documentation For detailed documentation on customizing your Docus project, visit the [Docus Documentation](https://docus.dev){rel=""nofollow""} ## 🚀 Deployment Build for production: ```bash npm run build ``` The built files will be in the `.output` directory, ready for deployment to any hosting provider that supports Node.js. ## 📄 License [MIT License](https://opensource.org/licenses/MIT){rel=""nofollow""} #right :::template-features --- features: - label: Nuxt 3 content: Powered by Nuxt 3 for optimal performances and SEO. - label: Markdown content: Write your pages with MDC thanks to Nuxt Content. - label: Nuxt UI content: Offers a very large set of full customizable components. - label: TypeScript content: A fully typed development experience. - label: Nuxt Studio content: Supported by Nuxt Studio for fast updates and previews. - label: Search content: A full-text search modal empowered by Fuse.js. --- ::: :: # Docus I18n ::template-core > A beautiful, internationalized starter for creating multi-language documentation with Docus This is the i18n Docus starter template that provides everything you need to build beautiful, multi-language documentation sites with Markdown and Vue components. ## ✨ Features - 🌍 **Internationalization** - Native i18n support for multi-language docs - 🎨 **Beautiful Design** - Clean, modern documentation theme - 📱 **Responsive** - Mobile-first responsive design - 🌙 **Dark Mode** - Built-in dark/light mode support - 🔍 **Search** - Full-text search functionality per language - 📝 **Markdown Enhanced** - Extended markdown with custom components - 🎨 **Customizable** - Easy theming and brand customization - ⚡ **Fast** - Optimized for performance with Nuxt 4 - 🔧 **TypeScript** - Full TypeScript support ## 🚀 Quick Start ```bash # Install dependencies npm install # Start development server npm run dev ``` Your multilingual documentation site will be running at `http://localhost:3000` ## 🌍 Languages This starter comes pre-configured with: - 🇺🇸 **English** (`en`) - Default language - 🇫🇷 **Français** (`fr`) - French translation ## 📁 Project Structure ```text my-docs/ ├── content/ # Your markdown content │ ├── en/ # English content │ │ ├── index.md # English homepage │ │ └── docs/ # English documentation │ └── fr/ # French content │ ├── index.md # French homepage │ └── docs/ # French documentation ├── public/ # Static assets ├── nuxt.config.ts # Nuxt configuration with i18n setup └── package.json # Dependencies and scripts ``` ### Content Structure The content is organized by language, making it easy to manage translations: ```text content/ ├── en/ # English content │ ├── index.md │ ├── 1.getting-started/ │ │ ├── installation.md │ │ └── configuration.md │ └── 2.essentials/ │ ├── markdown.md │ └── components.md └── fr/ # French content ├── index.md ├── 1.getting-started/ │ ├── installation.md │ └── configuration.md └── 2.essentials/ ├── markdown.md └── components.md ``` ## 🔗 URL Structure The i18n starter generates URLs with language prefixes: - English: `/en/getting-started/installation` - French: `/fr/getting-started/installation` - Default locale fallback: `/getting-started/installation` (redirects to English) ## ⚡ Built with This starter comes pre-configured with: - [Nuxt 4](https://nuxt.com){rel=""nofollow""} - The web framework - [Nuxt Content](https://content.nuxt.com/){rel=""nofollow""} - File-based CMS - [Nuxt i18n](https://i18n.nuxt.com/){rel=""nofollow""} - Internationalization - [Nuxt UI](https://ui.nuxt.com){rel=""nofollow""} - Premium UI components - [Nuxt Image](https://image.nuxt.com/){rel=""nofollow""} - Optimized images - [Tailwind CSS 4](https://tailwindcss.com/){rel=""nofollow""} - Utility-first CSS - [Docus Layer](https://www.npmjs.com/package/docus){rel=""nofollow""} - Documentation theme ## 📖 Documentation For detailed documentation on customizing your Docus project, visit the [Docus Documentation](https://docus.dev){rel=""nofollow""} ## 🚀 Deployment Build for production: ```bash npm run build ``` The built files will be in the `.output` directory, ready for deployment to any hosting provider that supports Node.js. ## 📄 License [MIT License](https://opensource.org/licenses/MIT){rel=""nofollow""} #right :::template-features --- features: - label: Nuxt 4 content: The web framework - label: Nuxt I18n content: Internationalization support. - label: Nuxt UI content: Offers a very large set of full customizable components. - label: TypeScript content: A fully typed development experience. - label: Nuxt Studio content: Supported by Nuxt Studio for fast updates and previews. - label: Search content: A full-text search modal empowered by Fuse.js. - label: Nuxt Image content: A powerful image component. - label: Nuxt Content content: A powerful content component. --- ::: :: # Landing ::template-core [Nuxt UI](https://ui.nuxt.com){rel=""nofollow""} is a collection of premium components designed to facilitate the creation of appealing and responsive Nuxt applications in a matter of minutes. The Nuxt UI team is dedicated to deliver the best integration and customization experience, while the Studio team is providing full compatibility with Nuxt Studio module. - **Fully customizable**: change the style of your UI from your App Config or edit your landing page content from the `index.yml` file. - **Beautiful Typography styles**: Tailwind CSS typography plugin is pre-configured and styled to match Nuxt UI components and colors. - **Responsive by design**: Nuxt UI components aims to structure your content, they are responsive by design and will adapt to any screen size. #right :::template-features --- features: - label: Nuxt 3 content: Powered by Nuxt 3 for optimal performances and SEO. - label: Content v3 content: Write your content in yaml files or use Markdown with the MDC syntax. - label: Nuxt UI v3 content: Offers a very large set of full customizable components. - label: TypeScript content: A fully typed development experience. - label: Nuxt Studio content: Supported by Nuxt Studio for fast updates and previews. --- ::: :: # Minimal Starter ::template-core Unleash your imagination with our minimalist starter: - Start with a clean slate and craft the application of your dreams. - Nuxt Content all setup. - Effortlessly compose pages using Markdown and Vue components, enhanced by the intuitive MDC syntax. #right :::template-features --- features: - label: Nuxt Architecture content: Harness the full power of Nuxt 3 and its modules ecosystem. - label: Nuxt Studio ready content: Edit your content with live-preview with Nuxt Studio module. - label: Vue Components content: Use built-in components (or your own) inside your content. - label: Write Markdown content: Enjoy the ease and simplicity of Markdown and discover MDC syntax. - label: Deploy anywhere content: In one click from Studio or with zero config on Vercel or Netlify. Choose between static generation, on-demand rendering (Node) or edge-side rendering on CloudFlare workers. --- ::: :: # Minted Directory ::template-core Minted Directory is a highly customizable template designed for building successful directory websites quickly. - Create a directory website with a customized style/brand - Manage listings with markdown - SEO optimized - Search and Categorization with tags - Blog Pages #right :::template-features --- features: - label: Nuxt Architecture content: Harness the full power of Nuxt 3 and its modules ecosystem. - label: Nuxt Studio ready content: Edit your content with live-preview within Nuxt Studio module. - label: Vue Components content: Insert built-in components (or your own) inside your content. - label: Write Markdown content: Enjoy the ease and simplicity of Markdown and discover MDC syntax. - label: Deploy anywhere content: In one click from Studio or with zero config on Vercel or Netlify. Choose between static generation, on-demand rendering (Node) or edge-side rendering on CloudFlare workers. - label: Extensible content: Customize the whole design, or add components using slots - you can make Minted Directory your own. --- ::: :: # Portfolio ::template-core [Nuxt UI](https://ui.nuxt.com){rel=""nofollow""} is a collection of premium components designed to facilitate the creation of appealing and responsive Nuxt applications in a matter of minutes. The Nuxt UI team is dedicated to deliver the best integration and customization experience, while the Studio team is providing full compatibility with Nuxt Studio. - **Fully customizable**: change the style of any component from your App Config or customize them specifically through the ui prop. - **Write Markdown with ease**: Nuxt UI overrides Nuxt Content prose components to make them awesome but also adds new ones like Callout, CodeGroup, Field, etc. - **Beautiful Typography styles**: Tailwind CSS typography plugin is pre-configured and styled to match Nuxt UI components and colors. - **Full-Text Search out of the box**: Nuxt UI ships with a ready to use command palette component. No need to setup Algolia DocSearch anymore. - **Slots for everything**: Each component leverages the power of Vue's slots to give you the flexibility to build anything. - **Responsive by design**: Nuxt UI components aims to structure your content, they are responsive by design and will adapt to any screen size. #right :::template-features --- features: - label: Nuxt 3 content: Powered by Nuxt 3 for optimal performances and SEO. - label: Markdown content: Write your pages with MDC thanks to Nuxt Content. - label: Nuxt UI content: Offers a very large set of full customizable components. - label: TypeScript content: A fully typed development experience. - label: Nuxt Studio content: Use Nuxt Studio module for fast updates and previews. --- ::: :: # Saas ::template-core [Nuxt UI](https://ui.nuxt.com){rel=""nofollow""} is a collection of premium components designed to facilitate the creation of appealing and responsive Nuxt applications in a matter of minutes. The Nuxt UI team is dedicated to deliver the best integration and customization experience, while the Studio team is providing full compatibility with Nuxt Studio. - **Fully customizable**: change the style of any component from your App Config or customize them specifically through the ui prop. - **Write Markdown with ease**: Nuxt UI overrides Nuxt Content prose components to make them awesome but also adds new ones like Callout, CodeGroup, Field, etc. - **Beautiful Typography styles**: Tailwind CSS typography plugin is pre-configured and styled to match Nuxt UI components and colors. - **Full-Text Search out of the box**: Nuxt UI ships with a ready to use command palette component. No need to setup Algolia DocSearch anymore. - **Slots for everything**: Each component leverages the power of Vue's slots to give you the flexibility to build anything. - **Responsive by design**: Nuxt UI components aims to structure your content, they are responsive by design and will adapt to any screen size. #right :::template-features --- features: - label: Nuxt 3 content: Powered by Nuxt 3 for optimal performances and SEO. - label: Markdown content: Write your pages with MDC thanks to Nuxt Content. - label: Nuxt UI content: Offers a very large set of full customizable components. - label: TypeScript content: A fully typed development experience. - label: Nuxt Studio content: Use Nuxt Studio module for fast updates and previews. - label: Search content: A full-text search modal empowered by Fuse.js. --- ::: :: # Meet the AI Assistant for Docus Documentation is only as valuable as the answers users can extract from it. We're introducing the Docus AI Assistant, a native, embedded chat experience that transforms how developers interact with your documentation. The AI Assistant searches your content and generates code examples users can copy directly. The best part? **It activates with a single environment variable.** :video{.w-full.h-auto.rounded-md autoplay controls loop muted playsinline src="https://res.cloudinary.com/nuxt/video/upload/v1770204403/studio/docus-assistant_e8xmxu.mp4"} ## The Documentation Discovery Problem Users arrive at your docs with questions, not keywords. They scroll through navigation, guess search terms, and scan pages hoping to find what they need. Even well-organized documentation creates friction between the question in their mind and the answer on the page. AI changes this dynamic. Instead of adapting their question to your navigation structure, users ask naturally and receive answers grounded in your actual documentation. ## Activation With One Environment Variable Docus abstracts the AI setup complexity and enables your own AI assistant chat with one environment variable. ```bash [.env] AI_GATEWAY_API_KEY=your-api-key ``` ::note A **Vercel AI Gateway API key** is required unless you deploy to Vercel, in which case no API key or configuration is needed. Test it for free with $5 credits offer! :: That's it. Deploy your docs and the AI Assistant activates automatically. No configuration files, no API setup, no infrastructure changes. The assistant works through [Vercel AI Gateway](https://vercel.com/docs/ai-gateway){rel=""nofollow""}, supporting OpenAI, Anthropic, Google, and other providers. Choose your preferred model and budget, the integration remains the same. ::prose-tip{to="https://docus.dev/en/ai/assistant#quick-start"} Read the full setup guide in the Docus documentation. :: ## How It Works ### MCP Integration The AI Assistant leverages the **Model Context Protocol (MCP)** to give AI models direct access to your documentation. Here's the simple architecture: 1. Docus automatically exposes an **MCP server** at `/mcp` that provides tools to search and retrieve your documentation 2. When you provide an `AI_GATEWAY_API_KEY`, AI models connect to your MCP server 3. The AI uses MCP tools to search your docs in real-time and provide accurate answers This approach prevents hallucination by grounding every response in actual content. The AI can only answer based on what's in your documentation. The MCP protocol is an open standard that allows AI models to interact with external data sources through well-defined tools. Docus implements this automatically, you just provide the API key. ### AI Customization The real power of the MCP integration is **extensibility**. Docus uses `@nuxtjs/mcp-toolkit` under the hood. It allows to add custom tools that expand what the AI assistant can do beyond just searching documentation. Want your AI to check API status, fetch live data, run code examples, or interact with your own services? Add custom MCP tools in your project: ```typescript [server/mcp/tools/check-api-status.ts] export default defineMcpTool({ description: 'Check the current status of the API', inputSchema: z.object({ endpoint: z.string().describe('API endpoint to check') }), handler: async ({ endpoint }) => { const status = await checkEndpointStatus(endpoint) return { content: [{ type: 'text', text: `API endpoint ${endpoint} is ${status}` }] } } }) ``` The AI assistant automatically discovers and uses your custom tools. Users can now ask **"Is the API down?"** and get real-time answers, not just documentation. ::note You can override different aspects of your AI assistant with: - **Custom Tools**: add any capability in `server/mcp/tools/` using `defineMcpTool` - **Resources**: expose files or data through `server/mcp/resources/` - **Prompts**: create reusable prompt templates in `server/mcp/prompts/` - **Custom Handlers**: build separate MCP endpoints for specialized use cases :: ::prose-tip{to="https://docus.dev/ai/mcp#customization"} Learn more about MCP customization in the Docus documentation. :: ### Questions Configuration Users can ask questions without leaving the page they're reading. The assistant understands the current context and can reference the page you're viewing. Configuration is optional but powerful. Pre-populate common questions, adjust UI visibility, customize keyboard shortcuts, or disable features you don't need in the `app.config.ts` file. ```typescript [app.config.ts] export default defineAppConfig({ docus: { ai: { floatingInput: true, explainWithAI: true, faqs: [ { question: 'How do I install Docus?', category: 'Getting Started' }, { question: 'Can I customize the theme?', category: 'Customization' } ] } } }) ``` ### Internationalization The AI Assistant automatically adapts to your documentation's language settings. All UI text translates based on user locale, and the assistant responds in the user's language. If your docs support multiple languages, the assistant works across all of them without additional configuration. The same environment variable enables AI for every locale. ## Key Benefits ### Built for Developers The AI Assistant integrates with how developers browse documentation: - **Floating Input** (`Cmd/Ctrl+I`): Bottom-of-screen chat accessible via keyboard shortcut - **Explain with AI Button**: Sidebar button that opens the assistant with current page context - **Slideover Panel**: Persistent conversation history for continued interaction ### Code Generation Beyond answering questions, the assistant generates code examples based on your documentation patterns. Users can copy implementations directly from the chat without hunting through example repositories. The assistant understands your API structure, knows your conventions, and generates examples that match your documentation style. ### Privacy and Control Your documentation content stays within your control. The AI Assistant queries your published docs, the same content publicly available on your site. No separate indexing, no data collection, no external databases. You control the AI provider through Vercel AI Gateway, allowing you to choose models based on privacy requirements, latency needs, or cost constraints. Switch providers without changing your documentation code. ## Get Started Today ::prose-tip **New to Docus?** Create a complete documentation site with AI assistant built-in: ```bash npx skills add nuxt-content/docus ``` Then run `/create-docs` in your AI agent (Claude, Cursor, or any agent supporting skills) to generate everything automatically. :: Already have a Docus site? Add the AI Assistant with one environment variable: ```bash [.env] AI_GATEWAY_API_KEY=your-api-key ``` Deploy and the assistant activates automatically. The AI Assistant represents a shift in how developers interact with documentation. Instead of searching for answers, they have a conversation. Instead of parsing examples, they generate code. ::prose-tip{to="https://docus.dev/ai/assistant"} Read the complete AI Assistant documentation. :: ## What's Next We're constantly exploring new ways to make documentation more interactive and helpful. The AI Assistant is just the beginning of what's possible when you combine great documentation with intelligent tooling. Documentation is evolving from static reference material into an interactive learning environment, and we're excited to see where this journey takes us. # Docus, the Comeback We’ve completely rewritten the [Docus](https://docus.dev){rel=""nofollow""} theme. Reviving it with a fresh and modern foundation powered by the Nuxt ecosystem and designed by Nuxt UI to offer the best documentation experience. The goal was simple: take **the best parts of the Nuxt ecosystem** and deliver a documentation theme that’s powerful, elegant and easy to maintain. ## **What’s New in Docus v3?** ### **📦 A real** [Nuxt]{.text-primary} **app with just one dependency** Docus is built on top of [Nuxt 3](https://nuxt.com){rel=""nofollow""} (version 4 compatibility mode is enabled so we're already ready for Nuxt 4). That means your documentation is a full Nuxt application with access to the entire Nuxt features: components, modules, plugins, runtime config, and more. **But**, **the best part is**... You only need the **docus** package. It bundles all the necessary officials Nuxt modules, so you can start writing documentation in seconds. All you need in your app is a `package.json` file and a `content/` folder with your Markdown in it. Then you’re good to go. ::prose-tip{to="https://docus.dev/concepts/nuxt"} Learn more about Nuxt layer in Docus dedicated section. :: ### **✨ Designed by** [Nuxt]{.text-primary} **UI Pro** Docus v2 is powered by **Nuxt UI Pro**, giving you a beautiful, responsive, and accessible theme out of the box. With **Tailwind CSS v4**, **CSS variables**, and the **Tailwind Variants API**, your docs look great by default but stays fully customizable. You can tweak colors, update typography or adjust component styles globally or per component with simple updates in your `app.config.ts`. ::prose-tip{to="https://docus.dev/concepts/theme"} Learn more about UI theming in Docus dedicated section. :: ::prose-note A UI Pro license is currently required, but we’re working to make it free for everyone soon. Also, if you're currently building an OSS documentation, you can ask for the OSS license at `ui-pro@nuxt.com` . :: ### **✍️ Markdown with superpowers (MDC syntax by** [Nuxt]{.text-primary} **Content)** Writing docs has never been more simple. You're one Markdown folder away from it. Furthermore with Nuxt Content and the MDC syntax, you can embed interactive Vue components in Markdown and use any Nuxt UI components or your own custom ones. ::prose-tip{to="https://docus.dev/concepts/edition"} Learn more about MDC syntax in Docus dedicated section. :: ### 🖥️ [Nuxt]{.text-primary} Studio ready Docus works perfectly with **Nuxt Studio**, allowing you to manage and edit your docs entirely from the browser. No terminal, no local setup. It’s the ideal way to collaborate with non-technical contributors or manage docs centrally for your team. ::prose-tip{to="https://docus.dev/getting-started/studio"} Learn more about Studio editor in Docus dedicated section. :: ### **🔍 SEO out of the box** Technical SEO is tricky and boring. Docus offers a solid, opt-in default setup that works out of the box while giving you full control to customize your SEO metadata, from pages metas to social sharing images. ::prose-tip{to="https://docus.dev/concepts/configuration"} Learn more about app configuration in Docus dedicated section. :: ### **🔧 Full customization via component overrides** Need to replace parts of the layout or UI? Docus uses **Nuxt Layers** to let you override core components we've defined. Just create a new component in your project’s `components/` directory using the same name, and Docus will automatically use it. ::prose-tip{to="https://docus.dev/concepts/customization"} Learn more about components override in Docus dedicated section. :: ### **🤖** LLMs integration by default Docus integrates `nuxt-llms` by default to prepare your content for Large Language Models (LLMs). All your documentation pages are injected and `/llms.txt` file is automatically generated and pre-rendered. ::prose-tip{to="https://docus.dev/concepts/llms"} Learn more about LLMs integration in Docus dedicated section. :: ### **🧠 Smart defaults for a ready docs** Docus includes thoughtful defaults that save you time: - ✅ Auto-generated sidebar navigation from your folder structure - 🔍 Full-text search using Fuse.js - ✨ Optimized typography and layout - 🌙 Dark mode support out of the box - 🖼️ Nuxt Image integration for responsive, optimized images ### **🔁** Easy migration Moving from any Markdown-based is straightforward: drop your `.md` files into your `content/` folder and you’re live. ## **What’s Next?** ### **🔧 Try Docus Today** ```bash npx docus init docs ``` That's it 🚀 You're ready to edit your `content/` folder and start writing your doc. ::prose-tip{to="https://docus.dev"} Visit the documentation to learn everything about Docus. :: ### **🤝 Contribute** We’ve moved the repository to the **NuxtLabs** GitHub organization and cleaned up the issue tracker to start fresh. Whether you’re fixing bugs, suggesting features, or writing docs, we’d love your help. Feedback, contributions, and discussions about the future of Docus are all welcome! # Nuxt Studio Alpha Release When NuxtLabs joined Vercel, we promised to transform [nuxt.studio](https://nuxt.studio){rel=""nofollow""} from a hosted platform into a free, open-source module. Today, we're excited to announce the **first alpha release** of the Nuxt Studio module. ::u-button --- color: neutral icon: i-simple-icons-github target: _blank to: https://github.com/nuxt-content/nuxt-studio variant: subtle --- Discover the Nuxt Studio module on GitHub. :: You can now enable content editing directly in production, with real-time preview and GitHub integration, all from within your own Nuxt application. :video{controls loop src="https://res.cloudinary.com/nuxt/video/upload/v1733494722/contentv3final_rc8bvu.mp4"} ::u-button --- external: true class: mt-4 color: neutral icon: i-lucide-mouse-pointer-click to: https://content.nuxt.com/admin?redirect=/blog/studio-module-alpha --- Try editing this page :: ## 🏠 From Hosted Platform to Self-Hosted Module This milestone wouldn't have been possible without Vercel's support. Their backing allowed us to dedicate the resources needed to rebuild Studio as an open-source module. ### What's Different? Originally provided as a hosted platform at [nuxt.studio](https://nuxt.studio){rel=""nofollow""}, Studio is now a free and open-source Nuxt module that you can deploy alongside your Nuxt Content website. This means content editors can manage and update content directly in production, on their website, without the need of local development tools or Git knowledge. - **Self-hosted** — runs entirely on your infrastructure alongside your Nuxt app - **No external dependencies** — no APIs or third-party services required - **Free and open-source** — released under the MIT license - **Direct integration** — a simple GitHub OAuth app is needed to get started The only trade-off is that Studio now requires a server-side route for authentication. While static generation remains supported with [Nuxt hybrid rendering](https://nuxt.com/docs/4.x/guide/concepts/rendering#hybrid-rendering){rel=""nofollow""}, your site must be deployed on a platform that supports SSR. ## 📦 What's Shipped in Alpha The alpha release focuses on **core infrastructure and stability** without risking any bugs introduced by the Visual editor. We're using Monaco editor to ensure all file operations and GitHub workflows are rock-solid before introducing visual editing. **Monaco Code Editor** → IDE editing experience with syntax highlighting for Markdown, YAML, and JSON, including full MDC syntax support and split-screen diff viewer for conflicts. **File Operations** → Complete CRUD operations for your `content/` directory. Create, edit, delete, rename, and move files with built-in draft management. **Media Management** → Centralized library for assets in your `public/` directory with upload, organize, preview, and integrate capabilities. **Git Integration** → Direct commits to GitHub via OAuth with conflict detection, author attribution, and custom commit messages. **Real-time Preview** → Live preview of draft changes on your production website with instant updates and side-by-side editing. ## 🗺️ The Road Ahead ### Beta Release `Q4 2025` Inspired from what we've built on [nuxt.studio](https://nuxt.studio){rel=""nofollow""}, the beta phase will introduce the open-source visual editor, making Studio accessible to non-technical users: - **Markdown Editor** — Notion-inspired experience for Markdown - **Form-based Editing** — Schema-based forms for Markdown frontmatter, YAML, and JSON files - **Vue Component Edition** — Visual interface for editing component props and slots - **Google OAuth** — Alternative authentication for non-GitHub users ### Stable Release `End of Year 2025` Production-ready features, performance optimizations, and enhanced stability. ::warning At the end of year, the hosted platform will be sunset and the module will be the only way to edit your Nuxt Content website. :: ### Beyond `2026` AI-powered content suggestions, multiple git providers, and community-driven features. ## 🗄️ Storage Architecture Studio uses a three-tier storage architecture to keep content synchronized between your browser and GitHub. ### Production Database `SQLite WASM` When your Nuxt Content website loads, Nuxt Content v3 downloads a SQLite database dump from your server and initializes a local WASM database containing all content from your deployed branch. This database stays in sync with GitHub as long as your last deployment completed successfully. This is the production database updated by Studio when you edit content. ### Draft Storage `IndexedDB` Studio maintains a separate draft layer using [unstorage](https://unstorage.unjs.io/){rel=""nofollow""} backed by IndexedDB. When you edit content, changes are stored as drafts locally in your browser. Each time Studio loads, these drafts are merged with the SQLite database to render a drafted version of your production site. ::note Drafts are stored only in your browser. They're not shared between editors or devices. :: ### GitHub Repository `API Integration` When you publish, Studio commits your draft changes directly to GitHub through the GitHub API. Your CI/CD pipeline then rebuilds and redeploys your site automatically. After deployment, you'll need to refresh to update your browser database with the latest content. ## 🔄 The Sync Flow ### Initial Load ::prose-steps{level="4"} #### Database Initialization Nuxt Content downloads the SQLite database dump generated during the build process. :br This file contains all parsed content from your `content/` directory. #### Draft Recovery Studio checks IndexedDB for any existing drafts from previous sessions and loads them into the SQLite database. #### Preview Studio refreshes the site preview so you can view your latest drafts and edits directly on your production website. :: ### Editing Content ::prose-steps{level="4"} #### Draft Modification Changes are saved immediately in IndexedDB as draft items with a status of `created`, `modified`, or `deleted`. #### Database Update The local SQLite database is updated to include your draft content, allowing instant visual preview. #### Conflict Detection Studio compares your draft content against the latest version on GitHub to detect possible conflicts. :::note **Conflicts can occur when:** :br - Someone pushes a commit that modifies the same file and its version is currently building. - A deployment fails or hasn’t completed, leaving the production out of date and unsync with GitHub. ::: :: ### Publishing Changes ::prose-steps{level="4"} #### Draft Collection Studio gathers all draft items that contain changes. #### GitHub Commit Using the GitHub API, Studio creates a new commit with all updated files. #### Deployment Trigger Your CI/CD platform detects the commit and automatically rebuilds and redeploys your website. #### Deployment Wait After publication, Studio clears the local drafts and waits for the deployment to complete. :br During this time, a loading state is shown while the production SQLite database catches up with your latest commit. :::warning Until your commit is deployed, Studio remains in a pending state where the production database is not yet up to date. ::: :: ## 🚀 Get Started Today Install the module and configure your GitHub OAuth app to start editing content in production: ```bash npx nuxi module add nuxt-studio@alpha ``` Check out the [setup guide](https://content.nuxt.com/docs/studio/setup) for complete installation and configuration instructions. --- We're excited to see what you build with Nuxt Studio. Join the conversation on [GitHub Discussions](https://github.com/nuxt-content/nuxt-studio/discussions){rel=""nofollow""} or [join our Discord](https://discord.gg/sBXDm6e8SP){rel=""nofollow""} to help shape the future of the module. # Nuxt Studio is Now Free and Open Source **Nuxt Studio is dead, long live Nuxt Studio.** We promised to deliver by the end of the 2025 year and today we're keeping that promise: we're officially releasing the first stable version of Nuxt Studio as a **free, open-source Nuxt module**. At the same time, we're sunsetting the legacy [nuxt.studio](https://nuxt.studio){rel=""nofollow""} platform. It now becomes the new official documentation. ::u-button --- color: neutral icon: i-simple-icons-github target: _blank to: https://github.com/nuxt-content/nuxt-studio variant: outline --- Discover the Nuxt Studio module on GitHub. :: ## 🌄 Why We're Sunsetting [nuxt.studio](https://nuxt.studio){rel=""nofollow""} When NuxtLabs joined Vercel, we promised to make our premium products free and open source.We're following the same approach already taken with [Nuxt UI](https://ui.nuxt.com){rel=""nofollow""} and soon applied to [NuxtHub](https://hub.nuxt.com){rel=""nofollow""}. For us, this means everything. It's the opportunity to focus entirely on building tools that are **free, open source, and accessible to everyone**. This is why Studio platform will be discontinued. ## 🚀 Meet the New Studio Module We rebuilt Studio from the ground up as a Nuxt module. The result is a fully self-hosted content management solution that runs alongside your Nuxt Content website. ### What's Different? - **Self-hosted** — runs entirely on your infrastructure alongside your Nuxt app - **Free and open-source** — released under the MIT license - **Dev integration** — works also in development mode ## 📦 Features This stable release includes everything you need to edit content in production: ### TipTap Visual Editor The modern Notion-like editing experience for Markdown content is back with a improved version, powered by [TipTap](https://tiptap.dev/){rel=""nofollow""} integrated through the [Nuxt UI Editor](https://ui.nuxt.com/pro/components/editor){rel=""nofollow""} component: - Rich text editing with headings, formatting, links, and more - MDC component support for inserting Vue components - Vue component props editor for visual property editing - Drag & drop for reordering content blocks - Slash commands for quick formatting access - Real-time conversion between visual content and MDC syntax ### Form-Based Editor Schema-based forms automatically generated from your [collection definitions](https://content.nuxt.com/docs/collections/define): - Automatic form generation for frontmatter, YAML, and JSON files - Custom inputs for media and icon selection - Native type mapping (string → text, boolean → toggle, enum → select) - Array support and object support ### File Operations Complete CRUD operations for your `content/` directory: create, edit, delete, rename, and move files with built-in draft management. ### Media Management Centralized media library for assets in your `public/` directory with upload, organize and preview. ### Git Integration Direct commits to GitHub or GitLab with conflict detection, author attribution, and custom commit messages. ### Real-time Preview Live preview of draft changes on your production website with instant updates and side-by-side editing. ### Multi-Language Support The Studio interface is available in 17 languages including English, French, German, Spanish, Japanese, Chinese, and more. ### Authentication Options Multiple authentication providers: GitHub OAuth, GitLab OAuth, Google OAuth, or custom authentication with your own flow. ## 📦 Quick Start Install the module using the Nuxt CLI: ```bash [Terminal] npx nuxt module add nuxt-studio ``` Start editing in local or configure your repository for production: ```ts [nuxt.config.ts] export default defineNuxtConfig({ studio: { repository: { provider: 'github', owner: 'your-username', repo: 'your-repo', branch: 'main' } } }) ``` ::tip{to="https://nuxt.studio/setup"} Follow the complete setup guide for detailed installation instructions. :: ## 📅 Sunset Timeline ::prose-steps ### Now You can already migrate to the new module. All existing subscription have been canceled. ### 2026 The legacy nuxt.studio platform becomes the new official documentation and we'll keep improving this module day after day. :: ::note The [nuxt.studio](http://nuxt.studio){rel=""nofollow""} platform has always been just an editing layer. Your content lives in your Git repository and you remain in full control. The sunset of the platform will have zero impact on your deployed website or its behaviour. :: ## 🔄 Migration Guide Migration is extremely simple: 1. **Install the module**: Follow the [setup documentation](https://nuxt.studio/setup){rel=""nofollow""} 2. **Configure authentication**: Set up [GitHub](https://nuxt.studio/git-providers#github){rel=""nofollow""}, [GitLab](https://nuxt.studio/git-providers#gitlab){rel=""nofollow""}, or [Google OAuth](https://nuxt.studio/auth-providers#google){rel=""nofollow""} 3. **Remove legacy code**: Upcoming versions of Nuxt Content will automatically remove all legacy Studio code, but you can already remove the `preview` key in your Nuxt Content configuration. ## 🗺 What's Next We're committed to making the open-source module an even better experience. Here's what's coming in 2026: - **AI-powered content generation** — intelligent content suggestions and assistance - **TipTap extension exposal** — we'll expose the TipTap extensions we've built (related to MDC syntax) so you can use it with the [Nuxt UI Editor](https://ui.nuxt.com/docs/components/editor){rel=""nofollow""}. - **Community-driven features** — shaped by your feedback ## 🙏 Thank You Your feedback shaped both the old and new Studio. Your support made this transition possible. Thanks to Vercel for making this happen. Their pushing towards open source. We're excited to see what you build with the new Nuxt Studio module. Join the conversation on [GitHub Discussions](https://github.com/nuxt-content/nuxt-studio/discussions){rel=""nofollow""} or [join our Discord](https://discord.gg/sBXDm6e8SP){rel=""nofollow""} to help shape the future of content editing. --- If you need help migrating, reach out on our [Discord server](https://discord.gg/sBXDm6e8SP){rel=""nofollow""}. # Introducing Nuxt Studio v2 ::warning This article was published before the merge of the [Content](https://github.com/nuxt/content){rel=""nofollow""} and [Studio](https://github.com/nuxtlabs/studio-module){rel=""nofollow""} modules on January 6, 2025. As a result, it may contain some inconsistencies. The Studio module is now deprecated and available as an opt-in feature of the Content module. Learn how to enable it in [this guide](https://content.nuxt.com/docs/getting-started) . :: We are excited to announce the release of Nuxt Studio v2, a major update bringing a brand new interface designed specifically for our users, based on their feedback. ::tip Studio is optimized for **Nuxt Content** project but the only real requirement is to have a *content* folder with Markdown files. This simple setup is enough to start editing and publishing your files with the platform. :: ## **A more intuitive interface** ![Nuxt studio v2 interface](https://content.nuxt.com/blog/v2-interface.webp) The main improvement in Version 2 is a **complete rework of the interface**. We have designed it to be more intuitive and user-friendly, especially for non-technical users. Our goal was to simplify the user experience, making it easier to create and set up projects with minimal hassle. The new interface is light, straightforward, and designed to streamline your workflow. ## **Google authentication** ![Google and GitHub authentication](https://content.nuxt.com/blog/google-github.webp) We now have two different authentication methods. You can either login with **GitHub** or with **Google**. Both methods give you the same edition rights but since Studio is synchronized with GitHub, some features are specific to GitHub users, especially project creation. ::warning Since a Google user can not create a project, he has to **join a team** with existing projects to edit them. :: ## **Minimal setup to edit your files** You can now edit your content **without any setup**, just import your repository and this is it. You can navigate through your files and medias, edit your content and publish on GitHub. Collaboration is available for teams. ![Notion-like editor with collaboration](https://content.nuxt.com/blog/collaborate.webp) ::warning Medias in the editor are not displayed until you set up the live preview (see section below). :: ## Simplified setup for live preview ![preview enable between notion like editor and website](https://content.nuxt.com/blog/preview.webp) As the live preview feature requires a deployed URL, we made it as simple as possible to set it up. While GitHub pages deployment remains available and still does not require any configuration on your end, requirements have been simplified for self-hosted project as we removed the token verification. [Enabling the Studio module](https://nuxt.studio/docs/get-started/setup#enable-the-live-preview){rel=""nofollow""} is the **only remaining requirement.** ::warning{to="https://github.com/nuxtlabs/studio-module"} It's crucial to use the latest version of the **Studio module** to ensure compatibility and access to new features. :: ## New documentation With a revamped platform comes a [new documentation](https://nuxt.studio/docs/get-started/introduction){rel=""nofollow""}. Don't hesitate to check it out to learn everything about the new Studio. Whether you are an [editor](https://nuxt.studio/docs/editors/introduction){rel=""nofollow""} or a [developer](https://nuxt.studio/docs/developers/introduction){rel=""nofollow""} you now have your dedicated section in the docs. ## A new direction for Studio Most available CMS solutions have to choose between being very customizable for developers or highly user friendly for content editors, with Studio we want to do both. **The developer provides the tools for the editors to focus on content, without requiring any technical knowledge**. ::tip Our Notion-like editor has a bright future ahead, and we want to develop it collaboratively with the community. :: ## # How to upgrade your Nuxt documentation website to Content x UI v3 **2025 kicks off with the power of 3!** This start of year is marked by major updates to our favorite tools. The UI team is about to launch **version 3** of the **UI / UI Pro libraries** (currently in alpha), while the Content team has already released **Nuxt Content v3**. These updates mean that all our starter templates combining **Content** and **UI** will need to be updated to align with the latest versions. To help you make the transition, this guide walks through migrating the **Nuxt UI Pro Docs Starter** to the new **Content v3 and Nuxt UI v3** packages. ::prose-tip{to="https://github.com/nuxt-ui-pro/docs/tree/v3"} Check the UI Pro documentation starter repository source code. :: ## Content migration (v2 → v3) ### 1. Update package to v3 ::code-group ```bash [pnpm] pnpm add @nuxt/content@^3 ``` ```bash [yarn] yarn add @nuxt/content@^3 ``` ```bash [npm] npm install @nuxt/content@^3 ``` ```bash [bun] bun add @nuxt/content@^3 ``` :: ### 2. Create `content.config.ts` file This configuration file defines your data structure. A collection represents a set of related items. In the case of the docs starter, there are two different collections, the `landing` collection representing the home page and another `docs` collection for the documentation pages. ```js [content.config.ts] import { defineContentConfig, defineCollection, z } from '@nuxt/content' export default defineContentConfig({ collections: { landing: defineCollection({ type: 'page', source: 'index.yml' }), docs: defineCollection({ type: 'page', source: { include: '**', exclude: ['index.yml'] }, schema: z.object({ links: z.array(z.object({ label: z.string(), icon: z.string(), to: z.string(), target: z.string().optional() })).optional() }) }) } }) ``` On top of the built-in fields provided by the [`page`](https://content.nuxt.com/docs/collections/types#page-type) type, we added the extra field `links` to the `docs` collection so we can optionally display them in the docs [page header](https://ui3.nuxt.dev/components/page-header){rel=""nofollow""}. ::prose-tip The `type: page` means there is a 1-to-1 relationship between the content file and a page on your site. :: ### 3. Migrate `app.vue` ::prose-steps{level="4"} #### Navigation fetch can be updated by moving from `fetchContentNavigation` to `queryCollectionNavigation` method :::prose-code-group ```ts [app.vue (v3)] const { data: navigation } = await useAsyncData('navigation', () => queryCollectionNavigation('docs')) ``` ```ts [app.vue (v2)] const { data: navigation } = await useAsyncData('navigation', () => fetchContentNavigation()) ``` ::: #### Content search command palette data can use the new `queryCollectionSearchSections` method :::prose-code-group ```ts [app.vue (v3)] const { data: files } = useLazyAsyncData('search', () => queryCollectionSearchSections('docs'), { server: false, }) ``` ```ts [app.vue (v2)] const { data: files } = useLazyFetch('/api/search.json', { default: () => [], server: false }) ``` ::: :: ### 4. Migrate landing page ::prose-steps{level="4"} #### Home page data fetching can be updated by moving from `queryContent` to `queryCollection` method :::prose-code-group ```ts [index.vue (v3)] const { data: page } = await useAsyncData('index', () => queryCollection('landing').path('/').first()) ``` ```ts [index.vue (v2)] const { data: page } = await useAsyncData('index', () => queryContent('/').findOne()) ``` ::: #### `useSeoMeta` can be populated using the `seo` field provided by the [page](https://content.nuxt.com/docs/collections/types#page-type) type ```ts [index.vue] useSeoMeta({ title: page.value.seo.title, ogTitle: page.value.seo.title, description: page.value.seo.description, ogDescription: page.value.seo.description }) ``` :::prose-note Please note that the `seo` field is automatically overridden by the root `title` and `description` if not set. ::: :: ### 5. Migrate catch-all docs page ::prose-steps{level="4"} #### Docs page data and surround fetching can be updated and mutualised by moving from `queryContent` to `queryCollection` and `queryCollectionItemSurroundings` methods :::prose-code-group ```ts [docs/[...slug\\].vue (v3)] const { data } = await useAsyncData(route.path, () => Promise.all([ queryCollection('docs').path(route.path).first(), queryCollectionItemSurroundings('docs', route.path, { fields: ['title', 'description'], }), ]), { transform: ([page, surround]) => ({ page, surround }), }) const page = computed(() => data.value?.page) const surround = computed(() => data.value?.surround) ``` ```ts [docs/[...slug\\].vue (v2)] const { data: page } = await useAsyncData(route.path, () => queryContent(route.path).findOne()) const { data: surround } = await useAsyncData(`${route.path}-surround`, () => queryContent() .where({ _extension: 'md', navigation: { $ne: false } }) .only(['title', 'description', '_path']) .findSurround(withoutTrailingSlash(route.path)) ) ``` ::: #### Populate `useSeoMeta` with the `seo` field provided by the [page](https://content.nuxt.com/docs/collections/types#page-type) type ```ts [index.vue] useSeoMeta({ title: page.value.seo.title, ogTitle: `${page.value.seo.title} - ${seo?.siteName}`, description: page.value.seo.description, ogDescription: page.value.seo.description }) ``` :::prose-note Please note that the `seo` field is automatically overridden by the root `title` and `description` if not set. ::: :: ### 6. Update types Types have been significantly enhanced in Content v3, eliminating the need for most manual typings, as they are now directly provided by the Nuxt Content APIs. Concerning the documentation starter, the only typing needed concerns the navigation items where `NavItem` can be replaced by `ContentNavigationItem` . ```ts import type { ContentNavigationItem } from '@nuxt/content' const navigation = inject>('navigation') ``` ### 7. Replace folder metadata files All `_dir.yml` files become `.navigation.yml` ### 8. Migrate Studio activation Since the [studio module](https://nuxt.studio){rel=""nofollow""} has been deprecated and a new generic `Preview API` has been implemented directly into Nuxt Content, we can remove the `@nuxthq/studio` package from our dependencies and from the `nuxt.config.ts` modules. Instead we just need to enable the preview mode in the Nuxt configuration file by binding the Studio API. ```ts [nuxt.config.ts] export default defineNuxtConfig({ content: { preview: { api: 'https://api.nuxt.studio' } }, }) ``` ::prose-tip That's it, content v3 is now powering the starter. Let's now migrate to version 3 of [Nuxt UI / UI Pro](https://ui3.nuxt.dev){rel=""nofollow""} . :: ## Nuxt UI Pro Migration (v1 → v3) ::prose-caution This is a migration case, it won't cover all breaking changes introduced by the version upgrade. You should check each component you're using in the documentation to know if you need updates concerning props, slots or styles. :: ### 1. Setup package to v3 ::prose-note To maintain consistency with the UI versioning, which transitioned from v1 to v2. The Nuxt UI Pro version 2 is being skipped, and the update jumps directly to v3. :: ::prose-steps{level="4"} #### Install the Nuxt UI v3 alpha package :::code-group{sync="pm"} ```bash [pnpm] pnpm add @nuxt/ui-pro@next ``` ```bash [yarn] yarn add @nuxt/ui-pro@next ``` ```bash [npm] npm install @nuxt/ui-pro@next ``` ```bash [bun] bun add @nuxt/ui-pro@next ``` ::: #### Add the module in the Nuxt configuration file It's no longer required to add `@nuxt/ui` in modules as it is automatically imported by `@nuxt/ui-pro` . :::prose-code-group ```ts [nuxt.config.ts (v3)] export default defineNuxtConfig({ modules: ['@nuxt/ui-pro'] }) ``` ```ts [nuxt.config.ts (v1)] export default defineNuxtConfig({ extends: ['@nuxt/ui-pro'], modules: ['@nuxt/ui'] }) ``` ::: :::prose-note **Nuxt UIPro V3** is now considered as a module and no longer as a layer. ::: #### Import Tailwind CSS and Nuxt UI Pro in your CSS ```css [assets/css/main.css] @import "tailwindcss" theme(static); @import "@nuxt/ui-pro"; ``` ```ts [nuxt.config.ts] export default defineNuxtConfig({ modules: ['@nuxt/ui-pro'], css: ['~/assets/css/main.css'] }) ``` #### Remove tailwind config file and use CSS-first theming Nuxt UI v3 uses Tailwind CSS v4 that follows a CSS-first configuration approach. You can now customize your theme with CSS variables inside a `@theme` directive. - Delete the `tailwind.config.ts` file - Use the `@theme` directive to apply your theme in `main.css` file - Use the `@source` directive in order for Tailwind to detect classes in `markdown` files. ```css [assets/css/main.css] @import "tailwindcss" theme(static); @import "@nuxt/ui-pro"; @source "../content/**/*"; @theme { --font-sans: 'DM Sans', sans-serif; --color-green-50: #EFFDF5; --color-green-100: #D9FBE8; --color-green-200: #B3F5D1; --color-green-300: #75EDAE; --color-green-400: #00DC82; --color-green-500: #00C16A; --color-green-600: #00A155; --color-green-700: #007F45; --color-green-800: #016538; --color-green-900: #0A5331; --color-green-950: #052E16; } ``` :: ### 2. Update `ui` overloads in `app.config.ts` ::prose-caution{to="https://ui3.nuxt.dev/getting-started/theme#customize-theme"} All overloads using the `ui` props in a component or the `ui` key in the `app.config.ts` are obsolete and need to be checked in the **UI / UI Pro** documentation. :: ::prose-code-group ```ts [app.config.ts (v3)] export default defineAppConfig({ ui: { colors: { primary: 'green', neutral: 'slate' } }, uiPro: { footer: { slots: { root: 'border-t border-gray-200 dark:border-gray-800', left: 'text-sm text-gray-500 dark:text-gray-400' } } }, } ``` ```ts [app.config.ts (v1)] export default defineAppConfig({ ui: { primary: 'green', gray: 'slate', footer: { bottom: { left: 'text-sm text-gray-500 dark:text-gray-400', wrapper: 'border-t border-gray-200 dark:border-gray-800' } } }, }) ``` :: ### 3. Migrate `error.vue` page New `UError` component can be used as full page structure. ::prose-code-group ```vue [error.vue (v3)] ``` ```vue [error.vue (v1)] ``` :: ### 4. Migrate `app.vue` page - `Main`, `Footer` and `LazyUContentSearch` components do not need any updates in our case. - `Notification` component can be removed since `Toast` components are directly handled by the `App` component. - Instead of the `NavigationTree` component you can use the `NavigationMenu` component or the `ContentNavigation` component to display content navigation. ::prose-code-group ```vue [Header.vue (v3)] ``` ```vue [Header.vue (v1)] ``` :: ### 5. Update landing page We've decided to move the landing content from `YML` to `Markdown` . ::prose-tip This decision was made because components used in Markdown no longer need to be exposed globally (nor do they need to be created in the `components/content` folder). Content v3 handles it under the hood. :: ::prose-steps{level="4"} #### Update content configuration ```ts [content.config.ts] export default defineContentConfig({ collections: { landing: defineCollection({ type: 'page', source: 'index.md' }), docs: defineCollection({ type: 'page', source: { include: '**', exclude: ['index.md'] }, ... }) } }) ``` #### Use `ContentRenderer` to render `Markdown` :::prose-note `prose` property must be set to `false` in `ContentRendered` as we don't want `Mardown` to be applied with prose styling in the case of a landing page integrating non prose Vue components. ::: :::prose-code-group ```vue [index.vue (v3)] ``` ```vue [index.vue (v1)] ``` ::: #### Migrate Vue components to MDC Move all components in `index.md` following the [MDC syntax](https://content.nuxt.com/docs/files/markdown). Landing components have been reorganised and standardised as generic `Page` components. - `LandingHero` => `PageHero` - `LandingSection` => `PageSection` - `LandingCard` => `PageCard` (we'll use the `PageFeature` instead) :::prose-tip{to="https://github.com/nuxt-ui-pro/docs/blob/v3/content/index.md"} Have a look at the final `Markdown` result on GitHub. ::: :: ### 6. Migrate docs page ::prose-steps{level="4"} #### Layout - `Aside` component has been renamed to `PageAside` . - `ContentNavigation` component can be used (instead of `NavigationTree`) to display the content navigation returned by `queryCollectionNavigation`. :::prose-code-group ```vue [layout/docs.vue (v3)] ``` ```vue [layout/docs.vue (v1)] ``` ::: #### Catch-all pages - `Divider` has been renamed to `Separator` - `findPageHeadline` must be imported from `#ui-pro/utils/content` - `prose` property does not exist no more on `PageBody` component. :: ::prose-tip{to="https://github.com/nuxt-ui-pro/docs/tree/v3"} That's it! The docs starter is now fully running on both UI and Content v3 🎉 :: ## Edit on Studio If you're using Nuxt Studio to edit your documentation you also need to migrate the related code. The Studio module has been deprecated and a new generic `Preview API` has been implemented directly into Nuxt Content, you can remove the `@nuxthq/studio` package from your dependencies and from the`nuxt.config.ts` modules. Instead you just need to enable the preview mode in the Nuxt configuration file by binding the Studio API. ```ts [nuxt.config.ts] export default defineNuxtConfig({ content: { preview: { api: 'https://api.nuxt.studio' } }, }) ``` In order to keep the app config file updatable from Studio you need to update the helper import of the `nuxt.schema.ts` file from `@nuxthq/studio/theme` to `@nuxt/content/preview`. :video{autoplay controls loop poster="https://res.cloudinary.com/nuxt/video/upload/v1737458923/studio/docs-v3_lqfasl.png" src="https://res.cloudinary.com/nuxt/video/upload/v1737458923/studio/docs-v3_lqfasl.mp4"} # **Announcing Nuxt Content 3.0** We are thrilled to announce the first stable version of Nuxt Content 3.0.0 ✨ ## 🚀 Performance Improvements Nuxt Content v3 moves away from a file-based storage approach to an SQL database system. Using a database instead of the file-based storage reduces many I/O operations when querying large datasets. ::prose-note The new database system enhances the way your data files are stored and structured, ensuring better performance and scalability. This update is entirely behind the scenes and does not affect the file types you can use in Content ( `yml` , `json` , and `markdown` ). :: This switch is transparent to users and Nuxt Content still provides a zero config support for development mode, [server hosting](https://content.nuxt.com/docs/deploy/server) and [static generation](https://content.nuxt.com/docs/deploy/static). Furthermore, [serverless](https://content.nuxt.com/docs/deploy/serverless) hosting is now supported and client-side navigation performance has been improved. ### Serverless Compatibility A key challenge with Nuxt Content v2 was the large bundle size required to store all content files. It was an issue when deploying to serverless or edge platforms like [Netlify](https://netlify.com){rel=""nofollow""}, [NuxtHub](https://hub.nuxt.com){rel=""nofollow""} or [Vercel](https://vercel.com){rel=""nofollow""}. In serverless environments, each user request triggers a fresh instance of your Nuxt server, it starts from scratch each time. This "stateless" nature means you can't store data in server memory or use file-based databases like SQLite. That's why we've implemented database adaptors that persist data independently of your server instances. ::prose-note We're manually switching to the appropriate provider (Vercel / Postgres, NuxtHub / D1...) according to the [database type](https://cfec52f9.content-f0q.pages.dev/docs/getting-started/configuration#database){rel=""nofollow""} you've set in your config. :: ### WASM SQLite in Browser For client-side navigation, the module uses a similar approach. When the application executes the first content query, it downloads the generated dump from the server and initializes a local SQLite database within the browser. From that point onward, all queries are executed locally without needing to call the server: significantly improving the responsiveness of the application and providing a seamless user experience. ## 🗄️ Content Collections Collections are groups of related content items within your Nuxt Content project. They help organize and manage large datasets more efficiently. ### **Define Collections** You can now define collections in the [`content.config.ts`](https://content.nuxt.com/docs/getting-started/configuration) file to configure the database structure, utility types, and methods for finding, parsing, and querying content. ### **Collections Schema** Schemas enforce consistency within collections and improve TypeScript typings for better integration with Nuxt Content utilities. ```ts [content.config.ts] import { defineCollection, z } from '@nuxt/content' // Export collections export const collections = { // Define collection using `defineCollection` utility posts: defineCollection({ // Specify the type of content in this collection type: 'page', // Load every file matching this pattern source: 'blog/**/*.md', // Define custom schema for this collection schema: z.object({ date: z.date(), image: z.object({ src: z.string(), alt: z.string() }), badge: z.object({ label: z.string(), color: z.string() }) }) }), } ``` ::prose-tip{to="https://content.nuxt.com/docs/collections/define"} Learn more about collections in the documentation. :: ## 🔧 Simplified Vue Utils We simplified the utils to now expose: - [queryCollection](https://content.nuxt.com/docs/utils/query-collection) to fetch your collections with our powerful query builder - [queryCollectionNavigation](https://content.nuxt.com/docs/utils/query-collection-navigation) to fetch the generated navigation for a specific collection - [queryCollectionItemSurroundings](https://content.nuxt.com/docs/utils/query-collection-item-surroundings) to fetch sibling content for a specific path - [queryCollectionSearchSections](https://content.nuxt.com/docs/utils/query-collection-search-sections) to fetch searchable sections from a collection for enhanced content discovery These four utils allow your to efficiently fetch and query your content within your Vue pages and components: ```vue [pages/blog.vue] ``` ## 📦 Built-in Components We've updated the components to include only the essentials: - [ContentRenderer](https://content.nuxt.com/docs/components/content-renderer) to render the parsed Markdown to HTML & Vue components - [Slot](https://content.nuxt.com/docs/components/slot) replaced `ContentSlot` as we now support unwrapping using a directive, making your Vue components perfectly compatible to be used in both Vue & Markdown - [Prose Components](https://content.nuxt.com/docs/components/prose) are pre-designed components tailored for MDC syntax, with integrated styling for a good appearance Here's an example of displaying the content of a Markdown file: ```vue [pages/about.vue] ``` ## 🔷 TypeScript Integration The new collections system provides automatic TypeScript types for all your data. Every utility and API is strongly typed based on your collection definitions, ensuring robust type safety throughout development. ## ⬆️ Migrating from V2 Migration should be as easy as possible, this is why we wrote the [migration guide](https://content.nuxt.com/docs/getting-started/migration). ::prose-note Note that we've decided to remove the document-driven mode to simplify the module usage. :: ## 🖼️ Studio Integration [Nuxt Studio](https://content.nuxt.com/studio) is a platform to visually edit your **Nuxt Content** projects in production. With support for `Markdown`, `YAML`, or `JSON` files, our editor ensures versatility and ease of use. ### Preview API Previously an independent module, the [Studio module](https://github.com/nuxtlabs/studio-module){rel=""nofollow""} has been updated to be more generic and is now integrated directly into Nuxt Content as a `Preview API`. Enabling the preview functionality in Studio is easier than ever—simply configure the Studio API as your `Preview API` in your Nuxt Content settings: ```ts [nuxt.config.ts] export default defineNuxtConfig({ content: { preview: { api: 'https://api.nuxt.studio' } } }) ``` This simplification means no extra module is required for Studio, making setup faster. Furthermore, the Preview API is now generic, enabling other providers to deliver great editing experiences on top of Nuxt Content. ### **Unified Documentation** In addition to this integration, we’ve unified the **Content** and **Studio** documentation and websites into a single comprehensive resource. Only the Studio platform (available once the user is logged-in) remains as a [standalone site](https://nuxt.studio){rel=""nofollow""}. **We can now take advantage of data structures and collections in Studio**. The Studio platform supports and adapts its behaviour to **collections** and **user-defined schemas**. This enhancement will allow schema-generated forms for both `YAML` and `JSON` files as well as front-matter within Markdown files. # Behind the scenes of Nuxt Studio's visual editor ## **Introduction** Nuxt Studio offers a versatile workspace for both developers and content writers, giving them the freedom to choose between two distinct editors for content creation and management: the Markdown editor and the Visual editor. ![Select your favorit editor from the project settings](https://content.nuxt.com/blog/favorite-editor.webp) Each editor serves its own purpose—some users are used to Markdown edition, while others prefer a non-technical, visual approach. At the end, **Markdown syntax is the final output** for both editors. This article explains the technical processes behind the visual editor, exploring how it interprets Markdown, converts it back, and why this process might occasionally lead to changes from the original content. ## **Markdown Editor** ![Edit directly markdown on Nuxt Studio](https://content.nuxt.com/blog/markdown-editor.webp) The Markdown editor in Nuxt Studio provides full control over your content, allowing you to write directly in [MDC](https://content.nuxt.com/docs/files/markdown) (an empowered Markdown syntax). This syntax enables integration of Vue components directly into your Markdown files, offering more flexibility to structure your pages. When your file is saved with the Markdown editor, the content is stored exactly as you've written it, preserving all specific syntax and formatting. This editor is ideal for users comfortable with Markdown who want precise control over the layout and structure of their content. ## **Visual Editor** ![Edit your content with a visual editor on Nuxt Studio](https://content.nuxt.com/blog/visual-editor.webp) The Visual Editor is a sort of WYSIWYG (What You See Is What You Get) tool built on top of [TipTap](https://tiptap.dev/){rel=""nofollow""} and [ProseMirror](https://prosemirror.net/){rel=""nofollow""}, designed to abstract away the complexities of Markdown syntax and offer a more intuitive, visual editing experience. This editor is particularly user-friendly for those who prefer not to deal with raw Markdown code. ### **How the visual editor processes files** When you open a Markdown file with the Visual Editor, Nuxt Studio first parses the original Markdown file. Using the [MDC module](https://github.com/nuxt-modules/mdc){rel=""nofollow""}, it generates an Abstract Syntax Tree (AST). This AST is then converted into a TipTap-compatible format (TipTap AST), allowing the editor to accurately render the document visually. Once the Visual Editor displays the content, users can make updates in a visually intuitive way. Behind the scenes, the editor continuously transforms the TipTap AST back into MDC AST then MDC syntax, ensuring that your content remains in Markdown format. ### **Why Changes might occur in the original markdown file without user modification** ![Alert is displayed when automatic markdown parsing is detected](https://content.nuxt.com/blog/automatic-parsing-modal.webp) #### **Non-Critical Changes** As the Visual Editor translates the visual formatting back into Markdown, it applies a parsing algorithm that applies predefined Markdown standards. In some cases, these standards may differ slightly from the original content. These changes are typically non-impactful and are only another working syntax of the Markdown, the rendered website should remain consistent with the original. #### **Critical Changes** Ideally, every feature in Markdown has a direct and accurate equivalent in the Visual Editor. We've built custom TipTap extensions to support custom MDC syntax such as [Vue components](https://content.nuxt.com/docs/files/markdown#vue-components) edition or [front-matter](https://content.nuxt.com/docs/files/markdown#front-matter). However, in rare cases, particularly with complex or unconventional Markdown elements, the Visual Editor may not fully support or correctly interpret these elements. When this happens, the editor might approximate, simplify, or even omit these elements during the parsing process. Such discrepancies can result in data loss or regressions when converting back to Markdown. While these occurrences are rare, they can disrupt the intended display or functionality of your content. Our primary objective is to prevent any loss of content and to maintain the integrity of your Markdown files. If you encounter any issues where the transition from visual to Markdown isn’t perfect, we encourage you to report them on our Discord server. Your feedback is invaluable in helping us refine and improve the Visual Editor, ensuring it meets the needs of all users. ## **Best practices to minimize unintended changes** To avoid losing crucial formatting or content, consider the following best practices: - **Avoid using complex HTML structures**. As the MDC syntax allows you to integrate Vue components, It's more effective to create reusable components that can be easily inserted into the Markdown and edited within the editor, rather than relying on intricate HTML code. - **Use one editor consistently.** Whenever possible, select the editor that best suits your needs and stick with it for the entire page. - **Review changes after switching from an editor to the other.** After switching editors, always review the Markdown (on the review page) and check the preview to ensure no important elements have been altered. ## **Conclusion** Switching between the Markdown editor and the visual editor in Nuxt Studio offers flexibility, but it's important to be aware of the technical implications. Understanding how the visual editor processes and converts Markdown can help ensure that what you craft in Markdown is accurately displayed in the visual editor, allowing non-technical users to easily edit everything without altering the original Markdown file. ### # Visual Front-matter Edition ::warning This article was published before the merge of the [Content](https://github.com/nuxt/content){rel=""nofollow""} and [Studio](https://github.com/nuxtlabs/studio-module){rel=""nofollow""} modules on January 6, 2025. As a result, it may contain some inconsistencies. The Studio module is now deprecated and available as an opt-in feature of the Content module. Learn how to enable it in [this guide](https://content.nuxt.com/docs/getting-started) . :: ## Visual Front-Matter editing You can now edit your markdown front-matter without writing in the `YAML` syntax. Instead, Nuxt Studio automatically generates a user-friendly form that simplifies metadata editing. :video{autoplay controls loop poster="https://res.cloudinary.com/nuxt/video/upload/v1729157955/frontmatterform2_rmh58v.jpg" src="https://res.cloudinary.com/nuxt/video/upload/v1729157955/frontmatterform2_rmh58v.mp4"} ## What is the front-matter? Front-matter is a convention used in Markdown-based CMSs to provide metadata for pages, such as descriptions, titles, and more. In [Nuxt Content](https://content.nuxt.com/docs/files/markdown#front-matter), the front-matter uses the YAML syntax. ::callout --- icon: i-ph-info to: https://content.nuxt.com/docs/files/markdown#front-matter --- For more detailed information about front-matter syntax, visit the Nuxt Content documentation. :: ## The last piece of our non-technical editor Nuxt Studio has been designed with non-technical users in mind, mainly since our editor has been released. Our goal is to make markdown and content edition accessible to everyone. The automatic form generation for front-matter is the next logical step. By moving away from the complexities of YAML syntax, we’re simplifying the process for non-developers, offering dynamic input options like image pickers, date pickers, boolean toggles and more. This enhancement brings us to a fully visual, user-friendly content management experience. ## Expanding to all YAML and JSON files Soon, the form generation feature will extend to all `YAML` and `JSON` files you edit within Nuxt Studio, making it easier than ever to work with structured data. ## Looking ahead to Nuxt Content v3 ::callout{icon="i-ph-lightbulb"} This section is just a teaser of [Nuxt Content v3](https://github.com/nuxt/content/tree/v3){rel=""nofollow""} . We will publish a more detailed blog post soon. :: We're actively working on the next major update of Nuxt Content which will bring significant performance improvements and new features to further enhance your content management experience. ### Improved Performance A key challenge with Nuxt Content v2 was the large bundle size required to store all content files. It was an issue when deploying to edge platforms like [NuxtHub](https://hub.nuxt.com/){rel=""nofollow""}. To address this, Nuxt Content v3 moves away from the file based storing in production and leverage SQL database system. This switch is transparent to users. We're providing a zero config support for development mode, static generation, server rendering and edge deployments with NuxtHub. ### Introducing Collections Collections are groups of related content items within your Nuxt Content project. They help organize and manage large datasets more efficiently. #### Define collections You'll be able to define collections in the `content.config.ts` file which is used by Nuxt Content to configure database structures, utility types, and methods for finding, parsing, and querying content. #### Collections schema Schemas enforce consistency within collections and improve TypeScript typings for better integration with Nuxt Content utilities. ```ts [content.config.ts] import { defineCollection, z } from '@nuxt/content' // Export collections export const collections = { // Define collection using `defineCollection` utility posts: defineCollection({ // Specify the type of content in this collection type: 'page', // Load every file matching this pattern source: 'blog/**/*.md', // Define custom schema for this collection schema: z.object({ date: z.date(), image: z.object({ src: z.string(), alt: z.string() }), badge: z.object({ label: z.string(), color: z.string() }) }) }), } ``` ### Built with Nuxt Studio in mind ::warning This article was published before v3.7, learn [this guide](https://github.com/nuxt/content/blob/main/CHANGELOG.md#370-2025-09-12){rel=""nofollow""} to migrate. :: Nuxt Studio was originally developed alongside Nuxt Content v2, but with v3, we're building the module with Nuxt Studio experience in mind. Our goal is to create the best CMS platform for content editing, while still offering the best developers experience. For example, collection schemas will help us further enhance form generation in Studio. Among other things, you'll be able to set the editor type for a field directly in your schema. ```ts [content.config.ts] image: z.object({ src: z.string().editor({ type: 'media' }) alt: z.string() }), icon: z.string().editor({ type: 'icon' }) ``` ::callout{icon="i-ph-lightbulb" to="https://github.com/nuxt/content/tree/main"} Nuxt Content v3 has been officially released. Don't hesitate to try it out and give us feedback. :: # Studio Form Customisation ::warning This article was published before v3.7, learn [this guide](https://github.com/nuxt/content/blob/main/CHANGELOG.md#370-2025-09-12){rel=""nofollow""} to migrate. :: The [Studio](https://nuxt.studio){rel=""nofollow""} forms are dynamically generated based on the collection schema defined in your content configuration file. This behaviour applies whether you’re editing the [frontmatter](https://content.nuxt.com/docs/files/markdown#frontmatter) of a `Markdown` file or a `JSON` / `YAML` file. :video{autoplay controls poster="https://res.cloudinary.com/nuxt/video/upload/v1739982761/frontmatterform_yjafgt.png" src="https://res.cloudinary.com/nuxt/video/upload/v1739982761/frontmatterform_yjafgt.mp4"} ## **Defining your form with** `zod` Schema Nuxt Content leverages [zod](https://github.com/colinhacks/zod){rel=""nofollow""} to let you define a type-safe schema for your content. This schema not only validates your data but also powers the form generation in **Studio**. ### **Built-in zod Helpers** You can define your Content schema by adding the `schema` property to your collection and by using a [zod](https://github.com/colinhacks/zod){rel=""nofollow""} schema. `@nuxt/content` exposes a `z` object that contains a set of [Zod](https://content.nuxt.com) utilities for common data types. ::prose-code-group ```ts [content.config.ts] export default defineContentConfig({ collections: { posts: defineCollection({ type: 'page', source: 'blog/*.md', schema: z.object({ draft: z.boolean().default(false), category: z.enum(['Alps', 'Himalaya', 'Pyrenees']).optional(), date: z.date(), image: z.object({ src: z.string().editor({ input: 'media' }), alt: z.string(), }), slug: z.string().editor({ hidden: true }), icon: z.string().optional().editor({ input: 'icon' }), authors: z.array(z.object({ slug: z.string(), username: z.string(), name: z.string(), to: z.string(), avatar: z.object({ src: z.string(), alt: z.string(), }), })), }), }), }, }) ``` :::code-preview{icon="i-lucide-eye" label="Generated Form"} ![Form preview](https://content.nuxt.com/docs/studio/preview-schema.png) ::: :: ### **Native Inputs Mapping** Primitive Zod types are automatically mapped to appropriate form inputs in **Studio**: - **String** → Text input - **Date** → Date picker - **Number** → Number input (counter) - **Boolean** → Toggle switch - **Enum** → Select dropdown - **Arrays of strings** → List of badge inputs - **Arrays of objects** → Accordion of items with embedded form :video{autoplay controls loop poster="https://res.cloudinary.com/nuxt/video/upload/v1740679550/arrayobjectandstring_r1jpvz.jpg" src="https://res.cloudinary.com/nuxt/video/upload/v1740679550/arrayobjectandstring_r1jpvz.mp4"} ### Custom Inputs Mapping Content goes beyond primitive types. You can customise form fields using the `editor` method, which extends Zod types with metadata to empower editor interface. This allows you to define custom inputs or hide fields. #### Usage ```ts [content.config.ts] mainScreen: z.string().editor({ input: 'media' }) ``` #### Options ##### `input: 'media' | 'icon'` You can set the editor input type. Currently both icon and media are available since there are handled in Studio editor. ##### `hidden: Boolean` This option can be set to avoid the display of a field in the Studio editor. ::prose-tip Studio inputs are fully extensible. We can create as many input as we want based on our users needs. :: # Visual YAML and JSON File Edition ::warning This article was published before the merge of the [Content](https://github.com/nuxt/content){rel=""nofollow""} and [Studio](https://github.com/nuxtlabs/studio-module){rel=""nofollow""} modules on January 6, 2025. As a result, it may contain some inconsistencies. The Studio module is now deprecated and available as an opt-in feature of the Content module. Learn how to enable it in [this guide](https://content.nuxt.com/docs/getting-started) . :: ## Auto-generated form for `YAML` and `JSON` files :video{controls loop src="https://res.cloudinary.com/nuxt/video/upload/v1730132248/yml-json-form_n9czcs.mp4"} Continuing our journey to make Nuxt Studio the tool for non-technical users to edit their content with Nuxt websites, we're excited to announce that `YAML` and `JSON` files can now be edited through a generated visual form. This update removes the need for users to interact directly with complex file syntax such as YAML or JSON. ::callout{icon="i-ph-info"} Arrays are not yet handled as form but we'll work on it once collections and user-defined schemas will be released with Nuxt Content v3. See the section below. :: ### Synchronized navigation Alongside this update, we’ve improved the synchronized navigation between the preview and selected files for non-Markdown formats (like YAML and JSON). To apply this fixe, please update the Studio module to the latest version `v2.2.0`. ## On the Road to Nuxt Content v3 We’re excited to share that the fourth alpha version of Nuxt Content v3 has been released, with the [**draft documentation**](https://content.nuxt.com/){rel=""nofollow""} available. ### What’s Next? In the coming months, we’ll focus on testing and refining Nuxt Content v3 to ensure a robust, production-ready release. Here’s a quick look at the Nuxt Studio related improvements ahead: - **Merging the Studio module**: Soon, the Studio module will be integrated directly into Nuxt Content. Once Nuxt Content v3 is released, activating Studio will be as simple as setting `content.editor: true` in your `nuxt.config.ts` file. This simplification means no extra module is required for Studio, making setup faster. - **Unified documentation**: With the module integration, we’ll also merge the [Content](https://content.nuxt.com){rel=""nofollow""} and [Studio](https://nuxt.studio){rel=""nofollow""} documentation and websites into one comprehensive resource. Only the Studio platform (available once the user is logged) will remain as a standalone site. - **Take advantage of data structures and collections in Studio**: With Nuxt Content v3, the Studio platform will support and adapt its behaviour to [collections](https://content.nuxt.com/docs/collections/define) and user-defined schemas. This enhancement will allow schema-generated forms for both YAML and JSON files as well as front-matter within Markdown files. These updates reflect our commitment to providing the best content editing platform for your Nuxt website. Stay tuned!