Content Sources
Publish Markdown and MDX outside app/routes while preserving one canonical source file.
On this page
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 { defineArdoConfig } from "ardo/config"
export default defineArdoConfig({
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 map local files; schema-backed collections add validation and typed records on top.
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 { 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: "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.
Add these options to the version-matched Basic example. The authored files stay canonical: the build generates the /recipes/* pages from them, and a custom React route can render the same records.