Getting started
Install docvia, declare a docs collection in code, and render your first page.
On this page
docvia is distributed as a set of @docvia/* packages on npm. A bundler
plugin compiles your Markdown, @docvia/core/source gives you the typed page
loader, and a renderer package turns pages into framework components.
One command
Inside a Next.js, SvelteKit or TanStack Start app:
pnpm dlx @docvia/cli init
It detects the framework, package manager and import aliases, writes the pages, routes, search endpoint and bundler config, and installs the packages. See the CLI guide for what it writes. The rest of this page is the same setup by hand.
Install
pnpm add -D @docvia/build # or
pnpm add @docvia/core # or
The framework plugin (which also exports defineConfig) is a dev dependency.
@docvia/core/source and the renderer are runtime dependencies: your pages import
them.
Configure
docvia.config.ts is optional. Without it the renderer is picked from your
dependencies and Shiki is enabled when @docvia/plugin-shiki is installed. Add
one to register components or set plugins and Markdown options:
import { defineConfig } from "@docvia/build/vite"; // or @docvia/build/next
import { createReactRenderer } from "@docvia/core/react";
import { shiki } from "@docvia/plugin-shiki";
export default defineConfig({
renderer: createReactRenderer(),
// Highlighted HTML is baked into the IR; no highlighter ships to the browser.
plugins: [shiki({ theme: "github-dark" })],
});
Then add the bundler plugin: docvia() in vite.config.ts, or withDocvia()
in next.config.ts. See Framework integration.
Declare a collection
Collections are declared in code. The bundler plugin rewrites defineDocs() at
build time into an index of the folder's frontmatter plus one lazy import per
page body:
import { loader } from "@docvia/core/source";
import { defineDocs } from "@docvia/core/source/macro";
import { z } from "zod";
const docs = defineDocs({
dir: "content/docs",
// Optional: extra frontmatter fields, any Standard Schema library.
docs: { schema: z.object({ author: z.string().optional() }) },
});
export const source = loader({ baseUrl: "/docs", source: docs.toDocviaSource() });
dir must be a string literal and defaults to content/docs. Components
registered in docvia.config.ts come from a second macro:
import { defineRegistry } from "@docvia/core/source/macro";
export const registry = defineRegistry();
Render a page
getPage() is synchronous and returns frontmatter only. The compiled body
loads on demand:
const page = source.getPage(slugs);
if (!page) notFound();
const { content, toc, headings, manifest, structuredData } = await page.data.load();
Pass content to the renderer (<Renderer nodes={content} registry={registry} />
in React, <Renderer nodes={content} {registry} /> in Svelte). Build the
sidebar from source.pageTree, and pre-render routes with
source.generateParams().
flowchart LR
C["docvia.config.ts"] --> P["docvia() / withDocvia()"]
M["defineDocs()<br/>lib/source.ts"] --> P
P --> L["loader()<br/>getPage · pageTree"]
L --> D["page.data.load()"]
D --> R["<Renderer nodes={content} />"] Nothing is written to disk: there is no .docvia/ folder and no sync step.
Pages compile lazily in memory, keyed by content hash, in dev and in
production builds.
Order the sidebar
Add a meta.json to any content folder to set its title and child order:
{
"title": "Guide",
"pages": ["install", "---Usage---", "...", "!drafts", "[GitHub](https://github.com/kanakkholwal/docvia)"],
"defaultOpen": true
}
... inserts the remaining pages, ---Label--- adds a separator, !name
excludes an entry, and [Text](url) adds a link. root: true marks the folder
as a separate sidebar root.
Every config option is documented in the Configuration reference.