Skip to content

Content Sources

Publish Markdown and MDX outside app/routes while preserving one canonical source file.

Use content sources when the Markdown you want to publish already belongs elsewhere in a repository: ADRs, package guides, changelogs, or shared documentation. Ardo materializes a read-only route view during the build while the original file remains canonical.

Map a Directory

import { defineConfig } from "vite"
import { ardo } from "ardo/vite"

export default defineConfig({
  plugins: [
    ardo({
      title: "Workspace Docs",
      content: [{ from: "../../adr", to: "decisions" }],
    }),
  ],
})

An ../../adr/0001-routing.md source becomes /decisions/0001-routing. Existing frontmatter stays intact; otherwise Ardo derives a title from the first heading or filename and uses a numeric filename prefix as sidebar order.

What Participates

Mapped Markdown and MDX become ordinary generated routes. They participate in the sidebar, section search, sitemap, LLM text output, redirects, internal-link checking, and frontmatter validation.

Generated-File Boundary

Ardo writes mapped files under the selected to directory and marks it as generated. Edit the original source, never the materialized route. The plugin refuses to delete a non-empty handwritten target directory, so use a dedicated prefix such as decisions/.

Boundaries

Content sources intentionally do not fetch remote URLs, act as a CMS, or transform arbitrary file types. They are a local-filesystem bridge while schema-backed collections evolve.

Schema-Backed Collections

Use collections when the same local Markdown set should both materialize into routes and be available to a custom React route as typed build data:

import { ardo, defineCollection } from "ardo/vite"

const recipes = defineCollection<{ title: string }>({
  from: "../../recipes",
  to: "recipes",
  schema: (data) => ({ title: String(data.title) }),
})

ardo({ title: "Docs", collections: { recipes } })

Import getCollection<T>() from virtual:ardo/collections in a custom route to read typed generated records:

import { getCollection } from "virtual:ardo/collections"

const recipes = getCollection<{ title: string }>("recipes")

Collections remain local and static: the schema runs during the build, while the generated route pages continue through the normal sidebar, search, sitemap, redirect, and LLM pipelines. Source paths are project-relative, so the virtual module does not expose an author's absolute filesystem paths.

The external content and collections reference builds this complete shape: project-content/recipes/ remains canonical, /recipes/* is generated, and a custom /catalog React route renders the same collection records.