Skip to main content

Module options

Runtime, parsing, search, sitemap, agent, and cache options for nuxt.config.ts.

Configure the Nuxt module under content. Collection shape and provider selection belong in content.config.ts.

nuxt.config.ts
export default defineNuxtConfig({
  modules: ['@lupinum/ginko-content'],
  content: {
    // options
  }
})

Options

OptionTypeDefaultPurpose
api.baseURLstring'/api/_content'Base path for built-in content endpoints
componentPolicyPortableComponentPolicyV1{ components: {} }Allowlist of portable MDC components and props
i18nboolean | ContentI18nOptionstrueLocale, fallback, and translated-slug policy
sitemapboolean | ContentSitemapOptionstrueNuxt Sitemap source and artifact assertions
searchfalse | ContentSearchOptionsfalseMiniSearch, Pagefind, or provider-owned search
validation'report' | 'error''report'Authored-link validation behavior
previewfalse | { token?: string }disabledFilesystem preview-overlay authorization
revalidatefalse | ContentRevalidateOptionsdisabledAuthenticated cache invalidation endpoint
linksContentLinksOptions{}Writer-facing links to named Nuxt routes
agentfalse | ContentAgentRouteOptionsenabledAgent routes, headers, negotiation, and prerendering
cachefalse | stringdisabledServer cache-adapter module specifier
watchbooleantrueContent watching and hot reload in development
sourcesRecord<string, MountOptions>{}Additional content storage mounts
ignoresstring[][]Files excluded from parsing and watching
markdownobjectsee belowComark plugins and rendering behavior
yamlfalse | Record<string, unknown>{}YAML parser configuration
csvfalse | { json?, delimiter? }{ delimiter: ',', json: true }CSV parser configuration
navigationfalse | { fields: string[] }{ fields: [] }Navigation generation and selected metadata
transformersstring[][]Custom content-transformer module specifiers
respectPathCasebooleanfalsePreserve uppercase characters in generated paths

Each sources entry requires a storage driver and may set name, prefix, and driver-specific options.

In development, watch: true refreshes active Nuxt useAsyncData and useFetch consumers when source content changes. A valid change to the resolved root content.config.* automatically reloads Nuxt module setup and rebuilds derived content caches and generated types; the pnpm dev command does not need to be restarted manually. Query promises are one-shot snapshots, so wrap browser-visible one(), many(), paginate(), and navigation reads in useAsyncData when they should react to content edits.

The module watches the resolved root config file. If that file imports local schema or collection helpers, add those helper paths to Nuxt's top-level watch option when helper-only edits should also reload module setup. Content config and its local helpers must evaluate synchronously; top-level await is not supported. Setting content.watch: false disables both source hot reload and automatic collection-config reload. If an invalid config save makes module setup exit, fix the config and restart the dev command.

Parsing and rendering

Markdown

nuxt.config.ts
export default defineNuxtConfig({
  content: {
    markdown: {
      plugins: [
        'toc',
        'shiki',
        'summary'
      ],
      tags: {
        code: 'ProseCodeInline',
        img: 'ProseImg',
        pre: 'ProsePre'
      },
      image: 'auto',
      anchorLinks: {
        depth: 4,
        exclude: [1]
      }
    }
  }
})

markdown.plugins is an ordered list of Comark plugin names or [name, options] tuples. No plugin is installed implicitly: omitting plugins and setting plugins: [] are identical. Built-ins are breaks, emoji, footnotes, shiki, json-render, math, mermaid, punctuation, security, summary, and toc. shiki uses Ginko's syntax-highlighting integration. The deprecated highlight name remains an alias during 0.4.x and warns once during module setup; do not configure both names. The optional math and mermaid plugins require KaTeX and Beautiful Mermaid respectively in the application. When Math is enabled, add katex/dist/katex.min.css to Nuxt's css array so its server-rendered markup is styled without a client-only flash:

ts
export default defineNuxtConfig({
  css: ['katex/dist/katex.min.css']
})

The zero-config shiki entry uses Ginko's light/dark Material defaults. For custom syntax registration, use Comark/Shiki's canonical themes: { light, dark } and languages options with imported registration objects; singular theme and langs are rejected during module setup. Custom plugin module specifiers run in the build/server content-ingestion profile. Their AST must still satisfy Ginko's inert render policy: Vue bindings, directives, event handlers, and arbitrary active elements are intentionally rejected.

Markdown optionTypeDefault
pluginsMarkdownPluginDescriptor[][]
tagsRecord<string, string>code, img, and pre mapped to built-in prose components
anchorLinksboolean | { depth?, exclude? }{ depth: 4, exclude: [1] }
image'auto' | 'img' | 'nuxt-image''auto'

image: 'auto' uses <NuxtImg> when Nuxt Image is available and falls back to <img>. The removed MDC-era highlight, markdown.mdc, remarkPlugins, rehypePlugins, and markdown.toc options are not accepted.

Structured files and transformers

csv.delimiter changes the delimiter. With csv.json: true, the first row becomes object keys; false returns rows as arrays.

Custom transformers are module specifiers. Each module must default-export a transformer created with defineTransformer() from @lupinum/ginko-content/transformers:

nuxt.config.ts
export default defineNuxtConfig({
  content: {
    transformers: ['~~/content-transformers/word-count']
  }
})

Transformers run in the ingest pipeline, so their output is used consistently by queries, navigation, search, and generated routes.

Component policy

componentPolicy is the closed portability and runtime allowlist for custom MDC components. Each component declares:

  • kind: 'block' or 'inline'
  • props: named props with a type of 'string', 'number', 'boolean', 'json', or 'asset', plus required
  • slots: allowed slot names
  • media: null or the source, alt, title, and filename prop mapping

Keep the policy in module config; it is included in the resolved content contract used by rendering and portability checks.

Expose extra frontmatter on navigation nodes with navigation.fields:

ts
content: {
  navigation: {
    fields: ['badge', 'icon']
  }
}

content.links gives authors stable shortcuts to named application routes:

nuxt.config.ts
export default defineNuxtConfig({
  content: {
    links: {
      main: {
        pricing: {
          route: 'pricing',
          params: { plan: 'team' },
          query: { ref: 'docs' }
        }
      }
    }
  }
})

Authors can then write [Pricing]($main.pricing). Each target requires route and may include static params and query. The route name, params, and query are passed to Nuxt I18n's localePath() resolver. Keep translated paths in Nuxt I18n rather than duplicating them here.

Localization

When @nuxtjs/i18n is installed, its top-level config is the sole authority for locales and defaultLocale. content.i18n may still set fallback, translatedSlugs, and strictTranslatedSlugs; repeating locale authority under both configs fails module setup.

nuxt.config.ts
export default defineNuxtConfig({
  modules: ['@lupinum/ginko-content', '@nuxtjs/i18n'],
  i18n: {
    defaultLocale: 'en',
    locales: ['en', 'de']
  },
  content: {
    i18n: {
      fallback: { de: ['en'] },
      translatedSlugs: true,
      strictTranslatedSlugs: true
    }
  }
})

Without Nuxt I18n, content.i18n may also declare locales and defaultLocale. translatedSlugs defaults to false; strictTranslatedSlugs promotes translated-slug warnings to validation errors.

Search is disabled by default. Set search: {} to enable MiniSearch or choose an engine explicitly.

nuxt.config.ts
export default defineNuxtConfig({
  content: {
    search: {
      engine: 'minisearch',
      collections: ['docs'],
      extraFields: ['tags'],
      minisearch: {
        fields: ['title', 'content', 'headings', 'tags'],
        storeFields: ['path', 'title', 'excerpt', 'collection', 'anchor', 'locale', 'tags'],
        boost: { title: 4, headings: 2, tags: 3, content: 1 }
      }
    }
  }
})
OptionTypeDefault
engine'minisearch' | 'pagefind' | 'provider''minisearch'
apiBaseURLstring${content.api.baseURL}/search
ignoredTagsstring[]['script', 'style', 'pre']
filterQueryQueryWhere{ partial: false }
collectionsstring[]Route-backed public collections
extraFieldsstring[][]
minisearch.fieldsstring[]['title', 'content', 'headings']
minisearch.storeFieldsstring[]['path', 'title', 'excerpt', 'collection', 'anchor', 'locale']
minisearch.boostRecord<string, number>{ title: 4, headings: 2, content: 1 }
minisearch.fuzzynumber | boolean0.2
minisearch.prefixbooleantrue

The required path, title, excerpt, and collection store fields are retained even when storeFields is customized. Add a frontmatter field to extraFields before indexing or storing it. Choose a search backend explains the deployment trade-offs.

Validation and preview

validation: 'report' writes <buildDir>/content-cache/validation.json; 'error' also fails before an invalid filesystem snapshot is published. Validation covers authored content links, quick links, headings, resolved Nuxt routes, and mounted public assets. External providers validate their own authored content.

preview protects request-scoped filesystem preview storage:

ts
content: {
  preview: {
    token: process.env.GINKO_CONTENT_PREVIEW_TOKEN
  }
}

The token is accepted through x-nuxt-content-preview or a previewToken cookie, never a query parameter. Valid preview responses are private and not stored. In production, the filesystem provider serves a sealed snapshot and rejects preview-overlay access; a custom provider owns its production preview policy. The drafts and partials guide covers the visibility model.

Agent-readable output

content.agent controls Nuxt plumbing for the agent site declared in content.config.ts.

OptionDefaultPurpose
routestrueRegister /raw/**.md, /llms.txt, localized LLM indexes, and full LLM routes
linkHeaderstrueAdd agent links and content-signal headers to eligible SSR responses
markdownNegotiationtrueReturn Markdown from eligible dynamic routes for Accept: text/markdown
prerendertrueWrite agent routes during static generation

Set agent: false to disable all four behaviors.

Static generation writes /llms.txt, /llms-full.txt, their non-default-locale variants, and the /raw/**.md routes discovered from those files. It does not generate /:route/index.md. Same-URL content negotiation only works when Nitro middleware handles the request, so static sites should link to /raw/**.md explicitly.

Use agentRawPathForRoute() from @lupinum/ginko-content/agent-paths when an application needs the raw URL:

ts
import { agentRawPathForRoute } from '@lupinum/ginko-content/agent-paths'

const markdown = await $fetch<string>(agentRawPathForRoute('/docs/intro'))

Sitemap

With @nuxtjs/sitemap installed, sitemap: true registers the Ginko source. The object form accepts:

OptionTypeDefault
pathstring'/sitemap'
includestring[]All eligible collections
excludestring[][]
includeDraftsbooleantrue in development, false otherwise
assertContentSitemapAssertOptionsdisabled

Data collections and collections with sitemap: false are excluded. Keep content URLs in Ginko and static page URLs in Nuxt; do not duplicate either in app-owned sitemap arrays. The sitemap and prerender guide describes that ownership boundary.

Enable assertions to fail a build when generated XML violates release invariants:

ts
content: {
  sitemap: {
    assert: {
      enabled: true,
      mode: 'generate',
      requiredCollections: ['docs'],
      requiredPaths: ['/docs'],
      forbiddenPathPrefixes: ['/api', '/_nuxt'],
      requireProductionSiteUrl: true
    }
  }
}
Assertion optionTypeDefault
enabledbooleanfalse
mode'generate' | 'build' | 'both''generate'
allowEmptybooleanfalse
minUrlsPerSitemapnumber1
requireImagesbooleanfalse
requiredCollectionsstring[][]
requiredPathsstring[][]
forbiddenPathPrefixesstring[][]
requireProductionSiteUrlbooleanfalse
sitemapsRecord<string, { allowEmpty?, minUrls?, requireImages? }>{}

requiredPaths compares URL pathnames. requireProductionSiteUrl rejects placeholder and local hosts. Entries in sitemaps override the global count and image rules for a named sitemap.

Cache and revalidation

cache points to a module whose default export, contentCacheAdapter, or cacheAdapter implements ContentCacheAdapter. The adapter may omit invalidate when it only applies response metadata. In that case authenticated revalidation requests fail with 501 revalidation_not_supported; Ginko never acknowledges a purge that did not occur.

nuxt.config.ts
export default defineNuxtConfig({
  content: {
    cache: '~~/server/content-cache',
    revalidate: {
      token: process.env.GINKO_CONTENT_REVALIDATE_TOKEN!,
      allowUnsigned: false
    }
  }
})
Revalidation optionTypeDefaultPurpose
tokenstringrequiredHMAC secret and, when enabled, bearer token
allowUnsignedbooleanfalsePermit token-only requests for local or compatibility workflows

The endpoint is ${api.baseURL}/revalidate. Signed requests provide x-ginko-signature-timestamp, x-ginko-revalidation-event, and x-ginko-signature; the signature is HMAC-SHA256 over <timestamp>.<event-id>.<raw-body>. If allowUnsigned is true and no signature headers are present, callers may use x-ginko-revalidate-token or Authorization: Bearer <token>.

The closed JSON body is { tags?: string[], paths?: string[] }. It must contain at least one target and is limited to 32 KiB, 200 combined entries, and 1,000 characters per entry. The provider caching guide covers adapters and cache hints.