Configuration
Configure Ardo's CLI, generated routes, Markdown pipeline, API output, and React UI.
On this page
Ardo's CLI reads optional site configuration from ardo.config.ts. Most sites do not need a Vite configuration file: ardo dev, ardo build, and ardo preview provide the framework integration. Keep UI behavior in React: ArdoRoot, header/sidebar/footer props, component overrides, and runtime hooks.
This page documents Ardo's site and build options, then points to the React-side UI hooks and components. Existing Vite projects can continue to use the ardo() plugin API while migrating. For a broader capability matrix, including planned features that are not yet configurable, see Releases.
Basic Setup
import { defineArdoConfig } from "ardo/config"
export default defineArdoConfig({
title: "My Documentation",
description: "My awesome docs",
})
The defineArdoConfig() function accepts the same Ardo options that the Vite plugin supports. Add a vite.config.ts only for extra Vite plugins or Vite-specific settings; the CLI supplies Ardo's plugins automatically.
For example, configure the development server without adding an ardo() call:
import { defineConfig } from "ardo/vite"
export default defineConfig({
server: { port: 4000 },
})
defineConfig is re-exported by Ardo, so this does not require a direct Vite dependency.
Both files can export an object, a promise, or a callback that receives Vite's configuration environment. Changes to existing config files restart the development server automatically. Restart ardo dev after adding or removing either file.
When migrating a project, move options from ardo({...}) into ardo.config.ts before removing the plugin. Ardo reports an error if both configuration forms remain active for one project.
Site Configuration
These options control the identity and basic behavior of your documentation site.
title
- Type:
string - Default: the
nameinpackage.json, then"Ardo"
The site title, used in the default <title> meta tag and displayed in the header. Set it in ardo.config.ts when the package name isn't the title readers should see.
defineArdoConfig({
title: "My Documentation",
})
description
- Type:
string - Default:
""
Site description for the default meta tags.
defineArdoConfig({
title: "My Docs",
description: "A comprehensive guide to my library",
})
titleSeparator
- Type:
string - Default:
" | "
Separator between the page title and the site title in <title> tags. For example, a page titled "Getting Started" produces Getting Started | My Docs.
defineArdoConfig({
title: "My Docs",
titleSeparator: " - ",
})
base
- Type:
string - Default:
"/"
Base URL path for the site. Set this if your docs live under a subpath.
defineArdoConfig({
title: "My Docs",
base: "/docs/",
})
A GitHub Pages project site is served under the repository name, so set base: "/repo-name/". See Deployment for every host.
siteUrl
- Type:
string - Default:
""
Absolute public URL for the site. Ardo uses it to generate canonical URLs, og:url, and absolute social image URLs for Markdown routes.
defineArdoConfig({
title: "My Docs",
siteUrl: "https://example.com",
})
If your docs are served from a subpath, configure base separately. Ardo combines siteUrl, base, and the route path for generated canonical URLs.
metadata
- Type:
{ image?: string; ogType?: string; twitterCard?: "summary_large_image" | "summary"; twitterSite?: string } - Default:
{}
Site-level defaults for generated Markdown metadata. Page frontmatter can override these values per route.
defineArdoConfig({
title: "My Docs",
siteUrl: "https://example.com",
metadata: {
image: "/social.png",
ogType: "website",
twitterCard: "summary_large_image",
twitterSite: "@example",
},
})
seo.llms
- Type:
boolean | { indexFileName?: string; fullFileName?: string; includeFull?: boolean; title?: string; description?: string } - Default:
true
Generates LLM-friendly text artifacts alongside the other build outputs:
/llms.txtis a concise Markdown index of the Markdown and MDX routes. It follows the sidebar: one heading per section, in tab order, and pages byorder, so agents read the guide before the reference. A generated API or OpenAPI reference appears as one link to its index page./llms-full.txtcontains every page, in the same order, as one Markdown file. Each page starts with a##title, and its own headings move one level down.
With documentation versioning enabled, Ardo also emits both files under the current version's path, such as /v6/llms.txt and /v6/llms-full.txt. The versioned index links to its versioned full-text file. Preserve these files with older HTML snapshots; the root files remain aliases for the current documentation version.
Ardo uses page frontmatter titles and descriptions when available, skips TSX routes, and strips MDX imports, exports, and JSX component tags from the full export. Set llms: false in page frontmatter to exclude a Markdown page from both files.
defineArdoConfig({
title: "My Docs",
siteUrl: "https://example.com",
seo: {
llms: {
title: "My Docs",
description: "Developer documentation for my package.",
includeFull: true,
},
},
})
Set seo.llms: false if you want to manage these files yourself from public/.
seo.sitemap
- Type:
boolean | { changefreq?: "always" | "hourly" | "daily" | "weekly" | "monthly" | "yearly" | "never"; priority?: number; exclude?: string[] } - Default:
true
Generates /sitemap.xml from Markdown, MDX, and TSX routes. Ardo combines siteUrl, base, and each route path so subpath deployments still produce absolute URLs.
defineArdoConfig({
title: "My Docs",
siteUrl: "https://example.com",
base: "/docs/",
seo: {
sitemap: {
changefreq: "weekly",
priority: 0.7,
exclude: ["/kit"],
},
},
})
Set seo.sitemap: false to skip sitemap generation. Set sitemap: false in page frontmatter to exclude a single Markdown route. Use seo.sitemap.exclude for route path prefixes, including TSX routes: /kit excludes /kit and /kit/buttons, while /kitchen remains included. A trailing slash is optional. Prefixes refer to route paths before deployment base, version, and locale prefixes are added. This only controls sitemap inclusion; set the page's robots metadata separately when it should be noindex.
seo.robots
- Type:
boolean | { allow?: string[]; disallow?: string[] } - Default:
true
Generates /robots.txt. By default Ardo allows all crawlers and points them at the generated sitemap when siteUrl is configured.
defineArdoConfig({
title: "My Docs",
siteUrl: "https://example.com",
seo: {
robots: {
allow: ["/"],
disallow: ["/drafts/"],
},
},
})
Set seo.robots: false if you want to provide your own robots.txt from public/.
linkCheck
- Type:
{ enabled?: boolean; checkAnchors?: boolean; exclude?: string[]; level?: "warn" | "error" } - Default:
{ enabled: true, checkAnchors: true, level: "warn" }
Checks internal links in the finished build output with ferrolink. After ardo build has written every page, Ardo verifies each internal link, image, script, stylesheet, srcset candidate, and meta refresh in the generated HTML, whether it came from Markdown, a component, or a generated page. It also checks the redirect destinations in _redirects and vercel.json and the version paths in versions.json. Unless disabled, it verifies that every #fragment matches an element id on the target page. Redirect sources are valid targets, because Ardo writes a page for each of them.
External links are out of scope, because their availability depends on third-party servers rather than on the build. Links to the other versions in versioning count as present, because each version is deployed on its own. A link that leaves the deployment base, such as /guide/ on a site served below /docs/, is reported. Each finding names the file and line it comes from.
Patterns in exclude are globs of link targets to skip. A pattern with a leading / matches the URL path, base path included, such as /generated/**, or /blog/** for another app on the same host. Any other pattern is relative to the deployment base. A skipped target is reported neither as missing nor for a missing anchor.
defineArdoConfig({
title: "My Docs",
linkCheck: {
level: "error",
exclude: ["/generated/**"],
},
})
Use checkAnchors: false when another tool rewrites heading ids after Ardo has read the route manifest. Use enabled: false only when another link checker owns the build gate.
validation
- Type:
{ frontmatter?: { invalid?: "warn" | "error" | "ignore"; unknown?: "warn" | "error" | "ignore" } } - Default: invalid documented fields warn; unknown fields are ignored for compatibility.
Ardo validates its documented frontmatter fields once while it scans route metadata. Diagnostics name the source file and never feed malformed values into sidebar, search, sitemap, redirect, or LLM output. Turn strict mode on when a project wants misspelled metadata to fail CI:
defineArdoConfig({
title: "My Docs",
validation: {
frontmatter: {
invalid: "error",
unknown: "error",
},
},
})
Use unknown: "warn" while adopting strict mode in an established documentation set. Keep it at the default only when another tool intentionally owns custom frontmatter fields.
redirects
- Type:
Array<{ from: string; to: string }> - Default:
[]
Generates static redirect assets for configured legacy paths:
_redirectsfor Netlify-style hostsvercel.jsonredirect entries- HTML redirect files for static hosts that only serve files
defineArdoConfig({
title: "My Docs",
redirects: [
{ from: "/old-guide", to: "/guide/getting-started" },
{ from: "/v1/config", to: "/guide/configuration" },
],
})
Use frontmatter redirectFrom when a redirect belongs to one page instead of the whole site.
versioning
- Type:
false | { current: string; versions: Array<{ id: string; label?: string; path: string; locales?: string[] }>; rootRedirect?: boolean } - Default:
false
Enables a small major-version documentation model. Ardo builds the current docs under the active version path, exposes the configured versions to the version switcher, emits /versions.json, and generates a static /index.html redirect to the current version by default.
Each version can declare locales: omit it to preserve the current locale, use [] for an unlocalized archive, or list supported locale IDs to retain the current language when available and otherwise choose the first supported language. The version switcher sits at the end of the sidebar, in the footer of bare pages, and in the menu on narrow screens. List versions newest first: the first entry counts as the latest version, and the pages of every other version show a banner that links to the same page in the latest one.
defineArdoConfig({
title: "My Docs",
siteUrl: "https://docs.example.com",
versioning: {
current: "v3",
versions: [
{ id: "v3", label: "3.x", path: "/v3/" },
{ id: "v2", label: "2.x", path: "/v2/" },
],
},
})
When base is set, Ardo treats it as the deployment root and appends the version path. For example, base: "/my-project/" with path: "/v3/" produces canonical URLs under /my-project/v3/. The generated sitemap contains the current build only; older major versions can remain deployed as static folders without being advertised as duplicate current content.
Set rootRedirect: false if you want to provide your own root page or provider-level redirect. On static hosts such as GitHub Pages, Ardo's redirect is an HTML file with meta refresh and JavaScript. If you put a CDN or hosting layer with real HTTP redirects in front of the site, configure those redirects outside Ardo.
i18n
- Type:
false | { defaultLocale: string; locales: Array<{ id: string; label?: string }>; redirectUnlocalized?: boolean } - Default:
false
Ardo builds every locale as an explicit static route tree and renders a locale switcher when more than one locale is configured. Put the translated documents in app/routes/<locale>/; for example, app/routes/en/guide/getting-started.mdx and app/routes/de/guide/getting-started.mdx.
This is deliberately strict: every visible route must be present in every configured locale. The build fails for a missing translation or for a page outside a locale directory. There is no runtime fallback, so a localized URL never silently serves a different language. Search loads only the active locale. Ardo emits canonical and alternate-language links when siteUrl is configured. Translate UI labels through ArdoRoot and read the current locale with useArdoLocale(). See Internationalization for the complete Palamedes example.
When enabled, Ardo always includes the locale in canonical public URLs. With versioning active, the version segment comes first:
defineArdoConfig({
title: "My Docs",
versioning: {
current: "v3",
versions: [{ id: "v3", label: "3.x", path: "/v3/" }],
},
i18n: {
defaultLocale: "en",
locales: [
{ id: "en", label: "English" },
{ id: "de", label: "Deutsch" },
],
},
})
This produces URLs such as /v3/en/guide/getting-started. The root redirect points to the current version plus the default locale, and the version root redirects to the default locale root. The default locale is not hidden when i18n is active. The application shell is app/root.tsx; it is not a locale document. Set redirectUnlocalized: true to redirect existing unprefixed routes to the default locale in development and static builds.
outDir
- Type:
string - Default:
"build"
Base directory for build output, relative to the project root or an absolute path. Ardo writes its deployable static site to <outDir>/client/ and its build-time server bundle to <outDir>/server/. ardo preview uses the same configured client directory.
defineArdoConfig({
title: "My Docs",
outDir: "release/docs",
})
This writes the static site to release/docs/client/; deploy that directory instead of the default build/client/. The server bundle is only needed during the build.
Ardo clears both output directories by default. Use ardo build --no-emptyOutDir to retain existing files. An explicit build.emptyOutDir in vite.config.ts sets the same policy; an environment's environments.client.build.emptyOutDir or environments.ssr.build.emptyOutDir takes precedence.
lang
- Type:
string - Default:
"en"
Site language. Sets the lang attribute on the <html> element.
defineArdoConfig({
title: "My Docs",
lang: "de",
})
sidebar.sectionOrder
- Type:
string[] - Default:
[]
Controls the order of top-level sections used by ArdoGeneratedSidebar. Use route directory names, without leading slashes. Sections listed here appear first in the configured order. Any section not listed still appears automatically after the configured sections using the normal generated order.
defineArdoConfig({
title: "Product Docs",
sidebar: {
sectionOrder: ["guide", "reference", "api", "quality", "architecture", "archive"],
},
})
Child pages inside each section keep their own generated order from frontmatter order values and alphabetical fallback sorting.
head
- Type:
HeadConfig[] - Status: Ignored
Ardo 6 no longer renders these tags. Export meta from root.tsx or a route instead, typed as ArdoMetaFunction from ardo/runtime.
vite
- Type:
Record<string, unknown>
Reserved for Vite-adjacent integration data. Configure Vite-specific settings in an optional vite.config.ts; Ardo's CLI merges that file with its own integration.
project
- Type:
{ name?: string; homepage?: string; repository?: string; version?: string; author?: string; license?: string }
Project metadata exposed through virtual:ardo/config. The Vite plugin auto-detects these values from package.json when possible.
Integration Options
These options control route generation and other build integrations. Set them in ardo.config.ts with defineArdoConfig(). Existing Vite projects can keep passing the same options to ardo() while they migrate.
routes
- Type:
ArdoRoutesPluginOptions | false - Default: automatic
Configuration for the automatic route generation plugin. Set to false to disable auto-routing entirely.
icons
- Type:
false - Default: the generated owl icons
Ardo generates a lean favicon set from its owl at build time and serves it during development: /favicon.ico, /icon.svg, and /apple-touch-icon.png. Your own icon is an imported file on ArdoRoot, <ArdoRoot icon={icon} />, so Vite fingerprints it; see Theming. A file of the same name in public/ replaces the generated one, and icons: false leaves all three to you.
content
- Type:
Array<{ from: string; to: string; frontmatter?: (file) => Record<string, string | number | boolean | string[] | undefined> }> - Default:
[]
Materializes Markdown and MDX that lives outside the routes directory into generated route files. Use this for repo-native docs such as ADRs, CHANGELOG.md, or package READMEs that should remain in their canonical source location but still publish with the rest of the site.
defineArdoConfig({
title: "My Docs",
content: [
{
from: "./adr",
to: "decisions",
frontmatter: (file) => ({
description: `Copied from ${file.relativePath}`,
}),
},
{
from: "./CHANGELOG.md",
to: "changelog",
},
],
})
Ardo writes the generated files below routesDir/<to>/ and marks the directory with .ardo-generated. It refuses to replace a non-empty target directory without that marker. When a source file has no frontmatter, Ardo synthesizes title from the first # heading or filename and order from a numeric filename prefix such as 0001-.
When from points to a single file, Ardo writes it as routesDir/<to>/index.md or routesDir/<to>/index.mdx, so to: "changelog" becomes the /changelog route.
Content sources do not rewrite relative links or validate frontmatter. Use collections when the files also need a schema and typed records.
collections
- Type:
Record<string, { from: string; to: string; schema?: (data) => T }> - Default:
{}
Local Markdown and MDX that become routes, like content, and also typed records for custom React pages. schema validates each file's frontmatter at build time. Read the records with getCollection from virtual:ardo/collections.
import { defineArdoConfig } from "ardo/config"
import { defineCollection } from "ardo/vite"
const recipes = defineCollection<{ title: string }>({
from: "./recipes",
to: "recipes",
schema: (data) => ({ title: String(data.title) }),
})
export default defineArdoConfig({ title: "My Docs", collections: { recipes } })
Content Sources covers the full flow.
openapi
- Type:
{ spec: string; out?: string } - Default: none
Generates static reference pages from a local OpenAPI 3.0 or 3.1 file in JSON or YAML. out defaults to api. API Documentation covers the generated pages.
defineArdoConfig({
title: "Pet API",
openapi: { spec: "./openapi.yaml" },
})
routesDir
- Type:
string - Default:
"./app/routes"
Directory where route files are located.
defineArdoConfig({
title: "My Docs",
routesDir: "./src/routes",
})
Markdown
Markdown processing options are configured under the markdown key.
defineArdoConfig({
title: "My Docs",
markdown: {
theme: { light: "github-light-default", dark: "github-dark-default" },
lineNumbers: true,
},
})
markdown.theme
- Type:
ThemeInput | { light: ThemeInput; dark: ThemeInput }from@ferriki/core - Default:
{ light: "github-light-default", dark: "github-dark-default" }
Syntax highlighting theme. Ferriki accepts a theme name, a custom TextMate theme registration, or a light/dark pair. Standard themes are downloaded and cached on first use.
markdown: {
theme: "one-dark-pro",
}
markdown.lineNumbers
- Type:
boolean - Default:
false
Show line numbers in code blocks.
markdown.anchor
- Type:
boolean - Default:
true
Enable anchor links on headings.
markdown.toc
- Type:
{ level?: [number, number] } - Default:
{ level: [2, 3] }
Table of contents configuration. The level tuple specifies which heading levels to include (min, max).
markdown: {
toc: {
level: [2, 4], // Include h2, h3, and h4
},
}
markdown.passes
- Type:
NativePassOptions[]fromferromark - Default:
[]
Run Ferromark's native transforms in array order. The same transformed heading text supplies the rendered page, table of contents, search, and link validation.
markdown: {
passes: [
{ kind: "typography", language: "en" },
{ kind: "emojiShortcodes" },
{ kind: "githubReferences", repository: "owner/repository" },
],
}
markdown.typography is a shorthand for a typography pass. Configure either typography or passes; Ferromark rejects using both together.
Native Markdown syntax
Ferromark syntax options are configured directly under markdown. GFM tables, strikethrough, task lists, autolinks, footnotes, and callouts are enabled by default. Optional extensions include headingAttributes, imageAttributes, extendedAttributes, definitionLists, insertions, superscript, subscript, math, and lineComments.
headingOffset shifts rendered heading levels and TOC levels together. headingIdPrefix prefixes native IDs everywhere they are used. See the Ferromark Node API for option semantics and supported passes.
Ardo manages document format, YAML frontmatter, React component bindings, and removal of a matching first page-title heading. The former remark, rehype, and recma plugin options have been removed; see Upgrade to Ardo 5.
API Documentation
The api option generates source API documentation through native TypeScript and OXC during builds and development. Pass true for src/index.ts and tsconfig.json, an object for one project, or an array for multiple projects. Object configuration requires entryPoints and tsconfig; out defaults to api-reference and title to API Reference. Multiple outputs must be distinct and must not contain one another.
See API Documentation for installation, configuration, programmatic use, and current limits. The previous typedoc option and package exports have been removed; see the TypeDoc migration guide.
UI Configuration
Navigation, sidebar sections, footer, editLink, lastUpdated, and search are configured with JSX in your root.tsx using <ArdoRoot>. See Layout & Navigation for the ArdoRoot, header, footer, and sidebar-section reference.
Package Exports
Ardo keeps browser-safe runtime imports separate from build-time Node.js imports:
| Import path | Use it for |
|---|---|
ardo | Types plus the runtime and UI exports in one entry. It includes Node-only code; browser code imports ardo/runtime or ardo/ui. |
ardo/api | Native API projects, one-shot generation, declaration extraction, and documentation types. |
ardo/config | defineArdoConfig and the config types. |
ardo/vite | The ardo() Vite plugin, Vite's defineConfig, defineCollection, highlightCode, and build helpers. |
ardo/runtime | React providers, runtime hooks, ArdoLink, useArdoPath, ArdoHead, ArdoScripts, ArdoMetaFunction, and sidebar prev/next helpers. |
ardo/ui | Default UI components, layout components, MDX components, ArdoTheme, and icon registration helpers. |
ardo/icons | The 50 built-in content icons and the brand icons as React components. |
ardo/theme | vars, the Vanilla Extract theme tokens for your own styles. |
ardo/mdx-provider | The MDX provider used to auto-register Ardo's MDX components. |
ardo/virtual | Type declarations for Ardo virtual modules. |
ardo/react | React APIs and types from Ardo's managed React installation. |
ardo/react-dom | React DOM APIs, with /client, /server, and /static subpaths. |
ardo/ui/styles.css | The default Ardo UI stylesheet. Every site loads it; import it yourself only to control cascade order. |
ardo/tsconfig | TypeScript preset for the docs package: "extends": "ardo/tsconfig". |
Frontmatter
Per-page configuration is done through frontmatter in your MDX files. See the dedicated Frontmatter page for all available options.
Vite Plugin Compatibility Example
This form remains supported for projects that manage Vite directly. New Ardo sites should use ardo.config.ts and only add a Vite config for custom Vite integrations. Use this form instead of ardo.config.ts, and import defineConfig from Ardo so a separate Vite dependency is unnecessary. Add optional API generation through the API Documentation guide.
import { defineConfig } from "ardo/vite"
import { ardo } from "ardo/vite"
export default defineConfig({
plugins: [
ardo({
title: "My Library",
description: "Comprehensive documentation for My Library",
lang: "en",
markdown: {
theme: { light: "github-light-default", dark: "github-dark-default" },
lineNumbers: false,
toc: { level: [2, 3] },
},
}),
],
})