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.
export default defineNuxtConfig({
modules: ['@lupinum/ginko-content'],
content: {
// options
}
})Options
| Option | Type | Default | Purpose |
|---|---|---|---|
api.baseURL | string | '/api/_content' | Base path for built-in content endpoints |
componentPolicy | PortableComponentPolicyV1 | { components: {} } | Allowlist of portable MDC components and props |
i18n | boolean | ContentI18nOptions | true | Locale, fallback, and translated-slug policy |
sitemap | boolean | ContentSitemapOptions | true | Nuxt Sitemap source and artifact assertions |
search | false | ContentSearchOptions | false | MiniSearch, Pagefind, or provider-owned search |
validation | 'report' | 'error' | 'report' | Authored-link validation behavior |
preview | false | { token?: string } | disabled | Filesystem preview-overlay authorization |
revalidate | false | ContentRevalidateOptions | disabled | Authenticated cache invalidation endpoint |
links | ContentLinksOptions | {} | Writer-facing links to named Nuxt routes |
agent | false | ContentAgentRouteOptions | enabled | Agent routes, headers, negotiation, and prerendering |
cache | false | string | disabled | Server cache-adapter module specifier |
watch | boolean | true | Content watching and hot reload in development |
sources | Record<string, MountOptions> | {} | Additional content storage mounts |
ignores | string[] | [] | Files excluded from parsing and watching |
markdown | object | see below | Comark plugins and rendering behavior |
yaml | false | Record<string, unknown> | {} | YAML parser configuration |
csv | false | { json?, delimiter? } | { delimiter: ',', json: true } | CSV parser configuration |
navigation | false | { fields: string[] } | { fields: [] } | Navigation generation and selected metadata |
transformers | string[] | [] | Custom content-transformer module specifiers |
respectPathCase | boolean | false | Preserve 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
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:
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 option | Type | Default |
|---|---|---|
plugins | MarkdownPluginDescriptor[] | [] |
tags | Record<string, string> | code, img, and pre mapped to built-in prose components |
anchorLinks | boolean | { 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:
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 atypeof'string','number','boolean','json', or'asset', plusrequiredslots: allowed slot namesmedia:nullor 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.
Navigation and quick links
Expose extra frontmatter on navigation nodes with navigation.fields:
content: {
navigation: {
fields: ['badge', 'icon']
}
}content.links gives authors stable shortcuts to named application routes:
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.
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
Search is disabled by default. Set search: {} to enable MiniSearch or choose an engine explicitly.
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 }
}
}
}
})| Option | Type | Default |
|---|---|---|
engine | 'minisearch' | 'pagefind' | 'provider' | 'minisearch' |
apiBaseURL | string | ${content.api.baseURL}/search |
ignoredTags | string[] | ['script', 'style', 'pre'] |
filterQuery | QueryWhere | { partial: false } |
collections | string[] | Route-backed public collections |
extraFields | string[] | [] |
minisearch.fields | string[] | ['title', 'content', 'headings'] |
minisearch.storeFields | string[] | ['path', 'title', 'excerpt', 'collection', 'anchor', 'locale'] |
minisearch.boost | Record<string, number> | { title: 4, headings: 2, content: 1 } |
minisearch.fuzzy | number | boolean | 0.2 |
minisearch.prefix | boolean | true |
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:
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.
| Option | Default | Purpose |
|---|---|---|
routes | true | Register /raw/**.md, /llms.txt, localized LLM indexes, and full LLM routes |
linkHeaders | true | Add agent links and content-signal headers to eligible SSR responses |
markdownNegotiation | true | Return Markdown from eligible dynamic routes for Accept: text/markdown |
prerender | true | Write 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:
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:
| Option | Type | Default |
|---|---|---|
path | string | '/sitemap' |
include | string[] | All eligible collections |
exclude | string[] | [] |
includeDrafts | boolean | true in development, false otherwise |
assert | ContentSitemapAssertOptions | disabled |
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:
content: {
sitemap: {
assert: {
enabled: true,
mode: 'generate',
requiredCollections: ['docs'],
requiredPaths: ['/docs'],
forbiddenPathPrefixes: ['/api', '/_nuxt'],
requireProductionSiteUrl: true
}
}
}| Assertion option | Type | Default |
|---|---|---|
enabled | boolean | false |
mode | 'generate' | 'build' | 'both' | 'generate' |
allowEmpty | boolean | false |
minUrlsPerSitemap | number | 1 |
requireImages | boolean | false |
requiredCollections | string[] | [] |
requiredPaths | string[] | [] |
forbiddenPathPrefixes | string[] | [] |
requireProductionSiteUrl | boolean | false |
sitemaps | Record<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.
export default defineNuxtConfig({
content: {
cache: '~~/server/content-cache',
revalidate: {
token: process.env.GINKO_CONTENT_REVALIDATE_TOKEN!,
allowUnsigned: false
}
}
})| Revalidation option | Type | Default | Purpose |
|---|---|---|---|
token | string | required | HMAC secret and, when enabled, bearer token |
allowUnsigned | boolean | false | Permit 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.