Skip to content
CtrlK

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: "Ardo"

The site title, used in the default <title> meta tag and displayed in the header. Set it in ardo.config.ts when you want a custom title.

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",
  },
})

brand

  • Type: { color?: number | Preset; accent?: number | Preset; neutral?: number | Preset; chroma?: number | { color?: number; accent?: number; neutral?: number }; logo?: string | { light: string; dark: string } }
  • Default: {}

Simple brand configuration for the default theme and header logo. color, accent, and neutral accept OKLCH hue numbers or preset names: berry, red, orange, amber, green, teal, blue, indigo, purple, pink, slate, and gray.

defineArdoConfig({
  title: "My Docs",
  brand: {
    color: "blue",
    accent: "teal",
    neutral: "slate",
    logo: "./app/assets/logo.svg",
  },
})

chroma scales the saturation: 1 keeps the default, 0.4 mutes the palette, and 0 turns it gray. A number applies to color and accent; an object sets each hue separately. Theming shows the effect.

When color is set without accent, Ardo derives an accent hue. When neutral is omitted, the neutral chrome follows color. Local SVG logo paths are bundled for the default header and also become the default source for generated favicons. Use an explicit logo on ArdoHeader or icons.source when the header and favicon should use different assets.

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.txt is a concise Markdown index of the Markdown and MDX routes. It follows the sidebar: one heading per section, in tab order, and pages by order, so agents read the guide before the reference. A generated API or OpenAPI reference appears as one link to its index page.
  • /llms-full.txt contains 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:

  • _redirects for Netlify-style hosts
  • vercel.json redirect 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 }>; 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.

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 }> }
  • 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.

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. Routes such as app/routes/root.tsx remain the application shell and are not locale documents.

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",
})
  • 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.

  • 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 | { source?: string }
  • Default: automatic Ardo icon set

Ardo generates a lean favicon set at build time and serves it during development: /favicon.ico, /icon.svg, and /apple-touch-icon.png. This matches the modern minimal browser set without adding a web app manifest or legacy apple-touch-icon-precomposed.png files.

defineArdoConfig({
  title: "My Docs",
  icons: {
    source: "./app/assets/logo.svg",
  },
})

Set icons: false if you want to manage all root icon files yourself from public/.

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[] from ferromark
  • 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 pathUse it for
ardoTypes plus the runtime and UI exports in one entry. It includes Node-only code; browser code imports ardo/runtime or ardo/ui.
ardo/apiNative API projects, one-shot generation, declaration extraction, and documentation types.
ardo/configdefineArdoConfig and the config types.
ardo/viteThe ardo() Vite plugin, Vite's defineConfig, defineCollection, highlightCode, and build helpers.
ardo/runtimeReact providers, runtime hooks, ArdoLink, useArdoPath, ArdoHead, ArdoScripts, ArdoMetaFunction, and sidebar prev/next helpers.
ardo/uiDefault UI components, layout components, MDX components, and icon registration helpers.
ardo/iconsThe 50 built-in content icons and the brand icons as React components.
ardo/themeVanilla Extract theme tokens and brand-theme helpers.
ardo/mdx-providerThe MDX provider used to auto-register Ardo's MDX components.
ardo/virtualType declarations for Ardo virtual modules.
ardo/reactReact APIs and types from Ardo's managed React installation.
ardo/react-domReact DOM APIs, with /client, /server, and /static subpaths.
ardo/ui/styles.cssThe default Ardo UI stylesheet. Every site loads it; import it yourself only to control cascade order.
ardo/tsconfigTypeScript 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] },
      },
    }),
  ],
})
Edit this page on GitHubLast updated: October 24, 2026