.json` files:
```json "translations/fr.json"
{
"cookieMessage": "Cette page utilise des cookies pour vous offrir la meilleure expérience.",
"cookieAccept": "Accepter",
"cookieDecline": "Refuser",
"cookiePolicy": "Politique de confidentialité",
"cookieConsent": "Consentement aux cookies"
}
```
## Reacting to a choice
A `CustomEvent` named `docmd:cookie-consent` is dispatched on `window` after the user accepts, declines, or dismisses:
```js
window.addEventListener('docmd:cookie-consent', (e) => {
if (e.detail.value === 'accept') {
// Load analytics, marketing scripts, etc.
}
});
```
The `detail.value` is one of `"accept"`, `"decline"`, or `"dismissed"`. If you need to read the choice synchronously (e.g. before any other script runs), `localStorage.getItem('docmd-cookie-consent')` returns the same payload.
## Re-styling
The dialog is built from BEM-style classes on the `.docmd-cookie-banner` root. Re-skin it via `customCss` (always wins at priority 15):
```css
.docmd-cookie-banner {
--accent-color: #ff5a5f;
border-radius: 16px;
}
.docmd-cookie-banner__btn--accept {
background-color: var(--accent-color);
border-color: var(--accent-color);
}
```
## Disabling
To remove the dialog entirely, simply remove the `cookie` key from your config. There is no plugin to disable.
::: callout tip "GDPR best practice"
If you are subject to GDPR, leave the dialog **enabled by default** and link to a real privacy policy via `policyUrl`. The default message is intentionally generic so you can supply your own via `message` or the translation system.
:::
---
## [Layout & UI Zones](https://docs.docmd.io/configuration/layout-ui/)
---
title: "Layout & UI Zones"
description: "Control the interface structure by managing headers, sidebars, and functional UI slots."
---
A standard page contains six primary functional zones:
1. **Menubar**: A full-width top navigation bar for global site links.
2. **Header**: A persistent secondary bar. It contains the page title and utility buttons.
3. **Sidebar**: The primary navigation tree, usually on the left.
4. **Content Area**: The central Markdown rendering zone. Includes **Breadcrumbs**.
5. **Table of Contents (TOC)**: Right-hand heading navigation for the current page.
6. **Footer**: Bottom area for copyright, branding, and site-wide links.
## Global Component Configuration
The engine uses a modular layout system. Configure most UI zones in the `layout` section of your `docmd.config.json`.
### Menubar
The menubar provides a high-level navigation layer. It supports brand titles, regular links, and nested dropdowns.
* **Location**: Fixed at the `top` or inline within the `header`.
* **Documentation**: See [Menubar Configuration](menubar.md) for schemas and styling.
### The Page Header
The header displays the page title, breadcrumbs, and utility menus.
* **Controls**: Enable or disable the header globally via `layout.header`. Toggle breadcrumbs via `layout.breadcrumbs`.
* **Overriding**: Use `hideTitle: true` in your [Page Frontmatter](../content/frontmatter.md) to hide the title area locally.
### Copy Widgets
The breadcrumbs bar includes two copy buttons. One copies the page's raw Markdown, the other copies a structured context block that includes the URL, title, and description. Useful for pasting into AI chat windows or support tickets.
Configure these buttons under `theme.copyWidgets` in your `docmd.config.json`:
```json "docmd.config.json"
{
"theme": {
"copyWidgets": {
"enabled": true,
"raw": true,
"context": true
}
}
}
```
* `enabled`: Set to `false` to disable the bar completely.
* `raw`: Set to `false` to hide the "Copy Markdown" button.
* `context`: Set to `false` to hide the "Copy Context" button.
### Utility Menus (Options Menu)
The `optionsMenu` groups core utilities like **Global Search**, **Theme Toggle**, and **Sponsorship links**.
```json "docmd.config.json"
{
"layout": {
"optionsMenu": {
"position": "header",
"components": {
"search": true,
"themeSwitch": true,
"sponsor": "https://github.com/sponsors/mgks"
}
}
}
}
```
::: callout info "Automatic Fallback" icon:sparkles
If the chosen position targets a disabled container, the engine moves the options menu to `sidebar-top`. This ensures utilities remain accessible.
:::
### Sidebar & Navigation
The sidebar is the primary navigation tree. Define its structure in your config or external JSON files.
* **Behaviour**: Supports animations, collapsible groups, and automatic path preservation.
* **Documentation**: See [Navigation Configuration](navigation.md).
### Footer
The engine provides **minimal** and **complete** layouts for your site footer.
```json "docmd.config.json"
{
"layout": {
"footer": {
"style": "complete",
"description": "Documentation built with docmd.",
"branding": true,
"columns": [
{
"title": "Community",
"links": [
{ "text": "GitHub", "url": "https://github.com/docmd-io/docmd" }
]
}
]
}
}
}
```
::: callout tip "Interface Hierarchy" icon:lightbulb
Use the menubar for global links and the sidebar for documentation structure. This separation keeps the navigation predictable for both human readers and crawlers.
:::
---
## [Localisation](https://docs.docmd.io/configuration/localisation/)
---
title: "Localisation"
description: "Serve documentation in multiple languages with locale-first routing, translated navigation, and automatic fallback."
---
Add multi-language support to your documentation site. docmd serves each locale at its own URL prefix, translates system UI strings, and falls back gracefully when a translation is missing.
## Add languages to your config
```json "docmd.config.json"
{
"i18n": {
"default": "en",
"locales": [
{ "id": "en", "label": "English" },
{ "id": "hi", "label": "हिन्दी" },
{ "id": "zh", "label": "中文" }
]
}
}
```
The `default` locale renders at the site root (`/`). All other locales render at `/{id}/`. You choose the IDs, labels, and which locale is the default - there are no hardcoded assumptions. If you want Hindi as the default, set `default: 'hi'` and Hindi renders at `/` whilst English renders at `/en/`.
| Key | Type | Description |
|:----|:-----|:------------|
| `default` | `string` | Locale ID that renders at `/`. Defaults to the first locale if omitted. |
| `locales` | `array` | List of locale objects. Each must have an `id`. |
| `position` | `string` | Where the language switcher appears. `options-menu` (default), `sidebar-top`, or `sidebar-bottom`. |
| `stringMode` | `boolean` | When `true`, generates locale pages from a single source using `data-i18n` attribute replacement. Default `false`. |
| `inPlace` | `boolean` | When `true` (with client-side script), swaps strings without URL navigation. For SPAs/dashboards only. Default `false`. |
Each locale object accepts:
| Key | Type | Default | Description |
|:----|:-----|:--------|:------------|
| `id` | `string` | - | Any identifier you choose (e.g. `en`, `hi`, `fr-ca`). Used as the folder name and URL prefix. Required. |
| `label` | `string` | Same as `id` | Display name shown in the language switcher. |
| `dir` | `string` | `ltr` | Text direction. Set to `rtl` for Arabic, Hebrew, etc. |
| `translations` | `object` | `{}` | Custom UI string overrides (see [Custom UI strings](ui-strings.md)). |
## URL structure
The default locale has no URL prefix. Non-default locales are nested under `/{id}/`. When combined with [versioning](../versioning.md), the URL is `/{locale}/{version}/page`.
```
/ ← default locale, current version
/getting-started ← default locale page
/05/ ← default locale, old version
/hi/ ← non-default locale, current version
/hi/getting-started ← non-default locale page
/hi/05/ ← non-default locale, old version
```
The language switcher preserves your current page and version when you switch locales. The version switcher preserves your current locale.
## Missing locale directories
If a locale is declared in `locales` but its source directory does not exist (e.g. no `docs/hi/` folder), docmd automatically **disables** that locale in the language switcher. The locale still appears in the dropdown - with an "N/A" badge and greyed-out styling - but clicking it does nothing.
This prevents 404 errors when you list planned languages before their content is ready.
## Position the language switcher
Control where the language switcher appears using the `position` option:
```json "docmd.config.json"
{
"i18n": {
"position": "sidebar-top"
}
}
```
| Position | Behaviour |
|:---------|:----------|
| `options-menu` | Compact globe icon alongside theme toggle and search. Default. |
| `sidebar-top` | Full dropdown with label at the top of the sidebar. |
| `sidebar-bottom` | Full dropdown with label at the bottom of the sidebar. |
## String Mode (noStyle pages only)
Standard i18n uses separate directories per locale (`docs/en/`, `docs/hi/`), each with its own markdown files. **String Mode** is a simpler alternative designed specifically for [noStyle pages](../../content/no-style-pages.md) - pages that use raw HTML instead of markdown.
```json "docmd.config.json"
"i18n": {
"default": "en",
"stringMode": true,
"locales": [
{ "id": "en", "label": "English" },
{ "id": "zh", "label": "中文" }
]
}
```
With `stringMode: true`:
1. Source files stay in the root `docs/` directory (no locale subdirectories)
2. The default locale builds at `/` as normal
3. For each non-default locale, docmd clones the rendered HTML and applies **server-side string replacement** using JSON files from `assets/i18n/{locale}.json`
4. Output goes to `/{locale}/` - e.g. `/zh/index.html` - with full SEO (hreflang tags, correct `lang` attribute)
5. If a translation file is missing, the page renders with the default language text
For full details on the `data-i18n` attribute syntax and JSON file format, see [noStyle string replacement](../../content/no-style-pages.md#string-replacement-i18n-for-nostyle).
::: callout warning "String Mode does not translate markdown content" icon:info
String replacement works by finding `data-i18n` attributes in the rendered HTML. Standard markdown content (`## Heading`, paragraphs, lists) renders to plain HTML tags without these attributes - so there is nothing for the replacer to find.
- **Documentation sites** → use directory mode (the default). Each locale has its own markdown files with fully translated prose.
- **Landing pages, marketing sites, dashboards** → use string mode. These are noStyle pages with custom HTML where you control every tag and can add `data-i18n` attributes.
If your site has both - for example, a noStyle landing page plus documentation - use directory mode for the docs and add `data-i18n` attributes to your noStyle page. String mode will translate the noStyle HTML while directory mode handles the documentation content.
:::
## Next steps
- [Translated content](translated-content.md) - directory structure, writing translations, navigation
- [UI strings & SEO](ui-strings.md) - customising system text, hreflang tags
- [noStyle string replacement](../../content/no-style-pages.md#string-replacement-i18n-for-nostyle) - `data-i18n` attribute syntax and JSON format for noStyle pages
---
## [Translated Content](https://docs.docmd.io/configuration/localisation/translated-content/)
---
title: "Translated Content"
description: "Organise translations in locale subdirectories with per-file fallback and per-locale navigation."
---
## Directory Structure
Every locale lives in its own subdirectory inside the source directory. The folder name matches the locale `id` from your config.
```text
docs/
├── en/ ← default locale content
│ ├── index.md
│ ├── navigation.json
│ └── getting-started/
│ └── installation.md
├── hi/ ← second locale
│ ├── index.md ← translated homepage
│ ├── navigation.json ← translated navigation labels
│ └── getting-started/
│ └── installation.md ← translated page
└── zh/ ← third locale
└── index.md ← only the homepage translated
```
The source directory holds only locale folders. No content files sit at the root level when i18n is enabled.
::: callout info "Folder Names Are Your Choice" icon:info
Folder names match the `id` values in your config. If your config sets `{ id: 'fr-ca' }`, your folder is `docs/fr-ca/`.
:::
## Per-file Fallback
You do not need to translate every page. docmd scans the **default locale directory** as the canonical structure. For every other locale, it checks for a translated page:
- If `docs/hi/getting-started/installation.md` exists → serves the Hindi translation.
- If it does not exist → serves the default locale version.
When a page falls back, docmd displays a translated callout. This informs viewers the page is shown in the default language. Customise this message via your [UI strings](ui-strings.md) configuration.
## Locale-Exclusive Pages
A non-default locale can host pages that do not exist in the default locale. These render only for that specific locale.
## Translate the Navigation
Each locale directory can include its own `navigation.json`. docmd uses a cascading priority system to resolve the sidebar.
For details on the resolution hierarchy, see [Navigation Configuration](../navigation.md).
A locale's `navigation.json` uses the standard format:
```json "navigation.json"
[
{
"title": "शुरू करें",
"children": [
{ "title": "इंस्टालेशन", "path": "/getting-started/installation" },
{ "title": "स्थानीयकरण", "path": "/configuration/localisation" }
]
}
]
```
::: callout tip "Partial Navigation" icon:info
Create a locale `navigation.json` only when you want translated labels. If missing, the default navigation is used.
:::
## Versioning and i18n
When combining versioning and i18n, structure the source directories hierarchically:
```text
docs/ ← current version
en/ ← current version, default locale
hi/ ← current version, translated locale
docs-v1/ ← previous version
en/ ← v1, default locale
hi/ ← v1, translated locale
```
The output URLs nest locale first, then version:
```text
/ ← default locale, current version
/hi/ ← translated locale, current version
/v1/ ← default locale, previous version
/hi/v1/ ← translated locale, previous version
```
---
## [UI Strings & SEO](https://docs.docmd.io/configuration/localisation/ui-strings/)
---
title: "UI Strings & SEO"
description: "Customise system UI text per locale and understand automatic SEO tags for multi-language sites."
---
## Built-in Language Support
docmd and its official plugins ship with built-in translations for common languages. When you configure a supported locale, the engine automatically translates system text like search placeholders, navigation labels, and theme toggles.
For unsupported languages or custom phrasing, the system falls back to English. You can override any string per locale.
## Custom UI Strings
Use the `translations` property on any locale to override system text:
```json "docmd.config.json"
"i18n": {
"default": "en",
"locales": [
{ "id": "en", "label": "English" },
{
"id": "ar",
"label": "العربية",
"dir": "rtl",
"translations": {
"onThisPage": "في هذه الصفحة",
"previous": "السابق",
"next": "التالي",
"search": "بحث",
"toggleTheme": "تبديل المظهر",
"editThisPage": "تعديل هذه الصفحة",
"selectLanguage": "اختر اللغة",
"selectVersion": "اختر الإصدار",
"fallbackMessage": "هذه الصفحة غير متاحة بعد باللغة {active}. عرض اللغة الافتراضية ({default})."
}
}
]
}
```
The merge order is: **system translations → plugin translations → your config translations**. Your config always wins.
## Available Keys
Instead of hardcoding a list of available keys, you can review the complete set of supported languages and translation keys directly in the docmd source repository.
**[View Translation Source on GitHub](external:https://github.com/docmd-io/docmd/tree/main/packages/ui/translations)**
The `fallbackMessage` key supports `{active}` and `{default}` placeholders. The engine replaces these with locale labels at build time.
## SEO and Hreflang
docmd automatically generates ` ` tags for every page across all locales. The default locale also receives the `x-default` hreflang value.
```html
```
No configuration is required. The engine injects these tags into every page when i18n is enabled.
::: callout info "noStyle Pages" icon:info
The UI strings system applies to themed layout pages. For noStyle pages using custom HTML, see [Client-Side String Replacement](../../content/no-style-pages.md#string-replacement-i18n-for-nostyle).
:::
---
## [Menubar](https://docs.docmd.io/configuration/menubar/)
---
title: "Menubar"
description: "Structure and position your menubar, manage navigation links, and configure drop-down menus."
---
The `menubar` is a premium navigation layer. It provides global context across your site. Position it as a fixed bar at the viewport top or relatively above the page header.
## Configuration
Configure the menubar in the `layout` section of your `docmd.config.json`.
```json "docmd.config.json"
{
"layout": {
"menubar": {
"enabled": true,
"position": "top",
"left": [
{ "type": "title", "text": "Brand", "url": "/", "icon": "home" },
{ "text": "Documentation", "url": "/docs" },
{
"type": "dropdown",
"text": "Ecosystem",
"items": [
{ "text": "GitHub", "url": "https://github.com/docmd-io/docmd" },
{ "text": "Live Editor", "url": "https://live.docmd.io" }
]
}
],
"right": [
{ "text": "Support", "url": "/support", "icon": "help-circle" }
]
}
}
}
```
### Options
| Option | Type | Default | Description |
| :--- | :--- | :--- | :--- |
| `enabled` | `Boolean` | `false` | Toggles menubar visibility. |
| `position` | `String` | `'top'` | `'top'` (fixed at absolute top) or `'header'` (positioned above the page title). |
| `left` | `Array` | `[]` | Left-aligned navigation items. |
| `right` | `Array` | `[]` | Right-aligned navigation items. |
## Item Types
The `left` and `right` arrays support various item types.
### 1. Standard Link
The most common item type.
- `text`: Display label.
- `url`: Path or external URL.
- `icon`: Optional Lucide icon name.
- `external`: Set to `true` to open in a new tab.
### 2. Title (Brand)
Set `type: 'title'` to apply branding styles (e.g. bold fonts).
### 3. Dropdown Menu
Set `type: 'dropdown'` and provide an `items` array to create a nested menu.
## Utility Integration
Host the global search and theme toggle in the menubar. Set `optionsMenu.position` to `'menubar'`.
```json "docmd.config.json"
{
"layout": {
"optionsMenu": {
"position": "menubar"
}
}
}
```
The options menu automatically aligns to the **right region**. It appears after any links defined in the `right` array.
::: callout info "Automatic Fallback"
If the `menubar` is disabled, assigned utilities automatically fall back to the `sidebar-top` position.
:::
## Custom Styling
Use CSS variables in your custom stylesheets to override the menubar appearance. See [Custom CSS & JS](../theming/custom-css-js.md) for details.
```css
:root {
--menubar-h: 56px;
--menubar-bg: var(--bg-color);
--menubar-border: var(--border-color);
--menubar-text: var(--text-color);
}
```
---
## [Navigation Configuration](https://docs.docmd.io/configuration/navigation/)
---
title: "Navigation Configuration"
description: "Structure your sidebar, categorise links, and configure icons for readers and search engines."
---
The compiler provides explicit control over your site navigation. A clear navigation hierarchy creates a logical reading sequence. This optimises the SPA experience and provides a clear context map for search indexing and AI models.
## 1. The Navigation Schema
An array of link objects in your `docmd.config.json` file controls the sidebar. Each object is a direct link or a nested category group.
```json "docmd.config.json"
{
"navigation": [
{ "title": "Overview", "path": "/", "icon": "home" },
{ "title": "Quick Start", "path": "/getting-started/quick-start", "icon": "rocket" }
]
}
```
## 2. Supported Properties
Every item supports these settings:
| Property | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| `title` | `String` | Yes | The text displayed in the sidebar menu. |
| `path` | `String` | No | Target URL. Relative local paths must begin with a forward slash (`/`). |
| `icon` | `String` | No | Name of any [Lucide Icon](external:https://lucide.dev/icons) in kebab-case format (e.g., `git-branch`). |
| `children` | `Array` | No | An array of nested navigation items to establish a submenu. |
| `collapsible`| `Boolean`| No | When `true`, the user can expand or collapse the category folder. |
| `external` | `Boolean`| No | When `true`, opens the link in a new browser tab. |
## 3. Organising Section Groups
Structure your sidebar using two primary grouping methods:
### Clicking Group (Direct Page + Child Folders)
Specify a `path` along with `children` for a category header. Clicking the title loads the landing page and expands the child links.
```json "docmd.config.json"
{
"title": "Cloud Services",
"path": "/cloud/overview",
"children": [
{ "title": "AWS Setup", "path": "/cloud/aws" },
{ "title": "GCP Setup", "path": "/cloud/gcp" }
]
}
```
### Static Label (Category Headers Only)
Omit the `path` parameter. The header serves as a non-clickable title grouping related links. Use this to divide major technical categories without a single landing page.
```json "docmd.config.json"
{
"title": "Formatting & Elements",
"icon": "layout-grid",
"children": [
{ "title": "Syntax Guide", "path": "/content/syntax" },
{ "title": "Rich Containers", "path": "/content/containers" }
]
}
```
## 4. Automated Breadcrumbs
The engine automatically generates contextual breadcrumbs for every page. These display directly above the main page header to assist with rapid orientation.
### Key Behaviours
* **Automatic Resolution**: The engine traces the active route through your navigation tree to construct the hierarchy.
* **Active Indicator**: The current page is the final, unlinked breadcrumb item.
* **Mobile Optimisation**: Breadcrumbs simplify or hide dynamically on small viewports to save screen space.
### Disabling Breadcrumbs
Breadcrumbs are enabled by default. Update your site layout options to disable them globally:
```json "docmd.config.json"
{
"layout": {
"breadcrumbs": false
}
}
```
## 5. Navigation Resolution Cascading
The compiler uses a "closest file wins" cascading resolution system. This supports multiple versions or languages without bloating your global configuration.
```text
my-project/
├── docmd.config.json [Level 3: Global Config] - Default Fallback
├── docs-v1.0/
│ ├── navigation.json [Level 2: Version Navigation] - Overrides Global
│ └── zh/
│ └── navigation.json [Level 1: Language Navigation] - Absolute Priority
```
1. **Level 1: Language-Specific** (`navigation.json` inside a locale folder): Overrides all settings for this specific language and version.
2. **Level 2: Version-Specific** (`navigation.json` inside a version folder): Overrides global configuration for this version across all languages.
3. **Level 3: Global Configuration** (`config.navigation`): The base fallback definition in the central configuration file.
### Smart Broken-Link Prevention
The engine automatically checks if targeted files exist during Level 2 or 3 navigation fallback. Missing files are filtered out of the sidebar dynamically. This eliminates broken links for older versions or missing translations.
## 6. Icon Integration
The compiler includes the complete **Lucide Icon** system. Use the official Lucide name in kebab-case format (e.g., `settings`, `folder-open`, `book-marked-line`) to apply an icon.
::: callout tip "Optimising Sidebar Labels" icon:sparkles
Keep sidebar titles clear and descriptive. A concise navigation structure allows AI agents to parse your site map easily from the compiled `llms.txt` feed.
:::
---
## [General Configuration](https://docs.docmd.io/configuration/overview/)
---
title: "General Configuration"
description: "Configure docmd.config.json to manage branding, custom schemas, routing, layout behaviour, and build engines."
---
The `docmd.config.json` file is the central configuration for your workspace. It controls site styling, sidebar hierarchies, localisation details, and compiler options.
## 1. The Configuration Schema
JSON is the standard configuration format. This allows high-performance serialisation across the engine's worker pools.
However, `docmd.config.js` and `docmd.config.ts` remain fully supported if you need dynamic JavaScript logic.
```json "docmd.config.json"
{
"title": "My Project",
"url": "https://docs.myproject.com",
"src": "docs",
"out": "site",
"base": "/"
}
```
## 2. Core Settings
These top-level parameters configure the compiler's base inputs and destinations.
| Parameter | Type | Default | Description |
| :--- | :--- | :--- | :--- |
| `title` | `String` | `"Documentation"` | The formal name of your site. Appears in navigation headers and browser title tabs. |
| `url` | `String` | `""` | Your canonical production URL. Critical for SEO validation, Sitemap indexing, and OpenGraph metadata. |
| `src` | `String` | `"docs"` | Relative path to the folder containing your source Markdown (.md) files. |
| `out` | `String` | `"site"` | Relative path where the compiler writes the optimised production static site. |
| `base` | `String` | `"/"` | The root base path of your site (e.g., set to `/docs/` if hosting in a subfolder). |
| `tmp` | `String` | `null` | Custom directory for temporary compile files and caching. Defaults to an isolated system temp folder. |
| `i18n` | `Object` | `null` | Multi-language parameters. See the [Localisation Guide](localisation/translated-content.md). |
| `plugins` | `Object` | `{}` | Key-value mapping to configure standard and custom plugins. See [Plugins Guide](../plugins/usage.md). |
| `engine` | `String` | `"js"` | The active processing engine: `"js"` or `"rust"` (preview). |
## 3. Branding & Identity
Manage how your brand appears in the header and browser tabs.
```json "docmd.config.json"
{
"logo": {
"light": "assets/images/logo-dark.png",
"dark": "assets/images/logo-light.png",
"href": "/",
"alt": "Company Logo",
"height": "32px"
},
"favicon": "assets/favicon.ico"
}
```
## 4. UI Layout and Behaviour
The engine provides a modular header and sidebar layout. Customise functional regions. Toggle component visibility (search, dark-mode switch, breadcrumbs).
```json "docmd.config.json"
{
"layout": {
"spa": true,
"header": {
"enabled": true
},
"sidebar": {
"collapsible": true,
"defaultCollapsed": false
},
"optionsMenu": {
"position": "header",
"components": {
"search": true,
"themeSwitch": true
}
}
}
}
```
See the [Layout & UI Zones](layout-ui.md) guide for full visual customisation options.
## 5. Core Engine Features
Fine-tune how the parser processes your content files.
```json "docmd.config.json"
{
"minify": true,
"autoTitleFromH1": true,
"copyCode": true,
"pageNavigation": true,
"markdown": {
"breaks": true
}
}
```
| Option | Type | Default | Description |
| :--- | :--- | :--- | :--- |
| `minify` | `Boolean` | `true` | Compresses output HTML and JS structures for maximum speed. |
| `autoTitleFromH1` | `Boolean` | `true` | Resolves missing page titles using the first H1 header in the file. |
| `copyCode` | `Boolean` | `true` | Displays a "Copy Code" button on the top-right of syntax blocks. |
| `pageNavigation` | `Boolean` | `true` | Adds "Previous" and "Next" page links at the bottom of every page based on the navigation order. |
| `markdown.breaks` | `Boolean` | `true` | Standardises line breaks. Set to `false` if you wrap markdown lines at 80 columns. |
::: callout warning "Standalone editLink Deprecated" icon:alert-triangle
The standalone `editLink` configuration is deprecated. Use the core [Git plugin](../plugins/git.md) instead. It provides identical edit link functionality alongside commit timestamps and metadata logs.
:::
---
## [Redirects & 404](https://docs.docmd.io/configuration/redirects/)
---
title: "Redirects & 404"
description: "Configure metadata-based redirects and custom branded 404 error pages for static deployments."
---
Static hosting environments lack server-side logic (like Nginx rules) for dynamic routing. docmd generates native HTML failsafes to handle redirection and error states automatically.
## Server-less Redirects
Forward traffic from old URLs to new destinations by defining mappings in the `redirects` object.
```json "docmd.config.json"
{
"redirects": {
"/setup": "/getting-started/installation",
"/v1/api": "/api-reference"
}
}
```
### Technical Implementation
When you define a redirect, the engine creates an `index.html` file at the old path containing a ` ` tag. This strategy ensures:
1. **Seamless Redirection**: Users forward to the new destination instantly.
2. **SEO Preservation**: Search engines recognise the redirection to maintain link equity.
3. **Analytics Tracking**: Page views register before the redirect occurs.
## Branded 404 Pages
When users request a missing URL, static hosts automatically load a root `404.html` file. docmd generates this file by default. It inherits your site's theme, sidebar, and SPA functionality perfectly.
### Customising Error Content
Personalise the 404 error message in your configuration:
```json "docmd.config.json"
{
"notFound": {
"title": "404: Page Not Found",
"content": "We couldn't find the page you're looking for. Use the sidebar to find your way back."
}
}
```
::: callout tip "Local Development" icon:lightbulb
The development server automatically serves your custom 404 page for missing files. Test the error experience locally.
:::
---
## [Site Banner](https://docs.docmd.io/configuration/site-banner/)
---
title: "Site Banner"
description: "Site-wide announcement banner. Sits above the menubar, supports inline markdown, optional icon, CTA link, and per-session dismissal."
---
# Site Banner
> **New in 0.8.7.** A dismissable announcement banner built into the default UI. Sits above the menubar and below the page header. **Opt-in** — nothing is rendered unless you set `config.layout.banner`.
Use it for release announcements, maintenance windows, beta calls-to-action, or any other site-wide message.
## Enable in 30 seconds
```json "docmd.config.json"
{
"layout": {
"banner": {
"content": "**v0.9 ships Friday** — read the announcement.",
"type": "info",
"dismissible": true,
"link": { "text": "Read more", "url": "/blog/v0-9" }
}
}
}
```
The banner appears on every page. Users who close it once won't see it again until the next browser session.
## Configuration reference
| Field | Default | Description |
|---|---|---|
| `content` | `""` | Inline markdown text (`**bold**`, `` `code``). Mutually exclusive with `html`. |
| `html` | `""` | Raw HTML. Takes precedence over `content`. Use for richer layouts. |
| `type` | `"info"` | `"info"` \| `"success"` \| `"warning"` \| `"danger"` — affects background tint. |
| `dismissible` | `true` | Show a close (X) button. When `false`, the banner is permanent. |
| `link` | `null` | `{ text, url }` for an optional CTA link rendered after the content. |
| `icon` | `null` | Lucide icon name shown on the left. Common picks: `megaphone`, `info`, `bell`. |
### Examples
Plain announcement:
```json "docmd.config.json"
{
"layout": {
"banner": {
"content": "Site maintenance scheduled for Sunday 02:00–04:00 UTC.",
"type": "warning"
}
}
}
```
Success / release:
```json "docmd.config.json"
{
"layout": {
"banner": {
"content": "**v1.0 is out!** Read the release notes.",
"type": "success",
"icon": "party-popper",
"link": { "text": "Release notes", "url": "/blog/v1-0" }
}
}
}
```
Rich HTML (escape carefully):
```json "docmd.config.json"
{
"layout": {
"banner": {
"html": "New: AI-powered search is here. Learn more → ",
"type": "info",
"dismissible": false
}
}
}
```
## Behaviour
- **Position** — Sits at the very top of the page, above the menubar and sidebar logo bar. CSS-only positioning, no layout shift when dismissed.
- **Dismissal persistence** — The "dismissed" state is stored in `sessionStorage`. A fresh browser session re-shows the banner. If you need longer persistence, write to `localStorage` from your own client-side script — the banner's `data-docmd-banner` attribute makes it easy to find.
- **Per-page override** — Not yet supported in 0.8.7. To hide the banner on a single page, set `layout.banner: null` in a `config.templates[page]` entry (planned for a follow-up).
## Re-styling
The banner is built from BEM-style classes on the `.docmd-banner` root. Re-skin it via `customCss`:
```css
.docmd-banner--info {
background: linear-gradient(90deg, #fef3c7 0%, #fff 100%);
border-bottom: 2px solid #f59e0b;
}
.docmd-banner__link {
font-weight: 600;
}
```
## Disabling
To remove the banner globally, set `layout.banner` back to `null` (or remove the key). To hide it on a single page, use the planned per-page override or render `null` in a `frontmatter` (post-0.8.7).
::: callout tip "Combine with a changelog template"
Pair the banner with a `template-changelog` package to give your users a permanent record of every release you announce.
:::
---
## [Versioning](https://docs.docmd.io/configuration/versioning/)
---
title: "Versioning"
description: "Enable multi-version documentation with seamless switching, sticky path preservation, and isolated build directories."
---
docmd features a native Versioning Engine. Manage and serve multiple versions of your project simultaneously. The engine automatically handles URL routing, sidebar updates, and switching logic.
## Directory Organisation
Organise your documentation into versioned source folders. A common pattern keeps the active version in `docs/` and archived versions in directories prefixed with `docs-`.
```text
my-project/
├── docs/ # Latest Version (Main)
├── docs-v1/ # Legacy Version
├── docmd.config.json
```
## Configuration
Define your versions within the `versions` object:
```json "docmd.config.json"
{
"versions": {
"current": "v2",
"position": "sidebar-top",
"all": [
{ "id": "v2", "dir": "docs", "label": "v2.x (Latest)" },
{ "id": "v1", "dir": "docs-v1", "label": "v1.x" }
]
}
}
```
## Core Features
### 1. Root SEO (The "Current" Version)
The `current` version generates directly at your output root (e.g., `mysite.com/`). This ensures search traffic always lands on your most up-to-date documentation.
### 2. Isolated Sub-directories
Non-current versions build automatically into subfolders matching their `id`.
* `v2 (Current)` → `mysite.com/`
* `v1` → `mysite.com/v1/`
### 3. Sticky Switching (Path Preservation)
docmd preserves the relative path when users switch versions. If a user reads `mysite.com/getting-started` and switches to **v1**, they automatically redirect to `mysite.com/v1/getting-started` (if the page exists).
### 4. Asset Isolation
Each version inherits your global `assets/` directory. docmd isolates them during the build to prevent style leakage or conflicts.
### 5. Versioned Navigation
Each version can maintain an independent navigation structure. docmd uses a cascading priority system to resolve the sidebar.
See [Navigation Configuration](navigation.md) for details on the resolution hierarchy.
## Best Practices
1. **Semantic IDs**: Use concise, URL-friendly IDs like `v1`, `v2`, or `beta`.
2. **Navigation Parity**: Maintain consistent folder structures across versions to maximise "Sticky Switching".
3. **Unified Configuration**: Do not create separate config files for each version. docmd processes all versions in a single pass.
---
## [Workspaces](https://docs.docmd.io/configuration/workspaces/)
---
title: "Workspaces"
description: "Build multiple independent documentation projects from a single docmd instance, with global configuration cascading and a built-in Project Switcher."
---
Workspaces let you build and deploy multiple documentation projects from one repository. Each project keeps its own configuration. Global settings defined at the workspace root cascade automatically into every project.
```text
docs.example.com/ → Main documentation
docs.example.com/sdk/ → SDK reference
docs.example.com/cli/ → CLI documentation
```
## Setup
### 1. Directory Structure
One directory per project. Shared assets and global configuration live at the repository root.
```text
my-docs/
├── assets/ ← shared assets (all projects inherit these)
├── main-docs/
│ ├── docmd.config.json ← project config (overrides root defaults)
│ └── docs/ ← project content
├── sdk-docs/
│ ├── docmd.config.json
│ └── docs/
├── docmd.config.json ← workspace root config
└── package.json
```
### 2. Root Workspace Config
The root `docmd.config.json` uses the `workspace` key. Any top-level keys (e.g. `theme`, `menubar`, `logo`) act as **global defaults** for every project.
```json "docmd.config.json"
{
"workspace": {
"projects": [
{ "prefix": "/", "src": "main-docs", "title": "Docs" },
{ "prefix": "/sdk", "src": "sdk-docs", "title": "SDK Reference" }
],
"switcher": {
"enabled": true,
"position": "sidebar-top"
}
},
"theme": { "name": "default", "appearance": "system" },
"logo": {
"light": "assets/logo-dark.svg",
"dark": "assets/logo-light.svg"
},
"menubar": [
{ "text": "GitHub", "url": "https://github.com/my-org/my-repo", "external": true }
]
}
```
#### `workspace` Options
| Key | Type | Description |
| :-- | :--- | :---------- |
| `projects` | `Array` | List of project entries. At least one must use `prefix: "/"`. |
| `switcher` | `Object` | Controls the [Project Switcher](#project-switcher) visibility and position. |
#### Project Entry Fields
| Key | Type | Required | Description |
| :-- | :--- | :------- | :---------- |
| `prefix` | `String` | ✅ | URL prefix. Use `"/"` for the root project. |
| `src` | `String` | ✅ | Directory path (relative to CWD) containing the project's content and optional `docmd.config.json`. |
| `title` | `String` | - | Display name shown in the Project Switcher UI. |
### 3. Project-Level Config
Each project directory can have its own `docmd.config.json`. Settings defined here **override** the workspace root defaults.
```json "docmd.config.json"
{
"title": "SDK Reference",
"src": "docs",
"plugins": {
"search": {},
"openapi": {}
}
}
```
If no local config file is found, the engine applies zero-config auto-routing using the workspace defaults.
### 4. Global Configuration Cascading
Any key defined in the root workspace config automatically applies to every project. Project configs can selectively override any of these globals.
| Layer | Precedence |
| :---- | :--------- |
| Root workspace config | Lowest (applied first as defaults) |
| Project `docmd.config.json` | Higher (overrides root defaults) |
| Project `navigation.json` | Highest (always wins for navigation) |
**Example**: Define your global `theme` and `menubar` once at the root. Each project only needs to set `title`, `src`, and its own `plugins`.
::: callout info "Navigation Priority" icon:info
A project-level `navigation.json` file **always takes precedence** over any `navigation` array defined in the workspace root config. If neither exists, docmd falls back to automatic directory scanning.
:::
## Project Switcher
The Project Switcher renders a slim UI component for navigating between workspace projects.
### Configuration
```json "docmd.config.json"
{
"workspace": {
"switcher": {
"enabled": true,
"position": "sidebar-top"
}
}
}
```
| Position | Description |
| :------- | :---------- |
| `sidebar-top` (default) | Pinned at the top of the sidebar, above navigation. |
| `sidebar-bottom` | Pinned at the bottom of the sidebar. |
| `options-menu` | Integrated into the header options menu alongside search and theme toggles. |
The switcher only renders when two or more projects are defined.
## Assets
### Shared Assets
Place logos, favicons, and global CSS in the root `assets/` directory. The engine copies these into every project's output automatically during both `dev` and `build`.
### Project-Specific Assets
Each project can have its own `assets/` directory. Project assets take priority over shared assets when filenames conflict.
## Building & Development
### Dev Server
```bash
npx @docmd/core dev
```
Builds all projects and serves them from a single port. File changes trigger **targeted, per-project** rebuilds - only the modified project re-renders, not the whole workspace. Root config changes trigger a full workspace rebuild.
### Production Build
```bash
npx @docmd/core build
```
Outputs a single static directory. All projects merge into their respective subpaths. No reverse proxy or complex CI pipelines are required.
## Rules & Constraints
1. **Root Project Required**: Exactly one project must have `prefix: "/"`.
2. **Unique Prefixes**: Every project must use a unique URL prefix.
3. **`out` in Root Only**: Only the root workspace config controls the output directory. Child project configs must not define `out`.
4. **No Prefix Conflicts**: If a root project has a folder named `sdk/`, and another project uses `prefix: "/sdk"`, the engine emits a conflict warning. The prefixed project always wins.
## Migrating from Legacy Configurations
The pre-0.8.3 `projects` array syntax and other legacy configuration keys are automatically normalised to the modern `workspace` schema for backward compatibility.
While manual updates are strictly not required, you can automatically upgrade your configuration file to the modern schema using the CLI.
::: callout tip "Migrate with one command" icon:lightbulb
Run `npx @docmd/core migrate --upgrade` to automatically rewrite your root configuration to the current schema.
:::
---
## [Zero-Config](https://docs.docmd.io/configuration/zero-config/)
---
title: "Zero-Config"
description: "Understand the heuristics engine of docmd that automatically structures your site without files."
---
`docmd` features a smart heuristics engine designed to parse and structure your documentation automatically. You can start building, serving, and translating your documentation without writing a single line of configuration.
## How It Works
When run without a `docmd.config.json` file, the engine automatically triggers **Zero-Config Mode**. It scans the workspace directory for content and applies the following heuristics:
### 1. Source Directory Detection
The engine looks for documentation files in these candidate directories in order:
1. `docs/`
2. `src/docs/`
3. `documentation/`
4. `content/`
5. `.` (Root directory fallback)
If one of the candidate directories is found and contains Markdown files, it is selected as the source. If no directory is found, but the project root has Markdown files, the root directory is used (automatically ignoring `node_modules`, `.git`, output folders like `site/`, `dist/`, and `out/`).
If no documentation content is found at all, `docmd` initializes a fresh starter structure automatically.
### 2. Heuristics for Versions and Locales
The folder structure is scanned to dynamically extract versioning and localization metadata:
- **Versions**: Subdirectories matching `v[0-9]+` (e.g., `v1.0`, `v08`) are parsed as documentation versions.
- **Locales**: Subdirectories with two-letter language codes (e.g., `en`, `de`, `zh`) are treated as localized variants.
- **Structure Extraction**: The highest version is designated as the current release, and the first locale found (prioritizing `en` if present) is set as the default language.
### 3. Automatic Navigation Routing
If there are no root-level versions or locales, the engine builds a navigation tree dynamically by analyzing the file structure:
- Subdirectories are mapped to navigation groups.
- Titles are generated dynamically from file basenames. E.g., `getting-started.md` is formatted as `Getting Started`.
- Index files (`index.md`, `README.md`) are routed as the landing page of the current directory.
## Zero-Config Best Practices
To get the most out of Zero-Config mode, follow these structure recommendations:
- **Explicit file naming**: Use clear, hyphenated or camelCase file names. The autoloader converts them to readable titles.
- **Folder-based sections**: Place related documents inside subfolders to automatically group them in the sidebar.
- **Index fallback**: Always place an `index.md` or `README.md` at the root of your source folder to serve as the landing page.
- **Clean output path**: If you are using the root folder `.` as your source, keep your built assets in the default `site/` folder which is automatically ignored.
## Built-in Defaults (new in 0.8.7)
A `docmd.config.json` (or no config at all) gives you a usable site out of the box. The following keys ship with sensible defaults, so you only need to set them when you want a different value.
::: callout info "How to opt out"
Set the key to `false` (or the appropriate empty value) to disable a default. For example, `pageNavigation: false` removes prev/next links; `theme.appearance: "dark"` overrides the colour mode.
:::
### Top-level QoL defaults
| Key | Default | Notes |
|---|---|---|
| `pageNavigation` | `true` | prev/next links at the bottom of each article |
| `copyCode` | `true` | copy-code buttons on `` blocks |
| `autoTitleFromH1` | `true` | use the first `# H1` as the page title when frontmatter is absent |
### Layout & sidebar defaults
| Key | Default | Notes |
|---|---|---|
| `layout.spa` | `true` | SPA navigation between pages |
| `layout.breadcrumbs` | `true` | breadcrumb row above the page header |
| `layout.header.enabled` | `true` | top page header |
| `layout.sidebar.collapsible` | `true` | sidebar can collapse on desktop |
| `layout.sidebar.defaultCollapsed` | `false` | sidebar starts expanded |
| `layout.optionsMenu.position` | `"header"` | options menu (search / theme switch / sponsor) goes in the header |
| `layout.optionsMenu.components.search` | `true` | search trigger in the menu |
| `layout.optionsMenu.components.themeSwitch` | `true` | light/dark toggle in the menu |
| `layout.optionsMenu.components.sponsor` | `null` | opt-in — set to a URL to enable |
### Footer defaults
| Key | Default | Notes |
|---|---|---|
| `layout.footer.style` | `"minimal"` | one-line footer bar |
| `layout.footer.copyright` | `` `© ${new Date().getFullYear()}` `` | auto-generated current-year copyright |
| `layout.footer.branding` | `true` | show "Built with docmd" by default |
### Theme defaults
| Key | Default | Notes |
|---|---|---|
| `theme.name` | `"default"` | base CSS theme; reserved values: `default`, `sky`, `ruby`, `retro`. Any other value is auto-promoted to a [template name](../theming/templates.md). |
| `theme.appearance` | `"system"` | default colour mode (follows `prefers-color-scheme`). Set to `"light"` or `"dark"` to force. |
| `theme.codeHighlight` | `true` | syntax highlighting on `` blocks |
### New opt-in features (off by default)
| Key | Default | Notes |
|---|---|---|
| `cookie` | `null` | opt-in cookie consent dialog — see [Cookie Consent](cookie-consent.md) |
| `layout.banner` | `null` | opt-in site-wide announcement banner — see [Site Banner](site-banner.md) |
| `theme.template` | `null` | opt-in template selection — see [Templates](../theming/templates.md) |
The defaults were chosen to give brand-new sites a usable look and feel without any config. Older configs keep their explicit values untouched — only `undefined` keys are filled in.
---
## [Buttons](https://docs.docmd.io/content/containers/buttons/)
---
title: "Buttons"
description: "Inject clear, highly visible call-to-actions directly into your documentation."
---
Buttons are interactive components designed for navigation and call-to-actions. They can point to internal documentation pages or external resources.
## Syntax Reference
```markdown
::: button "Label text" target_url [property:value...]
```
| Parameter | Type | Description |
| :--- | :--- | :--- |
| **Path** | `/path/` | Relative project URL. Resolves automatically for SPA navigation. |
| **External** | `external:URL`| Opens the target URL in a new browser tab (`target="_blank"`). |
| **Colour** | `color:VALUE` | Applies a background colour (supports CSS names or Hex codes). |
| **Icon** | `icon:NAME` | Adds a [Lucide](external:https://lucide.dev/icons) icon before the label. |
## Examples
### Internal Navigation
Use relative Markdown paths to ensure seamless transitions within the docmd SPA.
```markdown
::: button "Install docmd" ../../getting-started/installation.md
```
::: button "Install docmd" ../../getting-started/installation.md
### External Resource Link
Prepend `external:` to the URL to force the link to open in a new tab.
```markdown
::: button "View GitHub Repository" external:https://github.com/docmd-io/docmd
```
::: button "View GitHub Repository" external:https://github.com/docmd-io/docmd
### Styling & Icons
Match buttons to your brand identity using colour overrides and Lucide icons to enhance visual clarity.
```markdown
::: button "Success Confirmation" ./#success color:#228B22
::: button "Danger Action" ./#delete color:crimson icon:alert-circle
::: button "View Source" external:https://github.com/docmd-io/docmd icon:github
```
::: button "Success Confirmation" ./#success color:#228B22
::: button "Danger Action" ./#delete color:crimson icon:alert-circle
::: button "View Source" external:https://github.com/docmd-io/docmd icon:github
## Critical Note: Self-Closing Logic
Buttons are self-closing. Adding a terminal `:::` line immediately after a button will terminate the **parent container** (e.g., a Card or Tab), potentially breaking your layout.
**Incorrect Sequence:**
```markdown
::: card "Setup"
::: button "Begin" ../../setup.md
::: <-- Error: This closes the Card prematurely.
:::
```
**Correct Sequence:**
```markdown
::: card "Setup"
::: button "Begin" ../../setup.md
::: <-- Correct: This closes the Card cleanly.
```
---
## [Callouts](https://docs.docmd.io/content/containers/callouts/)
---
title: "Callouts"
description: "Highlight critical warnings, pro-tips, and background context using semantic visual blocks."
---
Callouts isolate information that requires the reader's immediate attention. docmd provides five semantic types, each with distinct styling and iconography.
::: callout info "Migration-Friendly Aliases"
If migrating from VitePress or Docusaurus, you can use their native syntax:
- `:::tip`, `:::warning`, `:::danger`, `:::info` (VitePress)
- `:::note`, `:::caution` (Docusaurus)
These aliases render identically to their docmd equivalents. Spaceless syntax like `:::callout` also works.
:::
## Syntax Reference
```markdown
::: callout type "Title text" [property:value...]
The content or warning goes here.
:::
```
| Parameter | Type | Description |
| :--- | :--- | :--- |
| **Type** | `info` \| `tip` \| `warning` \| `danger` \| `success` | The semantic intent which defines default colours and iconography. |
| **Title** | `"String"` | Optional. Overrides the default semantic label with a custom title. |
| **Icon** | `icon:NAME` | Optional. Overrides the default icon with a custom [Lucide](external:https://lucide.dev/icons) icon. |
### Supported Types
| Type | Visual Signal |
| :--- | :--- |
| `info` | Contextual background or helpful non-critical information. |
| `tip` | Performance shortcuts or best practices. |
| `warning` | Potential issues or deprecated features to monitor. |
| `danger` | Risk of data loss, breaking changes, or critical failures. |
| `success` | Confirmation of a successful configuration or build. |
## Examples
### Basic Callout
A minimal callout without a title uses the type as its default label.
```markdown
::: callout info
Legacy configuration schemas remain supported but are no longer recommended.
:::
```
::: callout info
Legacy configuration schemas remain supported but are no longer recommended.
:::
### Custom Title & Icon
Override the default label and icon with a custom title and any Lucide icon name.
```markdown
::: callout warning "Breaking Change" icon:alert-triangle
The internal WebSocket RPC system is officially deprecated.
:::
```
::: callout warning "Breaking Change" icon:alert-triangle
The internal WebSocket RPC system is officially deprecated.
:::
### Rich Content Composition
Callouts support full Markdown. Embed code blocks and buttons directly within the alert.
````markdown
::: callout tip "Optimised Local Testing" icon:command
Use the preserve flag to maintain build files during dev sessions:
```bash
npx @docmd/core dev --preserve
```
::: button "CLI Flag Reference" /cli-commands
:::
````
::: callout tip "Optimised Local Testing" icon:command
Use the preserve flag to maintain build files during dev sessions:
```bash
npx @docmd/core dev --preserve
```
::: button "CLI Flag Reference" ./#cli-commands
:::
::: callout tip "Prioritised Logic for AI"
For LLMs, callouts act as **High-Priority Anchors**. Use `::: callout danger` to document breaking changes - this provides a clear signal that the AI model must prioritise that information.
:::
---
## [Cards](https://docs.docmd.io/content/containers/cards/)
---
title: "Cards"
description: "Organise information into framed, visually distinct containers. Perfect for feature grids and landing pages."
---
Cards encapsulate related content into a distinct, bordered frame with an optional header, providing clear visual hierarchy for your documentation.
## Syntax Reference
```markdown
::: card "Title text" [property:value...]
This is the primary content area of the card.
:::
```
| Parameter | Type | Description |
| :--- | :--- | :--- |
| **Title** | `"String"` | Optional header title rendered at the top of the card. |
| **Icon** | `icon:NAME` | Optional. Adds a [Lucide](external:https://lucide.dev/icons) icon next to the header title. |
## Examples
### Feature Highlight
Use a card to frame a single technical capability with a clear title and icon.
```markdown
::: card "Asynchronous Generation" icon:zap
The core engine uses a non-blocking I/O pipeline, generating thousands of pages in milliseconds.
:::
```
::: card "Asynchronous Generation" icon:zap
The core engine uses a non-blocking I/O pipeline, generating thousands of pages in milliseconds.
:::
### Rich Content
Cards accept any standard Markdown, including code blocks and buttons.
````markdown
::: card "Instant Localisation"
Prepare your documentation for a global audience using the built-in i18n support.
```bash
docmd add i18n
```
::: button "L10n Strategy Guide" /guides/localization
:::
````
::: card "Instant Localisation"
Prepare your documentation for a global audience using the built-in i18n support.
```bash
docmd add i18n
```
::: button "L10n Strategy Guide" ./#localization
:::
### Multi-Column Layout
Wrap multiple cards inside a `grids` container for a responsive multi-column layout.
```markdown
::: grids
::: grid
::: card "Primary Node"
Configuration for the master instance.
:::
:::
::: grid
::: card "Secondary Node"
Configuration for redundant slave instances.
:::
:::
:::
```
::: grids
::: grid
::: card "Primary Node"
Configuration for the master instance.
:::
:::
::: grid
::: card "Secondary Node"
Configuration for redundant slave instances.
:::
:::
:::
::: callout tip "Semantic Clustering for AI" icon:lightbulb
In the `llms-full.txt` stream, content wrapped in a `card` is treated by AI agents as a **Cohesive Topic Cluster**. Utilising cards to segment unrelated concepts prevents context leakage and ensures LLM-generated summaries remain logically isolated.
:::
---
## [Changelogs](https://docs.docmd.io/content/containers/changelogs/)
---
title: "Changelogs"
description: "Generate structured, timeline-based version history and release notes."
---
The `changelog` container provides a specialised layout for documenting project evolution. It parses version or date headers into a vertical timeline, ensuring historical updates are easily scannable.
## Syntax Reference
```markdown
::: changelog
== Label Text
Description of the entry goes here.
:::
```
| Parameter | Type | Description |
| :--- | :--- | :--- |
| **Entry Marker** | `==` | The delimiter that defines a new timeline entry within the changelog. |
| **Label** | `String` | The text (e.g., version number or date) that renders as a timeline badge on the left. |
## Examples
### Release History
Changelogs support rich Markdown within each entry, including lists, callouts, and code blocks.
```markdown
::: changelog
== v2.0.0 (2026-03-15)
### Major System Overhaul
The core engine has been rearchitected for isomorphic execution.
* Implemented **SPA Router** for zero-reload navigation.
* Introduced the **Isomorphic Plugin** system.
::: callout success
This release offers a 40% improvement in initial build speed.
:::
== v1.5.1 (2025-12-10)
### Security Patch
* Resolved high-severity vulnerability in the internal parser.
* Updated dependency `flatted` to `v3.3.2`.
== v1.0.0 (2024-05-01)
Initial public release.
:::
```
::: changelog
== v2.0.0 (2026-03-15)
### Major System Overhaul
The core engine has been rearchitected for isomorphic execution.
* Implemented **SPA Router** for zero-reload navigation.
* Introduced the **Isomorphic Plugin** system.
::: callout success
This release offers a 40% improvement in initial build speed.
:::
== v1.5.1 (2025-12-10)
### Security Patch
* Resolved high-severity vulnerability in the internal parser.
* Updated dependency `flatted` to `v3.3.2`.
== v1.0.0 (2024-05-01)
Initial public release.
:::
::: callout tip "Historical Context for AI"
Changelogs provide a temporal map for AI agents. The `::: changelog` structure allows an LLM to accurately parse when specific features or security fixes were introduced in the `llms.txt` context stream.
:::
---
## [Collapsible Sections](https://docs.docmd.io/content/containers/collapsible/)
---
title: "Collapsible Sections"
description: "Embed interactive accordion-style toggles for FAQs, deep-dive content, and spoilers."
---
The `collapsible` container creates an interactive, toggleable accordion. It is ideal for FAQs and detailed technical configuration, keeping secondary information accessible without cluttering the primary view.
::: callout info "VitePress Alias"
If migrating from VitePress, use `:::details` as an alias for `:::collapsible`. Spaceless syntax like `:::collapsible` also works.
:::
## Syntax Reference
```markdown
::: collapsible [open] "Title text" [property:value...]
Main content goes here.
:::
```
| Parameter | Type | Description |
| :--- | :--- | :--- |
| **Open State** | `open` | Optional. If provided, the section initialises in an expanded state. |
| **Title** | `"String"` | The text rendered on the toggle bar. Defaults to "Click to expand". |
| **Icon** | `icon:NAME` | Optional. Adds a [Lucide](external:https://lucide.dev/icons) icon before the title text. |
## Examples
### Default State
A collapsible section is closed by default. Ideal for FAQs and reducing visual density.
```markdown
::: collapsible "How do I upgrade docmd?"
Run `npm update -g @docmd/core` to fetch the latest stable engine.
:::
```
::: collapsible "How do I upgrade docmd?"
Run `npm update -g @docmd/core` to fetch the latest stable engine.
:::
### Initially Open
Use the `open` flag for sections that should be visible by default but allow users to minimise them.
```markdown
::: collapsible open "Environment Prerequisites"
1. Node.js v18+ (LTS recommended)
2. PNPM package manager
:::
```
::: collapsible open "Environment Prerequisites"
1. Node.js v18+ (LTS recommended)
2. PNPM package manager
:::
### Rich Content
Collapsibles can contain any Markdown, including syntax-highlighted code blocks.
````markdown
::: collapsible "Sample JSON Response"
```json
{
"status": "success",
"data": { "version": "0.8.2" }
}
```
:::
````
::: collapsible "Sample JSON Response"
```json
{
"status": "success",
"data": { "version": "0.8.2" }
}
```
:::
::: callout tip
Content inside a `collapsible` is fully indexed by search and included in the `llms.txt` stream. AI agents can answer questions based on hidden technical details while keeping the human-facing interface clean.
:::
---
## [URL Embeds](https://docs.docmd.io/content/containers/embed/)
---
title: URL Embeds
description: Safely embed dynamic video, social, and interactive content directly into your documents.
---
docmd ships natively with the highly-optimised **[embed-lite](external:https://github.com/mgks/embed-lite)** parser. It transforms external URLs into secure, zero-latency UI components automatically.
## Supported Platforms
The engine natively supports structured formatters for the following networks:
* **Video:** YouTube (including Shorts), Vimeo, Dailymotion, TikTok
* **Social:** X (Twitter), Reddit, Instagram, Facebook, LinkedIn
* **Code & Prototyping:** GitHub Gists, CodePen, Figma, Google Maps
* **Music:** Spotify, SoundCloud
## Syntax Reference
```markdown
::: embed "target_url"
```
| Parameter | Type | Description |
| :--- | :--- | :--- |
| **URL** | `"String"` | The absolute URL of the external resource to embed (e.g., a YouTube video, Figma file, or GitHub Gist). |
## Examples
### Video Embed
Paste any YouTube, Vimeo, or TikTok URL to render a native, responsive player.
```markdown
::: embed "https://www.youtube.com/watch?v=0CSyIBHQy9g"
```
::: embed "https://www.youtube.com/watch?v=0CSyIBHQy9g"
### Fallback Behaviour
If the parser encounters an unsupported or invalid URL, docmd gracefully falls back to a hyperlink button rather than breaking the page.
```markdown
::: embed "https://docs.docmd.io/content/containers/embed/"
```
::: embed "https://docs.docmd.io/content/containers/embed/"
---
## [Grids](https://docs.docmd.io/content/containers/grids/)
---
title: "Grids"
description: "Organise layout into auto-adjusting responsive columns without writing HTML."
---
Grids provide a native, Markdown-driven layout system. Use the `grids` container to structure elements side-by-side. Columns automatically fill available space and stack vertically on smaller screens.
## Syntax Reference
```markdown
::: grids
::: grid
Content for the first column.
:::
::: grid
Content for the second column.
:::
:::
```
| Container | Description |
| :--- | :--- |
| **`::: grids`** | The parent container that initiates the responsive flexbox layout. |
| **`::: grid`** | Each child `grid` block acts as an individual column. Add as many as needed. |
## Examples
### Side-by-Side Cards
Combine `grids` with `cards` to display multiple features in a clean, responsive layout.
```markdown
::: grids
::: grid
::: card "Speed" icon:zap
Built on a non-blocking I/O pipeline for maximum performance.
:::
:::
::: grid
::: card "Scalability" icon:layers
Designed for massive monorepos and extensive project structures.
:::
:::
:::
```
::: grids
::: grid
::: card "Speed" icon:zap
Built on a non-blocking I/O pipeline for maximum performance.
:::
:::
::: grid
::: card "Scalability" icon:layers
Designed for massive monorepos and extensive project structures.
:::
:::
:::
### Three-Column Layout
Add a third `grid` block to create a three-column row. Columns automatically balance their widths.
```markdown
::: grids
::: grid
::: card "Search" icon:search
Client-side full-text search powered by MiniSearch.
:::
:::
::: grid
::: card "i18n" icon:globe
First-class locale routing and translated search indexes.
:::
:::
::: grid
::: card "Themes" icon:palette
Built-in dark mode and full CSS variable customisation.
:::
:::
:::
```
::: grids
::: grid
::: card "Search" icon:search
Client-side full-text search powered by MiniSearch.
:::
:::
::: grid
::: card "i18n" icon:globe
First-class locale routing and translated search indexes.
:::
:::
::: grid
::: card "Themes" icon:palette
Built-in dark mode and full CSS variable customisation.
:::
:::
:::
::: callout tip "Semantic Layouts"
The `grids` container keeps your structure purely in Markdown. This results in cleaner source files and ensures LLMs interpret structural relationships accurately.
:::
---
## [Hero Sections](https://docs.docmd.io/content/containers/hero/)
---
title: "Hero Sections"
description: "Build high-impact landing page headers and marketing highlights purely in Markdown."
---
The `hero` container creates visually striking landing page headers. It handles complex layouts including splits, glow effects, and sliders without requiring custom HTML.
## Syntax Reference
```markdown
::: hero [property:value...]
# Page Title
A short supporting tagline.
::: button "Call to Action" /target-url
:::
```
| Parameter | Type | Description |
| :--- | :--- | :--- |
| **Layout** | `layout:split` \| `layout:slider` | `split` divides the hero into a text area and a side media area. `slider` creates a horizontal scroll-snap carousel. |
| **Glow** | `glow:true` | Injects a subtle radial gradient glow in the background. |
| **Side Separator** | `== side` | Used with `layout:split`. Everything after this delimiter renders in the secondary (right-hand) area. |
| **Slide Separator** | `== slide` | Used with `layout:slider`. Each `== slide` defines a new carousel panel. |
## Examples
### Split Layout
Use the `== side` separator to divide content into a primary text area and a secondary media area.
```markdown
::: hero layout:split glow:true
# docmd
Isomorphic execution. AI-optimised.
::: button "Quickstart" ../../getting-started/quick-start.md color:blue
== side
::: embed "https://www.youtube.com/watch?v=0CSyIBHQy9g"
:::
```
::: hero layout:split glow:true
# docmd
Isomorphic execution. AI-optimised.
::: button "Quickstart" ../../getting-started/quick-start.md color:blue
== side
::: embed "https://www.youtube.com/watch?v=0CSyIBHQy9g"
:::
### Slider Layout
Use `== slide` separators to build an auto-advancing carousel of content panels.
```markdown
::: hero layout:slider
== slide
# Isomorphic Core
The engine renders everywhere.
== slide
# AI Optimisation
Built for the LLM era.
:::
```
::: hero layout:slider
== slide
# Isomorphic Core
The engine renders everywhere.
== slide
# AI Optimisation
Built for the LLM era.
:::
::: callout tip "Best Practices"
Use `glow:true` sparingly on dark mode sites for a premium feel. Place `::: button` elements in the primary text section, before `== side`, to ensure they remain visible on mobile screens.
:::
---
## [Custom Interactive Containers](https://docs.docmd.io/content/containers/)
---
title: "Custom Interactive Containers"
description: "A comprehensive directory of the interactive UI building blocks available in docmd."
---
Standard Markdown excels at basic text formatting, but professional technical documentation requires rich structural components to effectively communicate complex logic. `docmd` extends Markdown with a suite of **isomorphic containers** that render into responsive, high-fidelity UI elements.
::: callout tip "Migrating from Other Documentation Engines?"
`docmd` supports syntax aliases from **VitePress** and **Docusaurus** out of the box. Containers like `:::tip`, `:::warning`, `:::note`, `:::details`, and `:::caution` work without modification. Spaceless syntax (e.g., `:::tabs` instead of `::: tabs`) is also supported for all containers.
:::
## Block Syntax Reference
All containers utilise a consistent block syntax, ensuring a predictable authoring experience across your entire project.
```markdown
::: type "Optional Header Title"
This is the primary content area.
It supports **Markdown**, imagery, and deep component nesting.
:::
```
| Component | Keyword | Primary Use Case |
| :--- | :--- | :--- |
| **[Callouts](callouts.md)** | `callout` | Semantic highlights for tips, warnings, and alerts. |
| **[Cards](cards.md)** | `card` | Framed structural blocks for feature grids and layout control. |
| **[Grids](grids.md)** | `grids` | Auto-adjusting multi-column structural groups. |
| **[Tabs](tabs.md)** | `tabs` | Interactive switchable panes for alternative platform instructions. |
| **[Steps](steps.md)** | `steps` | Visual numbered timelines for how-to guides and tutorials. |
| **[Collapsibles](collapsible.md)** | `collapsible` | Interactive accordion toggles for FAQs and deep-dive technical data. |
| **[Buttons](buttons.md)** | `button` | Self-closing, prominent call-to-action navigation links. |
| **[Tags](tags.md)** | `tag` | Self-closing, coloured labels for versions, statuses, or inline highlights. |
| **[Hero](hero.md)** | `hero` | High-impact landing page sections with layout and slider support. |
| **[URL Embeds](embed.md)** | `embed` | Secure, zero-latency embeds for video, social, and interactive content. |
| **[Changelogs](changelogs.md)** | `changelog` | Structured, timeline-based version history and release notes. |
| **[Nested Containers](nested-containers.md)** | - | Recursive composition patterns for complex, multi-component layouts. |
## The Strategic Importance of Containers
Containers facilitate more than visual polish; they provide high-fidelity **Semantic Signals** to the `docmd` engine and downstream AI agents:
1. **AI Context Mapping**: Marking a block as a `callout warning` explicitly tells LLMs to prioritise that information during its reasoning and generation phases.
2. **Structural Integrity**: Combining `cards` with standard CSS allows for the creation of sophisticated landing pages without ever leaving the Markdown environment.
3. **Source Maintainability**: Eliminates "HTML Bloat" in your documentation source, keeping your `.md` files clean and machine-readable.
## Recursive Composition
`docmd` supports **Infinite Nesting Depth**. You can compose any container within another to build complex, interactive documentation nodes purely in Markdown.
```markdown
::: card "Architecture Overview"
::: callout info
This module utilises an asynchronous I/O pipeline.
:::
::: button "Deep Explore Core Engine" /advanced/developer-guide
:::
```
[Master the Nesting Guide](nested-containers.md)
---
## [Nested Containers](https://docs.docmd.io/content/containers/nested-containers/)
---
title: "Nested Containers"
description: "Use the recursive parser to combine cards, tabs, and callouts into high-fidelity page layouts."
---
docmd uses a recursive parsing engine. You can nest components within each other to build complex, interactive documentation blocks without writing custom HTML.
::: callout warning "Self-Closing Buttons"
The `::: button` component is self-closing (single line). Never add a terminal `:::` immediately after it - doing so closes the **parent container**, resulting in a broken layout.
:::
## Examples
### Interactive Resource Block
Combine a **Card** for structural framing, **Tabs** for environment-specific instructions, and a **Callout** for critical information.
````markdown
::: card "Monorepo Quickstart"
Choose your preferred initialisation path:
::: tabs
== tab "Automated"
```bash
pnpm onboard
```
::: callout success
This script handles all package installation and build tasks automatically.
:::
== tab "Manual"
Manually fetch and link the core engine.
::: button "Go to Developer Guide" ../../advanced/developer-guide.md
:::
:::
````
### Platform-Specific Tutorial Steps
Nesting **Tabs** inside **Steps** is a standard pattern for providing platform-specific instructions within a sequential tutorial.
```markdown
::: steps
1. **Environment Setup**
Configure your local operating system.
::: tabs
== tab "macOS"
Ensure Homebrew is installed and up-to-date.
== tab "Linux"
Verify the presence of `curl` and `bash`.
:::
2. **Core Verification**
Execute the version check to confirm connectivity.
:::
```
::: steps
1. **Environment Setup**
Configure your local operating system.
::: tabs
== tab "macOS"
Ensure Homebrew is installed and up-to-date.
== tab "Linux"
Verify the presence of `curl` and `bash`.
:::
2. **Core Verification**
Execute the version check to confirm connectivity.
:::
## Design Constraints
| Constraint | Note |
| :--- | :--- |
| **Recursive Tabs** | Nesting tabs within other tabs is technically supported but strongly discouraged - it creates confusing navigation on smaller viewports. |
| **Sequential Conflict** | If you need numbered steps within a tab, use a standard ordered list rather than `::: steps` to avoid layout conflicts. |
| **Indentation** | Indentation is not required by the parser, but 2 or 4-space indentation significantly improves source readability. |
::: callout tip "Knowledge Segmentation for AI"
Nesting provides clear **Semantic Boundaries**. A `callout` nested within a `card` explicitly scopes that tip to the card's topic in the `llms.txt` stream, preventing context leakage across unrelated sections.
:::
---
## [Steps](https://docs.docmd.io/content/containers/steps/)
---
title: "Steps"
description: "Convert standard ordered lists into high-impact visual timelines and tutorials."
---
The `steps` container transforms a standard Markdown ordered list into a numbered vertical timeline. It is designed for technical tutorials and sequential how-to guides.
::: callout info "Spaceless Syntax"
Both `::: steps` and `:::steps` (spaceless) work natively. Use whichever style you prefer.
:::
## Syntax Reference
```markdown
::: steps
1. **Step Title**
Step description goes here.
2. **Next Step**
Continue the sequence.
:::
```
| Container | Description |
| :--- | :--- |
| **`::: steps`** | The parent container that transforms child ordered list items into a numbered timeline. |
| **`1. `** | Any standard Markdown ordered list item acts as a chronological step. Bold the first line of each item to create a clear title. |
## Examples
### Basic Workflow
A straightforward sequence for a common task.
```markdown
::: steps
1. **Initialise Project**
Run `npx @docmd/core init` to scaffold your directory.
2. **Author Content**
Write your documentation using standard Markdown files.
3. **Build & Deploy**
Run `npx @docmd/core build` to generate your static site.
:::
```
::: steps
1. **Initialise Project**
Run `npx @docmd/core init` to scaffold your directory.
2. **Author Content**
Write your documentation using standard Markdown files.
3. **Build & Deploy**
Run `npx @docmd/core build` to generate your static site.
:::
### Steps with Rich Content
Each step can contain code blocks, callouts, and other nested containers.
```markdown
::: steps
1. **Configure Environment**
Define your project variables in `docmd.config.json`.
::: callout tip
Use `defineConfig` to enable IDE autocompletion for all configuration keys.
:::
2. **Generate Production Build**
Execute the build command to generate a highly optimised static site.
```bash
npx @docmd/core build
```
3. **Deploy to Infrastructure**
Synchronise the `site/` directory with S3, Cloudflare Pages, or Vercel.
:::
```
::: steps
1. **Configure Environment**
Define your project variables in `docmd.config.json`.
::: callout tip
Use `defineConfig` to enable IDE autocompletion for all configuration keys.
:::
2. **Generate Production Build**
Execute the build command to generate a highly optimised static site.
```bash
npx @docmd/core build
```
3. **Deploy to Infrastructure**
Synchronise the `site/` directory with S3, Cloudflare Pages, or Vercel.
:::
::: callout tip "Workflow Optimisation" icon:lightbulb
AI models interpret the `steps` container as a signal for **Sequential Workflows**. Always start each list item with a **bolded title** - this allows agents to reliably parse the objective of each step from the `llms.txt` context.
:::
---
## [Tabs](https://docs.docmd.io/content/containers/tabs/)
---
title: "Tabs"
description: "Organise dense, alternative, or multi-language information into switchable interactive panes."
---
Tabs present mutually exclusive or related data sets - such as "pnpm vs npm" or "macOS vs Windows". They condense information into a compact, interactive format.
::: callout info "Spaceless Syntax"
Both `::: tabs` and `:::tabs` (spaceless) work natively. Use whichever style you prefer.
:::
## Syntax Reference
```markdown
::: tabs
== tab "Label" [property:value...]
Content for this tab.
== tab "Another Label"
Content for the second tab.
:::
```
| Parameter | Type | Description |
| :--- | :--- | :--- |
| **Label** | `"String"` | The text displayed on the tab trigger button. |
| **Icon** | `icon:NAME` | Optional. Adds a [Lucide](external:https://lucide.dev/icons) icon before the label text. |
## Examples
### Package Manager Instructions
Show installation commands for multiple package managers in a single compact block.
````markdown
::: tabs
== tab "pnpm"
```bash
pnpm add @docmd/core
```
== tab "npm"
```bash
npm install @docmd/core
```
== tab "yarn"
```bash
yarn add @docmd/core
```
:::
````
::: tabs
== tab "pnpm"
```bash
pnpm add @docmd/core
```
== tab "npm"
```bash
npm install @docmd/core
```
== tab "yarn"
```bash
yarn add @docmd/core
```
:::
### Multi-Language Code Snippets
Keep programming environments cleanly separated with tab icons for quick visual identification.
````markdown
::: tabs
== tab "TypeScript" icon:hexagon
```typescript
import { build } from '@docmd/core';
await build('./docmd.config.json');
```
== tab "JavaScript" icon:braces
```javascript
const { build } = require('@docmd/core');
build('./docmd.config.json');
```
:::
````
::: tabs
== tab "TypeScript" icon:hexagon
```typescript
import { build } from '@docmd/core';
await build('./docmd.config.json');
```
== tab "JavaScript" icon:braces
```javascript
const { build } = require('@docmd/core');
build('./docmd.config.json');
```
:::
## Constraints
| Constraint | Note |
| :--- | :--- |
| **Nesting Depth** | Tabs cannot nest inside other tab components. |
| **Interactive Conflict** | Do not nest `::: steps` inside a tab. Use a standard ordered list instead. |
| **Responsive Limit** | Limit tab counts to 6 per block for mobile compatibility. |
| **State Persistence** | The active tab is tracked by the SPA router. Selecting "pnpm" on one page activates it on subsequent pages. |
::: callout tip "AI Context Mapping"
Always include the target language or platform in the tab label (e.g., `== tab "TypeScript"`). This helps AI agents instantly identify the correct context stream without having to infer it from code content.
:::
---
## [Tags](https://docs.docmd.io/content/containers/tags/)
---
title: "Tags"
description: "Use the tag container to label versions, statuses, or highlight short text snippets inline."
---
The `tag` container is a self-closing component that inserts small, pill-shaped badges inline. Tags retain their compact proportions everywhere - they do not inherit heading sizes or surrounding text styles.
## Syntax Reference
```markdown
::: tag "Label text" [property:value...]
```
| Parameter | Type | Description |
| :--- | :--- | :--- |
| **Label** | `"String"` | The text displayed inside the pill-shaped badge. |
| **Colour** | `color:VALUE` | Applies a background colour (supports CSS names or Hex codes). Automatically calculates a contrasting text colour. |
| **Icon** | `icon:NAME` | Adds a [Lucide](external:https://lucide.dev/icons) icon inside the badge. |
| **URL** | `url:URL` | Makes the tag a clickable hyperlink. Prefix with `external:` to force a new tab. Matches the unquoted-URL convention used by [buttons](button). |
## Examples
### Version Badge
Use a coloured tag inline to mark when a feature was introduced.
```markdown
This feature was added in ::: tag "v0.8.2" color:blue and works perfectly.
```
This feature was added in ::: tag "v0.8.2" color:blue and works perfectly.
### Status Labels
Use tags for status indicators across a page. Colours are fully customisable.
```markdown
::: tag "Deprecated" color:#ef4444
::: tag "Beta" color:#eab308
::: tag "Stable" color:#22c55e
::: tag "Verified" icon:check-circle color:#10b981
```
::: tag "Deprecated" color:#ef4444
::: tag "Beta" color:#eab308
::: tag "Stable" color:#22c55e
::: tag "Verified" icon:check-circle color:#10b981
### Linked Tag
Add `url:` to make a tag act as a hyperlink, useful for cross-referencing release notes or external resources. The value is unquoted to match the convention used by [buttons](buttons.md).
```markdown
Check out the latest ::: tag "Release Notes" icon:external-link url:/release-notes/0-8-2.md
```
Check out the latest ::: tag "Release Notes" icon:external-link url:/release-notes/0-8-2.md
### External Link
Prefix the URL with `external:` to force the link to open in a new tab, even when the target lives on your own domain.
```markdown
::: tag "GitHub" icon:github url:external:https://github.com/docmd-io/docmd
```
::: tag "GitHub" icon:github url:external:https://github.com/docmd-io/docmd
---
## [Frontmatter Reference](https://docs.docmd.io/content/frontmatter/)
---
title: "Frontmatter Reference"
description: "The complete guide to page-level metadata and configuration."
---
Frontmatter overrides global settings for specific pages. Write it in YAML format at the top of your Markdown files.
## Core Metadata
| Key | Type | Description |
| :--- | :--- | :--- |
| `title` | `String` | **Required.** Sets the HTML `` and the primary section header. |
| `description` | `String` | Sets the meta description for SEO and search results. |
| `keywords` | `Array` | A list of keywords for the ` ` tag. |
::: callout warning "Title is Important" icon:triangle-alert
The `title` field is strongly recommended. Without it, the engine falls back to the first `# H1` heading or the filename. This can produce less ideal search results.
:::
## Visibility & Indexing
| Key | Type | Description |
| :--- | :--- | :--- |
| `noindex` | `Boolean` | Excludes the page from the internal search index. |
| `llms` | `Boolean` | Set to `false` to exclude this page from AI context files (`llms.txt`). |
| `hideTitle` | `Boolean` | Hides the title from the sticky header. Useful for custom H1s. |
| `bodyClass` | `String` | Adds a custom CSS class to the ` ` tag. |
## Layout Control
| Key | Type | Description |
| :--- | :--- | :--- |
| `layout` | `String` | Set to `full` to use maximum width and hide the TOC sidebar. |
| `toc` | `Boolean` | Set to `false` to disable the Table of Contents entirely. |
| `noStyle` | `Boolean` | Disables the entire UI (Sidebar, Header, Footer) for custom pages. |
| `titleAppend` | `Boolean` | Set to `false` to prevent appending the site title to metadata tags. Default is `true`. |
### `noStyle` Component Control
When `noStyle: true` is active, you must opt-in to the components you wish to retain.
```yaml
---
noStyle: true
components:
meta: true # Injects SEO metadata
favicon: true # Injects site favicon
css: true # Injects docmd-main.css
theme: true # Injects theme-specific styling
highlight: true # Injects syntax highlighting
scripts: true # Injects the SPA router logic
sidebar: true # Injects the navigation sidebar
footer: true # Injects the site footer
---
```
## Plugin Overrides
### SEO (`seo`)
* `image`: Custom social share image URL for the page.
* `aiBots`: Set to `false` to block AI crawlers from this page.
* `canonicalUrl`: Sets a custom canonical link for SEO.
---
## [Live Preview](https://docs.docmd.io/content/live-preview/)
---
title: "Live Preview"
description: "Run the engine entirely in the browser without a backend server using the Live architecture."
---
The compiler separates filesystem operations from core logic. The core engine can therefore run entirely in the browser, powering live editors and CMS previews without a Node.js backend.
::: button "Open Live Editor" external:https://live.docmd.io
## The Live Editor
The built-in Live Editor provides a high-performance, split-pane interface. Author your Markdown in the left pane. Watch the rendered output update and sync in real-time on the right.
### Local Execution
Launch the Live Editor locally within your project:
```bash
npx @docmd/core live
```
### Static Distribution
Generate a standalone, static version of the editor. Host it on platforms like Vercel or GitHub Pages:
```bash
npx @docmd/core live --build-only
```
This generates a `dist/` directory. It contains the `index.html` entry point and the bundled `docmd-live.js` engine.
## Embedding @docmd/core
Add the browser-compatible bundle to a third-party app to render Markdown on the client.
### 1. Resource Integration
Include the CSS and JavaScript bundles from your assets or a CDN:
```html
```
### 2. Isomorphic API
The global `docmd` object exposes a `compile` method for instant rendering.
```javascript
const html = await docmd.compile(markdown, {
"title": "Dynamic Preview",
"theme": { "appearance": "dark" }
});
document.getElementById("preview-frame").srcdoc = html;
```
::: callout tip "AI Feedback Loops" icon:sparkles
The Live architecture is ideal for building **AI-Agent Sandboxes**. Pipe an agent's suggested changes to a live-compilation buffer. Visually verify AI suggestions before committing changes to your repository.
:::
---
## [docmd : Bespoke No-Style Page Demo](https://docs.docmd.io/content/no-style-example/)
---
title: "docmd : Bespoke No-Style Page Demo"
description: "A functional demonstration of the noStyle architectural feature."
noStyle: true
components:
meta: true
favicon: true
css: true
theme: true
scripts: true
mainScripts: true
copyCode: true
customHead: |
---
Bespoke Page Architecture
Demonstrating the absolute layout control enabled via noStyle: true.
Logical Foundation
This demonstration utilises the noStyle: true frontmatter directive to bypass the global documentation layout (Sidebar, Header, and TOC). This provides a "Zero-Friction" canvas for creating marketing landing pages or custom product dashboards.
Enabled System Components
When in No-Style mode, you explicitly opt-in to the documentation engine's core features:
SEO Meta Engine : Structured tags and social graph data are retained.
Project Branding : Global favicon injection remains active.
Foundational Typography : The processed docmd-main.css provides base styling.
Theme Synchronisation : Light/Dark mode state is fully preserved.
Interactive Capabilities : The SPA router and component logic remain available.
Technical Implementation
The layout for this page is authored using standard HTML wrappers and scoped CSS defined within the customHead frontmatter field. This ensures zero CSS leakage to the rest of the documentation site.
Analyse the Implementation Guide →
---
## [No-Style Pages](https://docs.docmd.io/content/no-style-pages/)
---
title: "No-Style Pages"
description: "Create custom landing pages and unique layouts by disabling the default docmd theme."
---
docmd allows you to bypass the standard documentation layout (Sidebar, Header, Footer) on a per-page basis. This is ideal for creating landing pages or custom dashboards while retaining access to the engine's components.
## Enabling No-Style Mode
To disable the global UI, add `noStyle: true` to the page's frontmatter.
```yaml
---
title: "Product Showcase"
noStyle: true
components:
meta: true # Retain SEO and OpenGraph tags
favicon: true # Retain site favicon
css: true # Inject docmd-main.css for typography
---
Next-Gen Documentation
Zero-config. Isomorphic. AI-Ready.
::: callout info "Infinite Nesting Support" icon:info
Even with `noStyle: true`, all standard docmd containers like `::: card`, `::: tabs`, and `::: hero` are fully supported and can be nested infinitely.
:::
```
## Component Opt-In
When `noStyle` is active, you start with a blank canvas. Selectively re-enable core system components as needed:
| Component | Description |
| :--- | :--- |
| `meta` | Injects ``, SEO meta tags, and structured OpenGraph data. |
| `favicon` | Injects the project-wide favicon. |
| `css` | Injects `docmd-main.css`. Highly recommended for foundational grid and typography. |
| `menubar` | Injects the site's top menubar. |
| `theme` | Injects the active theme's CSS variables and appearance overrides. |
| `scripts` | Injects interactive component logic (requires `mainScripts: true`). |
| `spa` | Enables the single-page application router (requires `scripts: true`). |
## Composable Landing Pages
The primary power of `noStyle` is using docmd components as high-fidelity "widgets" on a blank canvas. You aren't limited to raw HTML; you can build complex structural designs purely in Markdown.
### Building a Modern Entry Point
```yaml
---
title: "Welcome"
noStyle: true
components:
meta: true
css: true
menubar: true # Use the site's top navigation
scripts: true # Enable interactive components
mainScripts: true
---
::: hero layout:split glow:true
# Build Documentation that Wows.
The zero-config engine for modern engineering teams.
::: button "Get Started" ../getting-started/quick-start.md color:blue
::: button "GitHub" github:docmd-io/docmd color:gray
== side
::: embed "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
:::
:::
::: grids
::: card "Zero Configuration"
Just write markdown. No complex React logic or build scripts.
:::
::: card "AI Optimised"
Structure-aware parsing for the LLM era.
:::
::: card "Fast Without the Framework Tax"
Static generation with isomorphic SPA navigation.
:::
:::
```
::: callout tip "AI-Generated Layouts" icon:lightbulb
Because `noStyle` pages support raw HTML alongside docmd containers, they are perfectly suited for **AI-driven UI design**. Prompt an AI: *"Design a modern hero section using utility classes and docmd buttons, wrapped in a noStyle container."* The AI can iterate within your static site pipeline with zero configuration.
:::
## String Replacement (i18n for noStyle)
When your site has [i18n configured](../configuration/localisation/index.md), themed documentation pages get full server-side translations automatically. However, `noStyle` pages use custom HTML. docmd provides **string replacement** to translate HTML via `data-i18n` attributes and JSON translation files.
::: callout info "Why this only works for noStyle pages" icon:info
String replacement finds elements with `data-i18n` attributes and swaps their text content. Standard Markdown content renders to plain ``, `
`, ` ` tags without these attributes. For standard Markdown, use [Directory Mode](../configuration/localisation/translated-content.md).
:::
### How It Works
There are two modes for string replacement:
- **Server-side (recommended)**: With `stringMode: true` in your i18n config, docmd resolves `data-i18n` attributes **at build time**. It generates fully translated HTML in `/{locale}/` directories for search engines.
- **Client-side**: The `docmd-i18n-strings.js` script loads translations at runtime via XHR. This is useful for in-place switching without page reloads.
Both modes use the same `data-i18n` attribute syntax and JSON file format.
1. Place JSON translation files inside `assets/i18n/` - one per locale:
```text
assets/
i18n/
en.json
hi.json
zh.json
```
2. Each JSON file is a flat key-value map:
```json "assets/i18n/en.json"
{
"hero.title": "Markdown → Production Docs",
"hero.subtitle": "The zero-config documentation engine.",
"nav.docs": "Documentation",
"nav.editor": "Live Editor",
"cta.getStarted": "Get Started",
"cta.install": "npm i @docmd/core"
}
```
3. Use `data-i18n` attributes on your HTML elements:
```html
Markdown → Production Docs
The zero-config documentation engine.
Documentation
```
The default language text serves as the fallback. When a non-default locale is active, the engine replaces the text.
### Attribute Translation
To translate attributes like `placeholder`, `title`, or `aria-label`, use `data-i18n-{attr}`:
```html
☰
Docs
```
### HTML Content
For keys containing HTML markup, use `data-i18n-html` instead of `data-i18n`:
```html
Static HTML for SEO. SPA for speed.
```
### Switching Locales
The i18n strings module exposes a global API at `window.DOCMD_I18N_STRINGS`:
```javascript
// Switch language
DOCMD_I18N_STRINGS.switchLocale("hi");
// Get active language
console.log(DOCMD_I18N_STRINGS.locale);
// Get all languages
console.log(DOCMD_I18N_STRINGS.locales);
```
You can build a custom language switcher using this API:
```html
English
हिन्दी
```
### Events
Listen for the `docmd:i18n-applied` event to run custom logic after strings are applied:
```javascript
document.addEventListener("docmd:i18n-applied", function(e) {
console.log("Locale:", e.detail.locale);
console.log("Strings:", e.detail.strings);
});
```
::: callout info "Automatic Detection" icon:info
The script detects the active locale from the URL path prefix. For the default locale, it checks `localStorage` for a previously saved preference. The `switchLocale()` function handles URL navigation automatically.
:::
### In-Place Mode
For single-page sites, set `inPlace: true` in your i18n config to swap strings without URL redirects:
```json "docmd.config.json"
{
"i18n": {
"default": "en",
"locales": [
{ "id": "en", "label": "English" },
{ "id": "zh", "label": "中文" }
],
"inPlace": true
}
}
```
With `inPlace: true`, calling `switchLocale()` reloads the JSON for the new locale and replaces all `data-i18n` strings instantly. No navigation occurs.
---
## [Advanced Markdown Syntax](https://docs.docmd.io/content/syntax/advanced/)
---
title: "Advanced Markdown Syntax"
description: "Extended formatting features: task lists, custom element attributes, footnotes, and semantic definitions."
---
Beyond standard Markdown, docmd supports high-fidelity extensions derived from GitHub Flavored Markdown (GFM) and custom attribute plugins. These tools provide fine-grained control over document structure and styling.
## Task Lists
Create interactive or read-only checklists for roadmap tracking and release planning.
```markdown
- [x] Engine optimisation complete
- [ ] Plugin API finalisation
- [ ] Documentation audit
```
- [x] Engine optimisation complete
- [ ] Plugin API finalisation
- [ ] Documentation audit
## Emojis
Use standard shortcodes to add visual personality. Emoji codes render inline with surrounding text.
```markdown
We :heart: high-performance documentation! :rocket: :sparkles:
```
We :heart: high-performance documentation! :rocket: :sparkles:
## Custom Element Attributes
Assign unique IDs and CSS classes to headings, images, and links using the `{}` syntax.
### Custom IDs
Useful for deep-linking directly to technical subsections.
```markdown
## Performance Benchmarks {#benchmarks-2026}
```
### CSS Classes
Apply styling utilities to specific elements without touching your CSS.
```markdown
## Centre-Aligned Section {.text-centre .text-blue}
```
### Button-Style Links
Transform any standard Markdown link into a styled call-to-action button.
```markdown
[Download Latest Release](#download){.docmd-button}
```
## Footnotes
Add citations or technical deep-dives as footnotes. The engine automatically collects and renders them at the page bottom.
```markdown
Architectural decisions are documented in the RFC.[^1]
[^1]: RFC-42: Isomorphic Rendering Pipeline.
```
Architectural decisions are documented in the RFC.[^1]
[^1]: RFC-42: Isomorphic Rendering Pipeline.
## Definition Lists
Perfect for API parameter descriptions and glossaries.
```markdown
PropName
: The unique identifier for the configuration key.
DefaultValue
: The value used when no override is specified.
```
PropName
: The unique identifier for the configuration key.
DefaultValue
: The value used when no override is specified.
## Abbreviations
Define abbreviations globally within a page. Hovering over the term reveals its full definition.
```markdown
*[SPA]: Single Page Application
The docmd router enables a seamless SPA experience.
```
*[SPA]: Single Page Application
The docmd router enables a seamless SPA experience.
::: callout tip "Contextual Precision for AI"
Definitions and abbreviations provide high-quality technical signals to AI agents. Explicit semantic definitions prevent lexical ambiguity in the `llms.txt` context stream.
:::
---
## [Code Blocks](https://docs.docmd.io/content/syntax/code/)
---
title: "Code Blocks"
description: "Document technical implementations with syntax highlighting, file titles, and one-click copying."
---
docmd uses the ultra-fast `lite-hl` engine for automatic, context-aware syntax highlighting. Specify the language identifier on every fenced block to ensure the correct lexical rules apply.
## Syntax Highlighting
Always name the language after the opening fence. The highlighter applies grammar rules specific to that ecosystem.
````markdown
```typescript
async function build(config: string): Promise {
await initialise(config);
}
```
````
```typescript
async function build(config: string): Promise {
await initialise(config);
}
```
## Block Titles
Follow the language identifier with a quoted filename to render a labelled header above the block. This is useful for referencing configuration files and source paths directly.
````markdown
```json "docmd.config.json"
{
"title": "My Documentation",
"src": "docs/"
}
```
````
```json "docmd.config.json"
{
"title": "My Documentation",
"src": "docs/"
}
```
## Language Support
docmd supports common technical ecosystems out of the box:
* **Logic:** `javascript`, `typescript`, `python`, `rust`, `go`, `ruby`, `csharp`
* **Web:** `html`, `css`, `markdown`
* **Data & Shell:** `json`, `yaml`, `bash`, `powershell`, `dockerfile`
* **Documentation:** `mermaid`, `changelog`
## AI Context Strategy
When documenting code for AI agents, follow these practices:
1. **Label every block explicitly** - use `typescript`, `bash`, `json` rather than relying on auto-detection. This ensures the parser applies the correct grammar for the `llms.txt` stream.
2. **Embed intent in comments** - inline comments explain complex logic and provide critical reasoning context directly inside the code.
::: callout tip "One-Click Portability"
Set `copyCode: true` in your configuration to enable a subtle copy button. It appears on the top-right of every block on hover, allowing readers to copy snippets instantly.
:::
---
## [Images & Visual Media](https://docs.docmd.io/content/syntax/images/)
---
title: "Images & Visual Media"
description: "Embed responsive images, apply styling attributes, and enable interactive lightbox zoom."
---
docmd uses standard Markdown syntax for images. Centralise your media assets in the `assets/images/` directory within your project source for clean, consistent references.
```markdown

```
{.with-border .with-shadow .size-medium .align-centre}
## Sizing
Apply a size class using the `{ }` attribute syntax. Three predefined sizes are available.
```markdown
{ .size-small }
{ .size-medium }
{ .size-large }
```
## Alignment & Decoration
Combine alignment and decoration classes in a single attribute block.
```markdown
{ .align-centre }
{ .align-right .with-shadow .with-border }
```
## Figure Captions
Use the standard HTML5 `` element for precise, accessible image captioning.
```html
Figure 1.1: Core System Infrastructure Architecture.
```
## Image Galleries
Wrap multiple figures in a `div.image-gallery` to produce a responsive, balanced grid.
```html
Live Performance Monitor
Project Global Settings
```
## Lightbox Zoom
When `mainScripts` is active, docmd automatically applies a full-screen zoom effect to any image tagged with the `.lightbox` class or placed inside a gallery.
```markdown
{ .lightbox }
```
::: callout tip "AI Context & Accessibility" icon:sparkles
Always provide descriptive **alt text** for every image. Meaningful alt text is a direct, high-fidelity signal for AI agents parsing the `llms.txt` stream and improves accessibility for screen reader users.
:::
---
## [Markdown Syntax Foundation](https://docs.docmd.io/content/syntax/)
---
title: "Markdown Syntax Foundation"
description: "The baseline formatting rules for all docmd content: typography, structure, lists, and tables."
---
`docmd` adheres to standard **GitHub Flavored Markdown (GFM)** specifications. This page covers the core formatting primitives that apply across every page in your project.
## Typography
| Style | Syntax | Renders As |
| :--- | :--- | :--- |
| **Bold** | `**text**` | **Strong emphasis** |
| *Italic* | `*text*` | *Soft emphasis* |
| ~~Strikethrough~~ | `~~text~~` | ~~Deprecated content~~ |
| `Inline code` | `` `text` `` | `engine.initialise()` |
## Heading Hierarchy
`docmd` derives the page `` automatically from the `title` field in your frontmatter. Begin your heading structure at `##`.
```markdown
## Level 2 - Major Section
### Level 3 - Feature Detail
#### Level 4 - Sub-Detail
```
::: callout tip "Logical Integrity for AI"
AI models and search indexers rely on a strict heading hierarchy to build an accurate mental model of your project. Avoid skipping levels (e.g., jumping from `##` to `####`) to keep the `llms-full.txt` context stream logically sound.
:::
## Lists
Use unordered lists for scannable bullet points and ordered lists for sequential steps. For numbered tutorials, consider the higher-impact [Steps container](../containers/steps.md).
```markdown
* Unordered item
* Another item
1. First step
2. Second step
```
## Blockquotes
The standard `>` syntax highlights external quotes or background context.
```markdown
> The docmd engine redefines the boundaries between static site generation and dynamic application delivery.
```
> The docmd engine redefines the boundaries between static site generation and dynamic application delivery.
## Tables
```markdown
| Parameter | Type | Default |
| :--- | :--- | :--- |
| `name` | `string` | `undefined` |
| `active` | `boolean` | `true` |
```
| Parameter | Type | Default |
| :--- | :--- | :--- |
| `name` | `string` | `undefined` |
| `active` | `boolean` | `true` |
## Embedded HTML
docmd has raw HTML parsing enabled. Inject custom layouts or unique styling directly within Markdown files for specialised UI requirements.
```html
Bespoke UI elements live here.
```
---
## [Linking & Referencing](https://docs.docmd.io/content/syntax/linking/)
---
title: "Linking & Referencing"
description: "Master internal cross-linking, external resources, new-tab behaviour, and static asset referencing."
---
docmd provides a reliable, filesystem-aware linking system. Write links to your source `.md` files naturally in any format - the engine normalises them into clean, SEO-optimised URLs automatically.
::: callout info "Write Naturally, Ship Perfectly"
You do not need special linking conventions. Write `overview.md`, `overview/`, or `overview` - the build engine produces the exact same clean, trailing-slash URL in every case.
:::
## URL Normalisation
During the build process, the engine normalises every internal link automatically. This applies to Markdown text, button containers, tags, and navigation configuration.
| What You Write | What Gets Rendered | Why |
| :--- | :--- | :--- |
| `overview.md` | `overview/` | `.md` extension stripped, trailing `/` added. |
| `overview` | `overview/` | Trailing `/` added automatically. |
| `overview/` | `overview/` | Already correct. No change. |
| `api/commands.md` | `api/commands/` | Subdirectory link normalised. |
| `localisation/index.md` | `localisation/` | `index` stripped, the folder is the canonical URL. |
| `../index.md` | `../` | Parent directory index resolved cleanly. |
| `overview.md#settings` | `overview/#settings` | Hash fragment preserved. |
| `https://example.com` | `https://example.com` | External links pass through untouched. |
## Internal Links
Link to other pages using relative paths to the source `.md` files. The engine resolves them correctly regardless of directory depth.
| Target | Example |
| :--- | :--- |
| Sibling page | `[System Overview](overview.md)` |
| Subdirectory page | `[API Reference](api/node-api.md)` |
| Subdirectory index | `[Localisation](localisation/index.md)` |
| Parent directory | `[Back to Home](../index.md)` |
## Section Anchors
Navigate directly to a heading using standard URL hash fragments.
```markdown
[Jump to Roadmap](#project-roadmap)
[Review CLI Flags](../api/cli-commands.md#available-flags)
```
Hash fragments are preserved through normalisation. The cross-page link above renders as `../api/cli-commands/#available-flags` in production.
## Opening in a New Tab
Prepend `external:` to any link URL to force it to open in a new browser tab. This works in standard Markdown links, buttons, and tags.
```markdown
[Open in New Tab](external:./configuration/overview.md)
[GitHub](external:https://github.com/docmd-io/docmd)
```
The `external:` prefix is stripped from the rendered URL. By default, all links open in the same window.
## Linking to Raw Files
Use the `raw:` prefix to bypass normalisation and link directly to a downloadable file. The extension and path are preserved exactly as written.
```markdown
[View Raw Source](raw:docs/readme.md)
```
## Buttons & Tags
The `::: button` and `::: tag` containers support all standard linking conventions, including `external:` and `raw:` prefixes.
```markdown
::: button "Get Started" ./getting-started/quick-start.md icon:rocket
::: button "View on GitHub" external:https://github.com/docmd-io/docmd icon:github
::: button "Download Source" raw:docs/readme.md icon:download
::: tag "v0.8.2" link:release-notes/0-8-2.md icon:tag color:#22c55e
::: tag "Open Externally" link:external:./configuration/overview.md icon:external-link
```
## Navigation Configuration
Paths defined in `navigation.json` and `docmd.config.json` are normalised at build time. Write them in any format - all three entries below produce the identical canonical URL.
```json "navigation.json"
[
{ "title": "Overview", "path": "configuration/overview" },
{ "title": "Overview", "path": "configuration/overview.md" },
{ "title": "Overview", "path": "configuration/overview/" }
]
```
For items that should open in a new tab, set the `external` flag.
```json "navigation.json"
[
{
"title": "GitHub",
"path": "https://github.com/docmd-io/docmd",
"external": true
}
]
```
::: callout warning "Index Pages in Navigation"
When linking to a directory's index page, use the folder path rather than explicitly referencing `index.md`. Both work identically, but the folder path is cleaner.
```markdown
[Localisation](localisation/)
[Localisation](localisation/index.md)
```
:::
## Protocols & External Resources
The engine respects standard browser protocols for external resources and never modifies these links.
* **HTTPS** - `[docmd Homepage](https://docmd.io)` - opens in the same tab.
* **Mail** - `[Support](mailto:help@docmd.io)` - opens the email client.
* **Assets** - `[Download CLI Binary](/assets/bin/docmd-mac.zip)` - not normalised.
## Static Assets
Place downloadable files within your project's `assets/` directory. The builder moves these files to the production root without path modifications.
```markdown
[Download Documentation PDF](/assets/pdf/handbook.pdf)
[View Raw Global Config](/assets/config/docmd.config.json)
```
::: callout tip "Semantic Linkage for AI"
Prefer **descriptive anchor text** (e.g., `[Optimise PWA caching](../plugins/pwa.md)`) over generic labels (e.g., `[Read more](../plugins/pwa.md)`). Detailed link labels give AI agents a high-fidelity map of semantic relationships in the `llms.txt` stream.
:::
---
## [Caddy](https://docs.docmd.io/deployment/caddy/)
---
title: "Caddy"
description: "Deploy docmd with a production-ready Caddyfile."
---
[Caddy](https://caddyserver.com/) is a modern web server that handles HTTPS provisioning and certificate renewals automatically.
## Generate a Caddyfile
```bash
npx @docmd/core deploy --caddy
```
This generates a `Caddyfile` personalised to your project:
- **Site address** is set to the hostname from your `url` config. Caddy automatically provisions an SSL certificate for it. It falls back to `:80` if no URL is configured.
- **Root directory** uses your configured `out` directory (not hardcoded).
- **SPA fallback** is only included when `layout.spa` is `true` in your config.
### What Gets Generated
```caddy "Caddyfile"
docs.example.com {
root * ./site
file_server
# SPA Routing Fallback (only when layout.spa is true)
try_files {path} {path}/ /index.html
# Security Headers
header {
X-Content-Type-Options "nosniff"
X-Frame-Options "SAMEORIGIN"
-Server
}
# Custom 404
handle_errors {
rewrite * /404.html
file_server
}
# Cache Static Assets (6 months)
@static {
file
path *.ico *.css *.js *.gif *.jpg *.jpeg *.png *.webp *.avif *.svg *.woff *.woff2 *.eot *.ttf *.otf
}
header @static Cache-Control "public, max-age=15552000, immutable"
}
```
When you use a real domain as the site address (e.g., `docs.example.com` instead of `:80`), Caddy automatically provisions a free SSL certificate via Let's Encrypt. Zero HTTPS configuration is needed.
## Deployment Steps
1. Build your site: `npx @docmd/core build`
2. Transfer your output folder and the generated `Caddyfile` to your server.
3. Run `caddy start` or `caddy run` in the directory containing your Caddyfile.
### Re-Generating
Changed your site URL or output directory? Run `npx @docmd/core deploy --caddy` again. The engine regenerates the Caddyfile to match your current `docmd.config.json`.
---
## [Cloudflare Pages](https://docs.docmd.io/deployment/cloudflare-pages/)
---
title: "Cloudflare Pages"
description: "Deploy your docmd documentation to Cloudflare Pages using its global edge network. CI/CD-ready with automatic builds."
---
[Cloudflare Pages](https://pages.cloudflare.com/) hosts your docmd site on Cloudflare's global edge network with zero-configuration CI/CD. Connect your repository, and every push triggers an automatic build and deployment.
## Dashboard Setup
1. Go to the [Cloudflare Dashboard](https://dash.cloudflare.com/) and navigate to **Workers & Pages → Create → Pages**.
2. Connect your git provider (GitHub or GitLab) and select your repository.
3. Configure the build settings:
| Setting | Value |
|---------|-------|
| Framework preset | `None` |
| Build command | `npx @docmd/core build` |
| Build output directory | `site` |
4. Click **Save and Deploy**.
Cloudflare Pages detects the static output and distributes it across its edge network automatically.
## Custom Domain
Add a custom domain under **Pages → your project → Custom domains**. Cloudflare provisions an SSL certificate automatically.
Set the `url` field in `docmd.config.json` to match your domain. This ensures canonical tags, sitemaps, and the LLMs plugin generate correct absolute URLs.
## CI/CD Notes
Cloudflare Pages runs a fresh CI/CD build on every commit pushed to the connected branch. You do not need a separate GitHub Actions workflow. Cloudflare manages the build pipeline.
::: callout info "Why `npx @docmd/core`?"
In CI/CD environments where docmd is not globally installed, `npx @docmd/core` fetches and runs the package directly. If your project lists `@docmd/core` as a `devDependency`, running `npx @docmd/core build` after `npm install` works perfectly.
:::
## SPA Routing
docmd generates each page as its own `index.html`. Direct URL access works without any rewrite rules. No additional Cloudflare configuration is needed.
---
## [Deployer](https://docs.docmd.io/deployment/deployer/)
---
title: "Deployer"
description: "Generate provider-specific deployment configuration files from your docmd project config with a single command."
---
The `deploy` command reads your `docmd.config.json` and generates deployment configuration files tailored to your exact project — output directory, site URL, SPA routing, and Node.js version are all reflected automatically. No generic templates.
## Supported Providers
| Provider | Flag | Files Generated |
| :------- | :--- | :-------------- |
| Docker + Nginx | `--docker` | `Dockerfile`, `.dockerignore` |
| Nginx | `--nginx` | `nginx.conf` |
| Caddy | `--caddy` | `Caddyfile` |
| GitHub Pages | `--github-pages` | `.github/workflows/deploy.yml` |
| Vercel | `--vercel` | `vercel.json` |
| Netlify | `--netlify` | `netlify.toml` |
## Usage
Run from your project root (where `docmd.config.json` lives):
```bash
# Single provider
npx @docmd/core deploy --github-pages
# Multiple providers at once
npx @docmd/core deploy --docker --nginx
# Overwrite existing files
npx @docmd/core deploy --vercel --force
```
## What Gets Personalised
The deploy command reads your configuration (or zero-config defaults) and injects:
| Config Field | Used In |
|:--|:--|
| `title` | Comment headers in every generated file |
| `out` | `COPY` paths in Dockerfile, `root` directives in Nginx/Caddy |
| `url` | `server_name` in Nginx, site address in Caddy |
| `layout.spa` | Controls whether SPA routing fallback is included |
| Config path | Dockerfile build step uses `--config` when non-default |
No `docmd.config.json`? No problem. The command uses the same zero-config defaults as `npx @docmd/core dev` and `npx @docmd/core build`.
## Always In Sync
Every run regenerates your deployment files to match your current config. Changed your site URL or output directory? Just re-run the deploy command. Use `--force` to overwrite existing files without prompts.
## Provider Details
### GitHub Pages
```bash
npx @docmd/core deploy --github-pages
```
Generates `.github/workflows/deploy.yml` with a complete build-and-deploy pipeline. The workflow checks out your repository, installs Node.js, runs `npx @docmd/core build`, and uploads the output to GitHub Pages.
::: callout tip "Using the GitHub Action instead?"
If you want to deploy to GitHub Pages without generating a workflow file yourself, use the [GitHub Action](./github-action) directly — it handles everything in one composable step.
:::
### Docker
```bash
npx @docmd/core deploy --docker
```
Generates a `Dockerfile` using a multi-stage build:
1. **Build stage** — installs your exact pinned `@docmd/core` version and runs the build.
2. **Serve stage** — copies the output into a minimal `nginx:alpine` image.
If an `nginx.conf` already exists in your project root, the Dockerfile automatically copies it into the container.
```bash
# Generate Docker and Nginx configs together
npx @docmd/core deploy --docker --nginx
```
::: callout tip "Official Docker Image"
Looking to run docmd in a container without building a custom image? See the [Docker Image](./docker) page for the official pre-built image.
:::
### Nginx
```bash
npx @docmd/core deploy --nginx
```
Generates `nginx.conf` with SPA routing, gzip compression, and correct `root` path for your output directory. See the [NGINX](./nginx) page for the full generated config.
### Caddy
```bash
npx @docmd/core deploy --caddy
```
Generates a `Caddyfile` with automatic HTTPS, SPA routing, and file serving from your output directory. See the [Caddy](./caddy) page for the full generated config.
### Vercel
```bash
npx @docmd/core deploy --vercel
```
Generates `vercel.json` with SPA routing rules and your configured output directory. See the [Vercel](./vercel) page for deployment steps.
### Netlify
```bash
npx @docmd/core deploy --netlify
```
Generates `netlify.toml` with your build command, publish directory, and SPA redirect rules. See the [Netlify](./netlify) page for deployment steps.
## Trade-offs
Generated configs are opinionated starting points. They are correct for the majority of docmd deployments but may require adjustments for advanced scenarios such as custom domains, CDN rewrites, or multi-region deployments. Always review generated files before deploying to production.
---
## [Docker](https://docs.docmd.io/deployment/docker/)
---
title: "Docker"
description: "Run docmd in a Docker container — use the official pre-built image or generate a custom Dockerfile from your project config."
---
docmd generates static HTML, making it ideal for lightweight, reproducible Docker containers. There are two distinct approaches depending on your use case.
## Official Docker Image
The official image lets you build and serve your documentation without installing anything locally. It supports multiple architectures (`linux/amd64` and `linux/arm64`).
### Quick Start
```bash
# Pull a specific version (recommended — substitute the version you need)
docker pull ghcr.io/docmd-io/docmd:0.8.8
# Build your documentation (mounts local docs and outputs to ./site)
docker run -v $(pwd)/docs:/docs -v $(pwd)/site:/site ghcr.io/docmd-io/docmd:0.8.8 build
# Run the built-in demo site
docker run -p 3000:3000 ghcr.io/docmd-io/docmd:0.8.8
```
::: callout tip "Pinning a version"
We recommend pinning a specific version (e.g. `0.8.8`) for reproducible builds. The `:latest` tag is published automatically starting with 0.8.8, but for production pipelines you should always pin a specific release.
:::
### Docker Compose
Use Docker Compose to build and serve in a single workflow:
```yaml "docker-compose.yml"
version: '3.8'
services:
docs:
image: ghcr.io/docmd-io/docmd:0.8.8
command: build
volumes:
- ./docs:/docs
- ./site:/site
- ./docmd.config.json:/docmd.config.json:ro
serve:
image: nginx:alpine
ports:
- "8080:80"
volumes:
- ./site:/usr/share/nginx/html:ro
depends_on:
- docs
```
### Image Details
| Property | Value |
|:--|:--|
| Base | Alpine Linux (minimal footprint) |
| User | Starts as root, remaps to host uid automatically via `su-exec` |
| Working directory | `/docs` (mount anywhere; use `-w` to override) |
| Health checks | Built-in container health monitoring |
| SBOM | Software Bill of Materials attestation included |
| Architectures | `linux/amd64`, `linux/arm64` |
### Custom working directory and file ownership
The image is configured with `WORKDIR /docs`, but you can mount and run from any path inside the container. Pass `-w` to override the working directory and use a mount path that matches your project layout:
```bash
# Run from a custom working directory inside the container
docker run -v $(pwd):/workspace -w /workspace ghcr.io/docmd-io/docmd:0.8.8 init
```
The entrypoint automatically detects the uid:gid that owns the mounted directory and re-execs as that identity before running any command. Files written by `docmd init`, `docmd build`, or `docmd dev` are always owned by the correct host user — no `-u` flag required.
::: callout warning "Read-only bind mounts"
When using a read-only bind mount (`:ro`) for the config file, make sure the working directory and other mount points remain writable, or `docmd` will fail with a permission error.
:::
## Custom Dockerfile (via Deployer)
For production self-hosting, generate a `Dockerfile` tailored to your project configuration using the [Deployer](./deployer):
```bash
npx @docmd/core deploy --docker
```
This generates a `Dockerfile` using a multi-stage build:
1. **Build stage** — installs your exact pinned `@docmd/core` version and runs the build.
2. **Serve stage** — copies the output into a minimal `nginx:alpine` image.
Generate both Docker and Nginx configs together for a complete self-hosted setup:
```bash
npx @docmd/core deploy --docker --nginx
```
### Build and Run
```bash
docker build -t my-docs .
docker run -p 8080:80 my-docs
```
Your documentation will be live at `http://localhost:8080`.
::: callout tip "Re-generating"
Changed your config? Re-run `npx @docmd/core deploy --docker` to regenerate. Use `--force` to overwrite existing files.
:::
---
## [Firebase Hosting](https://docs.docmd.io/deployment/firebase/)
---
title: "Firebase Hosting"
description: "Deploy your docmd documentation to Firebase Hosting. Works manually or via GitHub Actions."
---
[Firebase Hosting](https://firebase.google.com/products/hosting) delivers your docmd static site over a global CDN with SSL included. It integrates cleanly into CI/CD pipelines via the Firebase CLI or GitHub Actions.
## Prerequisites
Install the Firebase CLI:
```bash
npm install -g firebase-tools
firebase login
```
## Setup
1. Build your site:
```bash
npx @docmd/core build
```
2. Initialise Firebase Hosting in your project root:
```bash
firebase init hosting
```
When prompted:
- Select your Firebase project (or create a new one).
- Set the **public directory** to `site`.
- Configure as a single-page app: **No** (docmd generates individual `index.html` files per page. No catch-all rewrite is needed).
- Do not overwrite `site/index.html`.
3. Deploy:
```bash
firebase deploy --only hosting
```
## CI/CD with GitHub Actions
To deploy automatically on every push, create `.github/workflows/firebase.yml`:
```yaml ".github/workflows/firebase.yml"
name: Deploy to Firebase Hosting
on:
push:
branches:
- main
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "20"
- run: npm install
- run: npx @docmd/core build
- uses: FirebaseExtended/action-hosting-deploy@v0
with:
repoToken: ${{ secrets.GITHUB_TOKEN }}
firebaseServiceAccount: ${{ secrets.FIREBASE_SERVICE_ACCOUNT }}
channelId: live
```
Set `FIREBASE_SERVICE_ACCOUNT` in your repository's **Settings → Secrets** using a Firebase service account JSON key.
::: callout info "Why `npx @docmd/core`?"
In CI/CD environments where docmd is not globally installed, `npx @docmd/core` fetches and runs the package directly. If your project lists `@docmd/core` as a `devDependency`, running `npx @docmd/core build` after `npm install` works perfectly.
:::
## Custom Domain
Add a custom domain in the Firebase Console under **Hosting → Add custom domain**. Firebase provisions SSL automatically.
Set the `url` field in `docmd.config.json` to match your domain. This ensures canonical tags and sitemaps generate correct absolute URLs.
---
## [GitHub Action](https://docs.docmd.io/deployment/github-action/)
---
title: "GitHub Action"
description: "Use the official docmd GitHub Action to build and deploy your documentation to GitHub Pages — zero config, one composable step."
---
The `docmd-io/deploy` action builds your documentation site and outputs the path to the compiled assets, ready for upload to GitHub Pages or any other hosting target. It handles Node.js setup, config detection, dependency installation, and the build step in a single composable action.
::: button "View on GitHub Marketplace" external:https://github.com/marketplace/actions/build-and-deploy-documentation-with-docmd icon:github
::: button "Source Code" external:https://github.com/docmd-io/deploy icon:code
::: callout tip "Starting a new project?"
Use the [Starter Template](./starter-template) — it includes a pre-configured workflow file and a ready-to-go repository structure. The GitHub Action is best for adding docmd deployment to an **existing** repository.
:::
## Quick Start
Add the action to any workflow file in your repository:
```yaml ".github/workflows/docs.yml"
# .github/workflows/docs.yml
name: Deploy Docs
on:
push:
branches: [main]
permissions:
contents: write
pages: write
id-token: write
jobs:
docs:
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deploy.outputs.page_url }}
steps:
- uses: actions/checkout@v4
- uses: docmd-io/deploy@v1
id: build
- uses: actions/upload-pages-artifact@v3
with:
path: ${{ steps.build.outputs.site-dir }}
- uses: actions/deploy-pages@v4
id: deploy
```
## Reusable Workflow
For the absolute minimum boilerplate, use the hosted reusable workflow. It handles permissions, checkout, build, upload, and deploy in one call:
```yaml ".github/workflows/docs.yml"
# .github/workflows/docs.yml
on:
push:
branches: [main]
jobs:
docs:
uses: docmd-io/deploy/.github/workflows/deploy.yml@v1
```
## Inputs
| Input | Default | Description |
|-------|---------|-------------|
| `node` | `20` | Node.js version to use during the build |
## Outputs
| Output | Description |
|--------|-------------|
| `site-dir` | Relative path to the compiled site directory (e.g. `site/`) |
## What the Action Does
The action runs the following steps internally:
1. **Sets up Node.js** using the specified version.
2. **Detects your config** — searches the repository tree (up to two levels deep) for `docmd.config.json`, `docmd.config.js`, or `docmd.config.ts`. Subdirectory configs are fully supported.
3. **Initialises docmd** — if no config is found, runs `npx @docmd/core init` to scaffold one automatically.
4. **Installs dependencies** — runs `npm ci` if a `package.json` is present, otherwise installs `@docmd/core` directly.
5. **Builds the site** — runs `npx @docmd/core build` and reads the output directory from your config.
6. **Outputs the path** — exposes `site-dir` so the upload step knows where to find the compiled assets.
## First-Time Setup
GitHub Pages must be configured to deploy from **GitHub Actions** (not from a branch). This is a one-time step per repository:
1. Go to your repository on GitHub.
2. Navigate to **Settings → Pages**.
3. Under **Source**, select **GitHub Actions**.
4. Save.
After this, every push to `main` triggers a deployment automatically.
## Nested Config Support
If your `docmd.config.json` lives in a subdirectory — for example, `packages/docs/docmd.config.json` in a monorepo — the action detects it and passes `--cwd` to docmd automatically. No manual path configuration is required.
## Project Pages (Subpath Deployment)
GitHub Pages serves project sites at `https://.github.io//`, not at the domain root. docmd handles this automatically — set `url` to your full GitHub Pages URL and docmd derives the correct asset paths from it:
```json "docmd.config.json"
{
"url": "https://username.github.io/my-repo"
}
```
That's it. docmd reads the `/my-repo/` subpath from your URL and uses it for every internal link and asset reference. No additional configuration is needed.
::: callout tip "How docmd knows your deployment path"
docmd derives the asset base path from your `url` config. The `url` you set for SEO and sitemaps is the same value docmd uses to figure out where your assets live. Set it once, everything follows.
If your deployment path ever differs from your URL path (rare, e.g. a CDN that strips a prefix), you can override with `base`. docmd normalises slashes for you, so any of these work:
`"my-repo"`, `"/my-repo"`, `"/my-repo/"`. You don't have to remember which slashes are required.
:::
## Custom Domain
To use a custom domain:
1. Add a `CNAME` file to your `docs/` directory (or your configured assets folder) containing your domain, e.g. `docs.example.com`.
2. Set the `url` field in `docmd.config.json` to your custom domain so sitemaps and canonical tags are correct.
3. Configure the domain in **Settings → Pages → Custom domain**.
## Pinning the Action Version
For production documentation sites, pin to a specific release tag rather than `@v1`:
```yaml ".github/workflows/docs.yml"
- uses: docmd-io/deploy@v1.0.0
id: build
```
This prevents unexpected behaviour from future minor updates.
## Troubleshooting
**`Error: Dependencies lock file is not found`**
This occurs when `actions/setup-node` is configured with `cache: 'npm'` but no `package-lock.json` exists. The `docmd-io/deploy` action handles caching internally — do not add a separate `actions/setup-node` step with `cache: 'npm'` when using this action.
**Build succeeds but the site is not live**
Ensure GitHub Pages is set to deploy from **GitHub Actions**, not from a branch. See [First-Time Setup](#first-time-setup) above.
**Config not detected**
The action searches up to two directory levels. If your config is deeper, pass `--cwd` manually in a custom workflow step or use the [Deployer](./deployer) to generate a tailored workflow file.
---
## [Deployment Overview](https://docs.docmd.io/deployment/)
---
title: "Deployment Overview"
description: "Choose how to deploy your docmd documentation site — from zero-config templates to self-hosted servers and cloud platforms."
---
docmd builds a fully static site. The output is a self-contained folder (default: `site/`) that can be hosted anywhere — no server-side runtime required.
```bash
npx @docmd/core build
```
## Choosing a Deployment Method
There are three main paths depending on your situation:
| Method | Best For |
|:--|:--|
| [Starter Template](./starter-template) | Starting a new project from scratch |
| [GitHub Action](./github-action) | Adding automated deployment to an existing repository |
| [Deployer](./deployer) | Generating server configs (Docker, Nginx, Caddy, Vercel, Netlify) |
## Starter Template
The fastest way to get started. Clone the official template repository — it includes a `docmd.config.json`, a sample page, and a pre-configured GitHub Actions workflow that deploys to GitHub Pages on every push.
→ [Starter Template](./starter-template)
## GitHub Action
The `docmd-io/deploy` action builds your site and outputs the compiled path, ready for upload to GitHub Pages or any other target. Use this to add docmd deployment to an existing repository without changing your project structure.
→ [GitHub Action](./github-action)
## Deployer
The `deploy` command reads your `docmd.config.json` and generates provider-specific configuration files tailored to your project. No generic templates — every file reflects your actual output directory, site URL, and SPA settings.
```bash
# Self-hosted
npx @docmd/core deploy --docker # Dockerfile + .dockerignore
npx @docmd/core deploy --nginx # Production nginx.conf
npx @docmd/core deploy --caddy # Production Caddyfile
# Cloud / CI
npx @docmd/core deploy --github-pages # GitHub Actions workflow
npx @docmd/core deploy --vercel # vercel.json
npx @docmd/core deploy --netlify # netlify.toml
```
→ [Deployer Reference](./deployer)
## Cloud Platforms
For managed hosting without running your own server:
- [Docker Image](./docker) — Official multi-arch image for containerised deployments
- [NGINX](./nginx) — Self-hosted with generated config
- [Caddy](./caddy) — Self-hosted with automatic HTTPS
- [Vercel](./vercel) — Zero-config cloud deployment
- [Netlify](./netlify) — Git-connected continuous deployment
- [Cloudflare Pages](./cloudflare-pages) — Edge-native hosting with built-in CI/CD
- [Firebase Hosting](./firebase) — Google CDN with GitHub Actions integration
## Production Checklist
1. **Site URL** — Set `url` in `docmd.config.json`. This drives canonical tags, sitemaps, social previews, and generated deployment files.
2. **Redirects** — Migrating from another tool? Use the `redirects` config to preserve SEO rankings.
3. **Analytics** — Enable the `analytics` plugin to track engagement and search queries.
4. **AI Context** — Enable the `llms` plugin to generate `llms.txt` for AI agent ingestion.
::: callout tip "Custom 404 Pages"
docmd writes a `404.html` into your output directory. Most static hosts serve it automatically for missing routes.
:::
---
## [Netlify](https://docs.docmd.io/deployment/netlify/)
---
title: "Netlify"
description: "Deploy your docmd documentation to Netlify using a generated netlify.toml."
---
`npx @docmd/core deploy --netlify` generates a `netlify.toml` file at the root of your project. It is pre-configured with the correct build command, publish directory, cache headers, and SPA redirects.
```bash
npx @docmd/core deploy --netlify
```
## What Gets Generated
The `netlify.toml` configures:
- **Build command** - installs `@docmd/core` and runs `npx @docmd/core build`.
- **Publish directory** - set to your configured `out` directory.
- **Node version** - pinned to Node 20.
- **Cache headers** - immutable for assets, no-cache for HTML pages.
- **SPA redirects** - a `/*` → `/index.html` rewrite when `layout.spa` is enabled.
## Deploying
Connect your repository to Netlify from the [Netlify dashboard](external:https://app.netlify.com). It detects the `netlify.toml` automatically and deploys on every push.
Alternatively, use the [Netlify CLI](external:https://docs.netlify.com/cli/get-started/):
```bash
npm install -g netlify-cli
netlify deploy --prod
```
## Re-generating
Re-run `npx @docmd/core deploy --netlify` any time you change `out` or other config fields. This keeps `netlify.toml` in sync.
---
## [NGINX](https://docs.docmd.io/deployment/nginx/)
---
title: "NGINX"
description: "Deploy docmd with a production-ready NGINX configuration."
---
NGINX is one of the most reliable web servers available. Because docmd output is entirely static, NGINX can serve it with near-zero latency.
## Generate nginx.conf
```bash
npx @docmd/core deploy --nginx
```
This generates an `nginx.conf` personalised to your project:
- **`server_name`** is set to the hostname extracted from your `url` config. It falls back to `localhost` if not set.
- **SPA fallback** (`try_files ... /index.html`) is only included when `layout.spa` is `true` in your config.
- **Security headers**, GZIP compression, and immutable asset caching are included by default.
### What Gets Generated
```nginx "nginx.conf"
server {
listen 80;
server_name docs.example.com;
root /usr/share/nginx/html;
index index.html;
# Security
server_tokens off;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
# GZIP Compression
gzip on;
gzip_vary on;
gzip_min_length 256;
gzip_types text/plain text/css application/json application/javascript
text/xml application/xml application/xml+rss text/javascript
image/svg+xml;
# SPA Routing Fallback (only when layout.spa is true)
location / {
try_files $uri $uri/ /index.html;
}
# Custom 404
error_page 404 /404.html;
# Cache Static Assets (6 months, immutable)
location ~* \.(?:ico|css|js|gif|jpe?g|png|webp|avif|woff2?|eot|ttf|otf|svg)$ {
expires 6M;
access_log off;
add_header Cache-Control "public, immutable";
}
}
```
## Deployment Steps
1. Build your site: `npx @docmd/core build`
2. Upload the contents of your output directory to your server's web root (e.g., `/var/www/html/` or `/usr/share/nginx/html/`).
3. Place the generated `nginx.conf` into your server's configuration (e.g., `/etc/nginx/conf.d/default.conf`).
4. Restart NGINX: `sudo systemctl restart nginx`
### Re-Generating
Changed your site URL or switched off SPA mode? Just run `npx @docmd/core deploy --nginx` again. The config file automatically regenerates to match your current `docmd.config.json`.
---
## [Starter Template](https://docs.docmd.io/deployment/starter-template/)
---
title: "Starter Template"
description: "Use the official docmd starter template to create a pre-configured documentation site with GitHub Pages deployment in under a minute."
---
# docmd Starter Template
The `docmd-template` repository is the fastest way to start a new documentation site. It includes a working `docmd.config.json`, a sample page, a `package.json` for local development, and a pre-configured GitHub Actions workflow that deploys to GitHub Pages automatically on every push.
::: button "Use this Template" external:https://github.com/docmd-io/docmd-template/generate icon:github color:#2ea44f
::: button "View Repository" external:https://github.com/docmd-io/docmd-template icon:external-link
## Getting Started
### 1. Create Your Repository
Click **[Use this template](https://github.com/docmd-io/docmd-template/generate)** on GitHub. Give your repository a name and click **Create repository**. You do not need to fork it — the template creates a clean, independent copy.
### 2. Configure Your Site
Open `docmd.config.json` in your new repository and update the `title` and `url` fields:
```json "docmd.config.json"
{
"title": "My Docs",
"url": "https://username.github.io/repo-name"
}
```
Replace `username` and `repo-name` with your GitHub username and repository name.
### 3. Enable GitHub Pages
This is a one-time step per repository:
1. Go to **Settings → Pages**.
2. Under **Source**, select **GitHub Actions**.
3. Save.
### 4. Push and Deploy
Push any change to `main`. The included workflow builds your site and deploys it to GitHub Pages automatically. Your documentation will be live at:
```
https://.github.io//
```
## What's Included
```
.github/
workflows/
docs.yml # Automated build and deploy on push to main
docmd.config.json # Site title, URL, and output directory
docs/
index.md # Your first documentation page
package.json # Local development scripts
```
## Local Development
Clone your repository and run the development server:
```bash
npm install
npm run dev
```
The site is available at `http://localhost:3000` with live reload. Changes to Markdown files are reflected immediately.
To build a production copy locally:
```bash
npm run build
```
The compiled site is written to `site/` by default.
## Included Workflow
The template ships with `.github/workflows/docs.yml`:
```yaml ".github/workflows/docs.yml"
name: Docs
on:
push:
branches: [main, master]
workflow_dispatch:
permissions:
contents: write
pages: write
id-token: write
concurrency:
group: docs
cancel-in-progress: false
jobs:
deploy:
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deploy.outputs.page_url }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-node@v4
with:
node-version: 20
- name: Install
run: npm install @docmd/core
- name: Build
run: npx @docmd/core build
- uses: actions/upload-pages-artifact@v3
with:
path: ./site
- name: Deploy
id: deploy
uses: actions/deploy-pages@v4
```
The workflow installs `@docmd/core` directly without a lock file, which is intentional — the template has no committed `package-lock.json` so `actions/setup-node` caching is not used. This keeps the template dependency-free whilst still deploying reliably.
## Adding Your First Page
Create a new Markdown file in `docs/`:
```bash
docs/
index.md # Home page
getting-started.md
api-reference.md
```
Add a `navigation.json` to control the sidebar:
```json "navigation.json"
[
{ "title": "Home", "path": "/" },
{ "title": "Getting Started", "path": "/getting-started" },
{ "title": "API Reference", "path": "/api-reference" }
]
```
See [Navigation Configuration](../configuration/navigation.md) for the full navigation schema.
## Custom Domain
To use a custom domain (e.g. `docs.example.com`):
1. Update the `url` field in `docmd.config.json`:
```json
{ "url": "https://docs.example.com" }
```
2. Add a `CNAME` file to your `docs/` directory containing your domain.
3. Configure the domain in **Settings → Pages → Custom domain**.
## Starter Template vs GitHub Action
The template gives you full ownership of the workflow file and config from the start. The [GitHub Action](./github-action) is better suited for adding docmd deployment to an existing repository without restructuring it.
| | Starter Template | GitHub Action |
|---|---|---|
| Starting point | New repository | Existing repository |
| Workflow file | Included, yours to edit | You write it, action handles build |
| Config | Pre-configured | Detected or scaffolded automatically |
| Best for | New projects | Adding docs to existing repos |
---
## [Vercel](https://docs.docmd.io/deployment/vercel/)
---
title: "Vercel"
description: "Deploy your docmd documentation to Vercel using a generated vercel.json."
---
`npx @docmd/core deploy --vercel` generates a `vercel.json` file at the root of your project. It is automatically configured for your site's output directory and SPA routing settings.
```bash
npx @docmd/core deploy --vercel
```
## What Gets Generated
The `vercel.json` configures:
- **Build command** - runs `npx @docmd/core build`.
- **Output directory** - set to the `out` property in your config.
- **Install command** - installs the exact `@docmd/core` version used.
- **Cache headers** - immutable caching for assets, no-cache for HTML.
- **SPA routing** - a catch-all route to `index.html` when `layout.spa` is enabled.
## Deploying
After generating the file, deploy using the [Vercel CLI](external:https://vercel.com/docs/cli):
```bash
npm install -g vercel
vercel
```
Alternatively, connect your repository to Vercel from the dashboard. It detects the `vercel.json` automatically.
## Re-generating
If you change your `out` directory or `url` in `docmd.config.json`, re-run the command to regenerate the file. This keeps the configuration in sync.
---
## [Building Plugins](https://docs.docmd.io/development/building-plugins/)
---
title: "Building Plugins"
description: "A comprehensive guide to extending docmd with custom logic, data injection, and interactive features."
---
Plugins are the primary extension mechanism for docmd. They allow you to inject HTML, modify Markdown parsing, inject build-time data, and automate post-build tasks. This guide outlines the plugin API.
## Plugin Descriptor
Every plugin must export a `plugin` descriptor declaring its identity and capabilities. This enables the engine to validate and isolate boundaries at load time.
```javascript
"plugin": {
"name": "my-analytics",
"version": "1.0.0",
"capabilities": ["head", "body", "post-build"]
},
"generateScripts": (config, opts) => { ... },
"onPostBuild": async (ctx) => { ... }
```
> **Note:** The descriptor is strictly required. Plugins without it will fail to load.
## The `docmd` Namespace (new in 0.8.9)
In addition to the runtime `plugin` descriptor, every official plugin **must** declare a `docmd` namespace in its `package.json`. This namespace is the build-time contract that the registry generator reads to build the single source of truth that the runtime loader consumes.
```json "package.json"
{
"name": "@docmd/plugin-foo",
"version": "1.0.0",
"docmd": {
"key": "foo",
"kind": "plugin",
"displayName": "Foo",
"tagline": "What this plugin does in one line",
"capabilities": ["head", "body", "post-build"]
}
}
```
| Field | Required | Description |
| :--- | :--- | :--- |
| `key` | Recommended | The user-facing identifier (`config.plugins.`). Derived from the package name if omitted. |
| `kind` | Recommended | One of `plugin`, `template`, `engine`. Derived from the directory layout if omitted. |
| `displayName` | Recommended | Human-readable name shown in catalogs and `docmd doctor` output. |
| `tagline` | Recommended | One-line description; used as a fallback for the npm description. |
| `capabilities` | Required for plugins and templates | The same hook capabilities the JS descriptor declares. The build-time cross-check warns if the two diverge. |
| `preview` | Optional | Path to a preview asset (template only); shown in catalogs. |
Engines have the same `docmd` namespace but **no `capabilities`** — they don't participate in the hook system, only in the engine loader.
The build-time cross-check (also new in 0.8.9) surfaces drift between the JS descriptor and the manifest, including the "implemented hook without declared capability" silent-drop bug that was previously invisible.
::: callout warning "Bundled registry removal in 0.9.0"
The hand-maintained `packages/plugins/installer/registry/plugins.json` that used to be the catalog of official plugins is **deprecated** as of 0.8.9 and will be **removed in 0.9.0**. The build-time registry generator is now the single source of truth — your plugin only needs a correct `docmd` namespace in its `package.json`, and the generator picks it up on the next `pnpm build` of `@docmd/api`. No code changes required for existing official plugins.
:::
## Core Capabilities
The `capabilities` array dictates which hooks your plugin is allowed to use.
| Capability | Allowed Hooks | Phase |
| :--- | :--- | :--- |
| `init` | `onConfigResolved` | Init |
| `markdown` | `markdownSetup` | Setup |
| `head` | `generateMetaTags`, `generateScripts` (head) | Render |
| `body` | `generateScripts` (body) | Render |
| `build` | `onBeforeParse`, `onAfterParse`, `onBeforeBuild`, `onBeforeRender`, `onPageReady` | Build |
| `post-build`| `onPostBuild` | Post-Build |
| `dev` | `onDevServerReady` | Dev Server |
| `assets` | `getAssets` | Output |
| `actions` | `actions` | Interactive |
| `events` | `events` | Interactive |
| `translations`| `translations` | i18n |
| `template` *(new in 0.8.7)* | `templates`, `templateAssets` | Render |
> **Note:** the `template` capability is exclusive — if a plugin declares it, it cannot also declare `head`, `build`, `post-build`, etc. Templates ship slots and assets only; they do not run lifecycle hooks. If you need both, ship two separate packages.
## Plugin API Reference
A docmd plugin is a standard JavaScript object that implements one or more of the following hooks.
| Hook | Description |
| :--- | :--- |
| `markdownSetup(md, opts)` | Extend the `markdown-it` instance. Synchronous. |
| `generateMetaTags(config, page, root)` | Inject ` ` or ` ` tags into the ``. |
| `generateScripts(config, opts)` | Return an object containing `headScriptsHtml` and `bodyScriptsHtml`. |
| `getAssets(opts)` | Define external files or CDN scripts to be injected. |
| `onPostBuild(ctx)` | Run logic after the generation of all HTML files. |
| `translations(localeId)` | Return an object of translated strings for the given locale. |
| `actions` | An object of named action handlers for WebSocket RPC calls. |
| `events` | An object of named event handlers for browser messages. |
| `templates[]` *(new in 0.8.7, capability: `template`)* | Array of `TemplateHook` entries — each `{ type, templatePath }` overrides one EJS slot. |
| `templateAssets[]` *(new in 0.8.7, capability: `template`)* | Array of `TemplateAssetHook` entries — each `{ type, path, priority?, position? }` ships the template's CSS/JS bundle. |
### Building a template plugin (new in 0.8.7)
A template is a plugin with `capabilities: ['template']`. It ships a `templates[]` array (slot overrides) and a `templateAssets[]` array (CSS/JS bundle). See the dedicated [Templates guide](../theming/templates.md) and [Theming → Templates](../theming/templates.md) for the full authoring walkthrough, slot table, and resolution chain. The minimum viable template looks like:
```javascript
export default {
plugin: {
name: 'template-foo',
version: '1.0.0',
capabilities: ['template'],
},
templates: [
{ type: 'menubar', templatePath: '/abs/path/to/templates/partials/menubar.ejs' },
{ type: 'footer', templatePath: '/abs/path/to/templates/partials/footer.ejs' },
],
templateAssets: [
{ type: 'css', path: '/abs/path/to/assets/css/foo.css', priority: 10, position: 'head' },
],
};
```
## Creating a Local Plugin
Creating a plugin is as simple as defining a JavaScript file. For example, `my-plugin.js`:
```javascript
import path from "path";
export default {
plugin: {
"name": "my-plugin",
"version": "1.0.0",
"capabilities": ["head", "post-build"]
},
markdownSetup: (md, options) => {
// Add custom parser rules
},
generateMetaTags: async (config, page, relativePathToRoot) => {
return ` `;
},
onPostBuild: async ({ config, pages, outputDir, log, options }) => {
log(`Custom Plugin: Verified ${pages.length} pages.`);
}
};
```
To enable your plugin, reference its **full package name** in your `docmd.config.json`:
```json "docmd.config.json"
"plugins": {
"my-awesome-plugin": {}
}
```
> **Note:** Shorthand names (e.g. `math`, `search`) are reserved for official `@docmd/plugin-*` packages. Third-party plugins must always use their full npm package name.
### Plugin Resolution
The docmd engine resolves plugin names as follows:
- **Official shorthands** (`math`, `search`) expand to `@docmd/plugin-`. Only official packages can exist under the `@docmd` scope.
- **Third-party plugins** must use their full package name (e.g. `my-awesome-plugin`, `@myorg/docmd-extras`). There is no alias system for external plugins. This eliminates supply-chain attack vectors.
### Plugin Isolation
Every hook invocation is wrapped in a try/catch block. A broken plugin cannot crash the build or interfere with other plugins. Errors are logged and collected into a summary.
### Scoping Plugins (`noStyle`)
Plugins inject their CSS/JS universally by default. Developers can explicitly prevent their plugin from rendering on `noStyle` pages by exporting a `noStyle` boolean:
```javascript
export default {
noStyle: false,
generateScripts: () => { ... }
}
```
Users can override this via configuration (`plugins: { math: { noStyle: false } }`) or dynamically via Markdown frontmatter (`plugins: { math: true }`).
## Lifecycle Hooks
Docmd provides deep integration hooks. They allow plugins to manipulate configuration, raw sources, and page data.
| Hook | Description | Expected Return |
| :--- | :--- | :--- |
| **`onConfigResolved(config)`** | Reads or modifies the active config right after initialisation. | `void` or `Promise` |
| **`onDevServerReady(server, wss)`** | Exposes the raw Node.js server during `npx @docmd/core dev`. | `void` or `Promise` |
| **`onBeforeParse(src, frontmatter, filePath?)`** | Pre-processes raw markdown string data immediately before parsing. | `string` or `Promise` |
| **`onAfterParse(html, frontmatter, filePath?)`** | Post-processes generated HTML representing the markdown body. | `string` or `Promise` |
| **`onBeforeBuild(ctx)`** | Called after all markdown is parsed but before HTML generation. Used for heavy pre-computation. | `void` or `Promise` |
| **`onBeforeRender(page)`** | Called before template rendering. Mutations to `frontmatter` and `html` are reflected in output. | `void` or `Promise` |
| **`onPageReady(page)`** | Accesses fully assembled page metadata just before it is written to the destination file. | `void` or `Promise` |
### Engine Acceleration & Background Tasks (`runWorkerTask`)
docmd executes intensive operations via a **Pluggable Engine Architecture**. Plugins can easily offload custom heavy I/O or CPU-bound subroutines through the configured build engine (e.g., JavaScript or native Rust workers).
The `runWorkerTask` method is injected transparently into `PageContext`, `PostBuildContext`, and `ActionContext`.
```javascript
{
"plugin": { "name": "my-plugin", "version": "1.0.0", "capabilities": ["post-build"] },
"onPostBuild": async (ctx) => {
// Pass a registered engine action name or absolute script path
const result = await ctx.runWorkerTask('/path/to/worker.js', 'parseData', [ctx.outputDir]);
}
}
```
### Data Fetching and Indexing (`onBeforeBuild`)
The `onBeforeBuild` hook runs *after* markdown parsing but *before* the HTML rendering loop begins. It is optimal for heavy data indexing or API calls.
It receives the `BeforeBuildContext`, containing all `pages` and the `tui` instance. This allows plugins to show isolated progress bars.
```typescript
export async function onBeforeBuild({ pages, tui }) {
tui.step('Fetching remote plugin data', 'WAIT');
let processed = 0;
for (const page of pages) {
if (page.sourcePath) {
page.frontmatter.remoteData = await fetchHeavyData(page.sourcePath);
}
processed++;
if (processed % 10 === 0 || processed === pages.length) {
tui.progress('Fetching remote plugin data', processed, pages.length);
}
}
tui.step('Fetching remote plugin data', 'DONE');
}
```
### `onBeforeRender` and `PageContext`
Use `onBeforeRender` to inject build-time data derived from the source file.
```typescript
interface PageContext {
sourcePath: string;
frontmatter: Record;
html: string;
localeId?: string;
versionId?: string;
relativePathToRoot?: string;
runWorkerTask(modulePath: string, functionName: string, args: any[]): Promise;
}
```
```javascript
export default {
plugin: {
name: "my-metadata-plugin",
version: "1.0.0",
capabilities: ["build"]
},
onBeforeRender: async (page) => {
const stats = fs.statSync(page.sourcePath);
page.frontmatter.wordCount = page.html.split(/\s+/).length;
page.frontmatter.fileSize = stats.size;
}
}
```
## Deep Dive: Asset Injection
The `getAssets()` hook allows your plugin to bundle client-side logic securely.
```javascript
export default {
getAssets: (options) => {
return [
{
url: "https://example.com/script.js",
type: "js",
location: "head"
},
{
src: path.join(__dirname, "plugin-init.js"),
dest: "assets/js/plugin-init.js",
type: "js",
location: "body"
}
];
}
}
```
## Translating Plugins (i18n)
Plugins rendering client-side UI should expose strings via the `translations(localeId)` hook. The engine merges these with core strings automatically.
The standard pattern stores a JSON file for each language in an `i18n/` directory:
```javascript
import fs from "fs";
import path from "path";
export default {
plugin: {
name: "my-plugin",
version: "1.0.0",
capabilities: ["translations", "body"]
},
translations: (localeId) => {
try {
const p = path.join(__dirname, "i18n", `${localeId}.json`);
return JSON.parse(fs.readFileSync(p, "utf8"));
} catch { }
return {};
}
}
```
## WebSocket RPC Actions
Plugins can register **action handlers** and **event handlers** that run on the dev server. They are callable from the browser via the `window.docmd` API.
```javascript
export default {
plugin: {
name: "my-live-plugin",
version: "1.0.0",
capabilities: ["actions", "events"]
},
actions: {
"my-plugin:save-note": async (payload, ctx) => {
const content = await ctx.readFile(payload.file);
const updated = content + "\n\n> " + payload.note;
await ctx.writeFile(payload.file, updated);
return { "saved": true };
}
},
events: {
"my-plugin:page-viewed": (data, ctx) => {
console.log(`Page viewed: ${data.path}`);
}
}
};
```
The `ctx` (ActionContext) provides:
| Method | Description |
| :--- | :--- |
| `ctx.readFile(path)` | Read a file relative to the project root. |
| `ctx.writeFile(path, content)` | Write a file (triggers rebuild + reload). |
| `ctx.readFileLines(path)` | Read a file as an array of lines. |
| `ctx.broadcast(event, data)` | Push an event to all connected browsers. |
| `ctx.runWorkerTask(module, fn, args)` | Offload heavy CPU tasks to the worker pool. |
| `ctx.source` | Source editing tools for block-level markdown manipulation. |
| `ctx.projectRoot` | Absolute path to the project root. |
| `ctx.config` | Current docmd site configuration. |
All file operations are sandboxed to the project root.
::: callout info "Dev Mode Only 🛡️"
The WebSocket RPC system is only active during `npx @docmd/core dev`. Production builds do not include the API client or server-side action handling.
:::
## Best Practices
1. **Declare Capabilities**: Always export a `plugin` descriptor with declared capabilities.
2. **Use `onBeforeRender` for data injection**: If your plugin computes frontmatter fields, use `onBeforeRender`.
3. **Async/Await**: Always use `async` functions for `onPostBuild`, `onBeforeRender`, and action handlers.
4. **Statelessness**: Avoid maintaining state within the plugin object. The engine may re-initialise plugins dynamically.
5. **Naming Convention**: Prefix community package names with `docmd-plugin-`.
6. **Action Namespacing**: Prefix action names with your plugin name (e.g., `my-plugin:save-note`).
7. **Action Validation**: Define and require an explicit payload schema in your actions.
8. **Logging**: Use the provided `log()` helper in `onPostBuild` to respect user verbosity settings.
::: callout tip "AI-Ready Design 🤖"
The docmd plugin API is **LLM-Optimal**. Because the hooks use standard JavaScript objects, AI agents can generate bug-free plugins with minimal instruction.
:::
## ESM Exports — the `default` Condition
Your plugin's `package.json` **must** include a `"default"` condition in
`exports["."]`, alongside the `import` condition:
```json
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js",
"default": "./dist/index.js"
}
}
```
If you declare only `import`, the auto-installer's first attempt throws
`ERR_PACKAGE_PATH_NOT_EXPORTED` because Node's CommonJS resolver cannot
match any condition. The retry path will still succeed (it uses dynamic
`import()` directly), but the build will print a redundant "Plugin
installed" TUI line every time.
This convention matches what `@docmd/plugin-git`, `@docmd/plugin-openapi`,
and `@docmd/plugin-threads` already ship with. Templates (`@docmd/template-*`)
have the same requirement.
---
## [Building Templates](https://docs.docmd.io/development/building-templates/)
---
title: "Building Templates"
description: "Author a docmd template package — directory layout, descriptor, EJS context, asset priorities, and API reference."
---
# Building Templates
::: callout info
**For template authors.** If you want to *use* a template in your docs site, see [Templates](/theming/templates) instead.
:::
A template is a regular npm package that declares `capabilities: ['template']` and ships a `templates[]` array of `.ejs` file overrides. The template resolver in `@docmd/ui` handles the per-page lookup, honours frontmatter / config overrides, and falls back to the default if anything goes wrong.
## Package layout
```
@docmd/template-summer/
├── package.json
├── index.js # Plugin entry — exports templates[] + templateAssets[]
├── templates/
│ ├── layout.ejs
│ ├── partials/
│ │ ├── menubar.ejs # Only the partials you need to override
│ │ └── footer.ejs
└── assets/
├── css/
│ └── summer.css # Layers on top of docmd-main.css; does not replace it.
└── js/
└── summer.js
```
## `package.json`
```json "package.json"
{
"name": "@docmd/template-summer",
"version": "0.1.0",
"type": "module",
"main": "index.js",
"peerDependencies": {
"@docmd/core": ">=0.8.7"
},
"docmd": {
"kind": "template",
"displayName": "Summer",
"description": "A bright summer-inspired layout for the 0.8.7+ template system."
}
}
```
## ESM Exports — the `default` Condition
Your template's `package.json` **must** include a `"default"` condition in
`exports["."]`, alongside the `import` condition:
```json
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js",
"default": "./dist/index.js"
}
}
```
If you declare only `import`, the auto-installer's first attempt throws
`ERR_PACKAGE_PATH_NOT_EXPORTED` because Node's CommonJS resolver cannot
match any condition. The retry path will still succeed (it uses dynamic
`import()` directly), but the build will print a redundant "Plugin
installed" TUI line every time. Plugins (`@docmd/plugin-*`) have the same
requirement — see the [plugin development guide](building-plugins.md#esm-exports--the-default-condition)
for the full context.
## `index.js`
```js "index.js"
import path from 'node:path';
import { fileURLToPath } from 'node:url';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
export default {
plugin: {
name: 'template-summer',
version: '0.1.0',
capabilities: ['template'],
},
templates: [
// Only the slots you actually want to override.
{ type: 'layout', templatePath: path.join(__dirname, 'templates/layout.ejs') },
{ type: 'menubar', templatePath: path.join(__dirname, 'templates/partials/menubar.ejs') },
{ type: 'footer', templatePath: path.join(__dirname, 'templates/partials/footer.ejs') },
],
templateAssets: [
{
type: 'css',
path: path.join(__dirname, 'assets/css/summer.css'),
priority: 10, // higher than theme (5), lower than customCss (15)
position: 'head',
},
{
type: 'js',
path: path.join(__dirname, 'assets/js/summer.js'),
priority: 10,
position: 'body',
},
],
};
```
## `layout.ejs` context
Templates receive the same EJS context as the default layout. The most common locals:
| Local | Description |
|---|---|
| `config` | The normalised site config. |
| `frontmatter` | Per-page frontmatter. |
| `relativePathToRoot` | E.g. `./` or `../` — use this to build relative URLs. |
| `renderIcon(name, opts)` | Render a Lucide icon. |
| `t(key, params?)` | Translation function. |
| `buildRelativeUrl(url)` | Resolve a URL relative to the current page. |
| `pageTitle`, `siteTitle`, `appearance` | Common strings. |
| `_template` | Metadata about the resolved template (new in 0.8.7). |
You can include default partials from `@docmd/ui` by reading them at build time. The simplest pattern is to keep a copy of the partials you reuse; templates do not inherit partial paths automatically.
## Asset priority chain
CSS and JS load in this order (lower loads first, higher wins cascade ties):
| Priority | Layer | Notes |
|---|---|---|
| 0 | Base (`docmd-main.css`, `docmd-main.js`) | Always present. |
| 5 | Theme colour overlay (`docmd-theme-sky.css`, etc.) | From `theme.name`. Skipped when the name auto-promoted to a template (see `_noCssOverlay`). |
| 10 | **Template structure** (default) | Your template's CSS — this is the default if you omit `priority`. |
| 15 | User `customCss` / `customJs` | Always wins — that's the contract. |
| 20 | Plugin CSS/JS | lightbox, search, analytics, etc. |
| 25+ | Higher template priority | **Use only when you must override plugins.** The official Summer template declares `priority: 25` so it loads after plugin CSS. Higher values cascade later. |
Templates may declare a higher priority than 10 — Summer itself uses **25** so it overrides plugin styles. The recommended band is **10–20** for "user-overridable" templates and **20+** for "opinionated layout" templates.
::: callout warning "Do not use !important"
Templates should write CSS that can be overridden by `customCss` at priority 15. Using `!important` breaks the contract and means users can't restyle your template without forking it. (Summer's CSS file header enforces this — `!important` is removed during 0.8.7 cleanup so users can finally override Summer without resorting to `!important` themselves.)
:::
## Auto-promotion of `theme.name`
The `theme.name` → `theme.template` promotion happens inside `normalizeConfig()`, not the resolver:
- When `theme.name` is a non-reserved value and `theme.template` is unset, the config is rewritten to `theme.template = theme.name` and `theme._noCssOverlay = true` (so the generator skips the `docmd-theme-${name}.css` lookup that would 404).
- At resolve time the resolver only ever sees `theme.template`.
This is why a non-reserved `theme.name` automatically loads your template — no need to also list it in `config.plugins`.
## Template localisation
The `i18n` config still applies — the active locale is passed to your template as a normal local. Translations are looked up via the `t(key)` helper as in the default templates.
## API reference
### `resolveTemplate(ctx)` from `@docmd/ui`
```ts
import { resolveTemplate } from '@docmd/ui';
const resolved = resolveTemplate({
type: 'layout', // any TemplateSlot
pagePath: '/guide/intro.html',
frontmatter: page.frontmatter, // may carry `template: "..."`
config, // normalised site config
localeId: 'en', // optional
versionId: '0.8', // optional
});
// resolved.templatePath → absolute path to the .ejs file
// resolved.source → 'default' | 'frontmatter' | 'config' | 'plugin'
// resolved.pluginName → plugin name (when source === 'plugin')
// resolved.type → the resolved slot
```
### Types from `@docmd/api`
```ts
import type {
TemplateSlot, // union of 12 slot names
TemplateHook, // { type, templatePath, priority?, pages?, exclude? }
TemplateAssetHook, // { type: 'css'|'js', path, priority?, position? }
ResolvedTemplate,
TemplateResolutionContext,
Capability, // now includes 'template'
} from '@docmd/api';
```
## Troubleshooting
### "Template declared slot X but file not found"
The template's `index.js` listed a `templatePath` that does not exist on disk. The resolver fell back to the default. Check the path is absolute (use `fileURLToPath(import.meta.url)`) and the file is included in the published package's `files` field.
### My template's CSS is being overridden by something else
CSS priority is final. User `customCss` (priority 15) always wins. If you want users to be able to override specific selectors without overriding the whole template, document the public CSS class names and let users target them with `customCss`.
### Per-page template override not working
Make sure the frontmatter `template` value matches a registered plugin. The resolver matches against the plugin's `descriptor.name`, stripping `@docmd/` and `template-` prefixes. So all of these are equivalent:
- `template: "summer"`
- `template: "template-summer"`
- `template: "@docmd/template-summer"`
If none of those match, the resolver falls through to `config.theme.template` and then the default.
---
## [JavaScript Engine](https://docs.docmd.io/development/engines/js/)
---
title: "JavaScript Engine"
description: "Deep explore docmd's native JavaScript execution engine: use cases, portability, capabilities, and limits."
---
The **JavaScript Engine** is the foundational execution engine bundled into docmd. It runs easily on modern JavaScript runtimes. It delivers excellent performance without external dependencies or complex compilers.
By default, every docmd repository relies on the JavaScript engine. It provides highly reliable file traversal, metadata indexing, and build generation.
## Configuration
To explicitly instruct docmd to utilise the JavaScript backend, define the `engine` property as `"js"` inside `docmd.config.json`.
```json "docmd.config.json"
{
"title": "Developer Handbook",
"engine": "js",
"src": "docs",
"out": "site"
}
```
## Ideal Use Cases & Where It Shines
The JavaScript engine is exceptionally versatile. It shines under the following conditions:
- **Standard Repositories**: Sites containing up to several hundred pages build extremely fast. It leverages optimised JIT compilation and native JSON parsing.
- **Maximum Portability**: If your team uses diverse operating systems or restricted enterprise networks, the JavaScript engine guarantees flawless builds everywhere.
- **Rapid Prototyping**: Local development builds benefit from instantaneous hot-reloading (`npx @docmd/core dev`) with low initialisation latency.
- **Custom Scripting**: Configuration fallbacks and plugin integrations execute naturally within JavaScript. Standard string parsing avoids cross-boundary serialisation costs.
## Available Devices & Host Compatibility
Because it operates entirely within native runtime environments, the JavaScript engine supports an exhaustive array of target platforms:
- **Operating Systems**: macOS, Linux, Windows, FreeBSD, and OpenBSD.
- **Hardware Architectures**: x64, ARM64 (Apple Silicon, AWS Graviton), ARMv7, and RISC-V.
- **Container Environments**: Alpine Linux, standard Debian/Ubuntu, serverless build runners (Vercel, Netlify), and embedded CI pipelines.
## Capabilities & Limitations
| Dimension | JavaScript Engine Profile | Operational Impact |
| :--- | :--- | :--- |
| **Concurrency Model** | Node.js Event Loop + Native Worker Threads | Excellent asynchronous scheduling for network responses. Disk-heavy blocks operate smoothly. |
| **Git Metadata Handling** | Subprocess Orchestration (`child_process.execFile`) | Safely spawns native Git binaries to harvest commit histories. Includes persistent disk caching. |
| **Setup & Initialisation** | Zero-Configuration | Boots instantaneously. No package postinstall compilation required. |
| **Scalability Ceiling** | Highly Performant up to ~1,000 documents | On monolithic repositories exceeding a thousand complex files, sequential subprocess overhead may introduce minor latencies. |
## Feature Completeness
The JavaScript engine is **exclusive in its universal feature support**. Every core feature, advanced syntax, templating zone, and official plugin is engineered to run easily here.
Whether compiling mathematical formulas, rendering live search indices, or generating static site maps, the JavaScript engine guarantees deterministic builds.
---
## [Engines Overview](https://docs.docmd.io/development/engines/overview/)
---
title: "Engines Overview"
description: "Understand the pluggable build engine architecture and select the best processing backend."
---
The compiler features a highly modular, multi-threaded **Pluggable Engine Architecture**. It decouples orchestration from computational tasks to execute heavy workloads efficiently.
Choose between the zero-configuration **JavaScript Engine** and the accelerated **Rust Engine**. Select the engine based on your repository size, platform, and performance needs.
## Available Engines
| Engine | Identifier | Default | Target Use Case | Key Strength |
| :--- | :--- | :---: | :--- | :--- |
| **JavaScript Engine** | `"js"` | ✅ Yes | Standard websites, rapid local prototyping, portability. | Runs universally on any device supporting Node.js. |
| **Rust Engine (Preview)** | `"rust"` | ❌ No | Massive repositories (1,000+ files), enterprise CI/CD builds. | Maximises parallel file I/O via Tokio. |
## Configuration Options
Configure your build engine in the `docmd.config.json` file. Set the `engine` parameter directly.
```json "docmd.config.json"
{
"title": "Enterprise Reference",
"engine": "js",
"src": "docs",
"out": "site"
}
```
### Complete Options Reference
| Key | Supported Values | Default | Description |
| :--- | :--- | :--- | :--- |
| `engine` | `"js"`, `"rust"` | `"js"` | The execution layer processing file discovery and batch reads. |
## High-Level Capabilities & Limitations
Both engines share a rigorous execution boundary. The core API layer enforces uniform security and deterministic output.
### Shared Capabilities
- **Thread Isolation**: Engines execute asynchronous tasks securely inside isolated worker threads. This prevents blocking the primary server loop.
- **Task Verification**: Strict allowlists prevent unauthorised disk access or unverified execution patterns.
- **Seamless Interoperability**: Plugins request data via standardised interfaces (`runWorkerTask`). They remain unaware of the underlying backend.
### Architectural Limitations
- **Serialisation Overhead**: Data crosses native runtime boundaries (N-API). Highly iterative tasks passing large JSON objects incur a small serialisation penalty.
- **Binary Compatibility**: The JavaScript engine runs natively everywhere. The Rust engine relies on OS-specific platform binaries distributed via npm.
## How the Engine Loader Works
When `@docmd/core` boots, the internal loader inspects your active configuration:
1. **Resolution**: If configured for `"rust"`, the engine lazy-loads the architecture-specific native package (e.g., `@docmd/engine-rust-darwin-arm64`).
2. **Graceful Fallback**: If the binary is missing or unsupported, the engine logs an advisory notice. It then transparently falls back to the JavaScript engine. Your build always succeeds.
Explore the deep-dive documentation for each engine:
- [JavaScript Engine Reference](js.md)
- [Rust Engine Reference](rust.md)
---
## [Rust Engine](https://docs.docmd.io/development/engines/rust/)
---
title: "Rust Engine"
description: "Explore the optional native Rust engine: use cases, file I/O capabilities, supported packages, and limitations."
---
The **Rust Engine** is an optional, high-performance execution engine. It accelerates heavy I/O workloads in massive documentation projects. By using native binaries through N-API, it bypasses standard event-loop constraints to deliver multi-threaded file reading and subprocess orchestration.
Available as an **experimental preview**, the Rust engine targets enterprise scale. It shines where thousands of markdown files and exhaustive Git logs introduce compilation bottlenecks.
## Configuration
To activate native Rust acceleration, configure the `engine` directive to `"rust"` within your `docmd.config.json` file.
```json "docmd.config.json"
{
"title": "Global API Registry",
"engine": "rust",
"src": "docs",
"out": "site"
}
```
## Ideal Use Cases & Where It Shines
The Rust engine solves specific compilation bottlenecks. It provides excellent efficiency gains under the following scenarios:
- **Massive Repositories (1,000+ Files)**: Monolithic projects benefit immensely from asynchronous, parallel file system access orchestrated via Tokio.
- **Intensive Git Metadata Harvesting**: Extracting deep commit logs across hundreds of pages requires heavy subprocess spawning. The Rust engine processes `git:log` tasks up to **1.24× faster** than JavaScript.
- **Cold Build Acceleration in CI/CD**: In environments where warm disk caches are unavailable, raw file read throughput reduces total processing time. Real-world benchmarks demonstrate a **~25% speedup during cold builds** and a **~17% improvement on warm builds**.
## Supported Devices & Platform Packages
The engine executes pre-compiled machine code. It requires dedicated native binaries tailored to your target host architecture. The foundational `@docmd/engine-rust` package automatically lazy-loads the correct platform binary during startup.
The following platform packages are currently distributed:
| Platform Package | Target Architecture | Host Operating System |
| :--- | :--- | :--- |
| `@docmd/engine-rust-darwin-arm64` | ARM64 (Apple Silicon) | macOS |
| `@docmd/engine-rust-darwin-x64` | x64 (Intel) | macOS |
| `@docmd/engine-rust-linux-x64-gnu` | x64 | Linux (glibc environments) |
| `@docmd/engine-rust-linux-arm64-gnu` | ARM64 | Linux (glibc environments) |
| `@docmd/engine-rust-win32-x64-msvc` | x64 | Windows |
::: callout info "Transparent Graceful Fallback"
If your environment lacks an available pre-built binary, the engine logs a non-fatal notification and **automatically falls back** to the high-performance JavaScript engine. Your builds remain fully deterministic.
:::
## Capabilities & Strategic Limitations
To achieve maximum utility, you must understand its architectural trade-offs. The engine excels at I/O-bound operations but incurs overhead during cross-boundary serialisation.
| Capability / Task | Rust Engine Performance Profile | Architectural Verdict |
| :--- | :--- | :--- |
| **Batch File Discovery & Reads** | Accelerated via parallel Tokio workers. | ✅ Highly Effective for massive directories. |
| **Git Commit Log Harvest** | Fast subprocess orchestration bypassing Node event loops. | ✅ Excellent for cold-start Git metadata extraction. |
| **Persistent Disk Caching** | Native support for anchored disk caches to eliminate redundant reads. | ✅ Highly Effective for warm builds. |
| **CPU-Bound Search Indexing** | **Slower than native JavaScript JIT**. | ❌ Inefficient due to double serialisation overhead. |
### The Double-Serialisation Tax Explained
Communication between docmd's core orchestrator and the native Rust engine relies on stringified JSON passing across the N-API runtime boundary:
```text
JS Worker → JSON.stringify() → NAPI Boundary → Serde Deserialisation → [Rust Task] → Serde Serialisation → NAPI Boundary → JSON.parse()
```
For I/O-heavy operations like querying Git histories or reading disk buffers, the processing time saved vastly outweighs the string conversion cost.
However, for highly iterative, CPU-bound tasks like full-text search indexing (`search:index`), **the serialisation round-trip consumes more CPU resources than the underlying task itself**. Serialising large arrays of content back and forth causes the Rust implementation to run slower than Node's native JIT string manipulation.
As a result, **the JavaScript engine remains the recommended runtime for semantic search pipelines**. Enable the Rust engine selectively for large-scale Git and file management workloads.
---
## [Node API Reference](https://docs.docmd.io/development/node-api-reference/)
---
title: "Node API Reference"
description: "Low-level Node API for plugin authors — URL utilities, action dispatchers, source tools, the engine loader, and TypeScript types."
---
::: callout info
**For plugin authors.** If you just want to *call* docmd from a Node script, see [Build API](/reference/build-api) instead. This page covers the lower-level utilities exposed by `@docmd/api` for writing plugins.
:::
The `@docmd/api` package is the dedicated home for the plugin system. It provides hook registration, WebSocket RPC dispatch, source editing tools, and centralised URL utilities.
```bash
npm install @docmd/api
```
::: callout tip
**Note:** All exports from `@docmd/api` are also available from `@docmd/core`. New projects should import directly from `@docmd/api`.
:::
## URL Utilities
Plugins should use these centralised utilities instead of rolling their own URL logic.
### `outputPathToSlug(outputPath)`
Convert a build engine output path to a clean directory-style slug.
```javascript
import { outputPathToSlug } from '@docmd/api';
outputPathToSlug('guide/index.html'); // → 'guide/'
outputPathToSlug('index.html'); // → '/'
outputPathToSlug('de/v1/api/index.html'); // → 'de/v1/api/'
```
### `outputPathToPathname(outputPath)`
Convert to a root-relative pathname.
```javascript
import { outputPathToPathname } from '@docmd/api';
outputPathToPathname('guide/index.html'); // → '/guide/'
outputPathToPathname('index.html'); // → '/'
```
### `outputPathToCanonical(outputPath, siteUrl)`
Build a full canonical URL.
```javascript
import { outputPathToCanonical } from "@docmd/api";
outputPathToCanonical("guide/index.html", "https://docs.example.com");
```
### `sanitizeUrl(url)`
Collapse double slashes (except after protocol).
```javascript
import { sanitizeUrl } from "@docmd/api";
sanitizeUrl("https://docs.example.com//guide"); // → "https://docs.example.com/guide"
sanitizeUrl("/foo//bar"); // → "/foo/bar"
```
### `buildAbsoluteUrl(base, localePrefix, versionPrefix, pagePath)`
Build an absolute URL with locale and version prefixes.
```javascript
import { buildAbsoluteUrl } from '@docmd/api';
buildAbsoluteUrl('/', 'de/', 'v1/', 'guide/'); // → '/de/v1/guide/'
```
### `resolveHref(href)`
Normalise user-written hrefs to clean URLs. Handles `.md` stripping, trailing slashes, `external:` and `raw:` prefixes.
```javascript
import { resolveHref } from "@docmd/api";
resolveHref("overview.md"); // → "overview/"
resolveHref("external:https://github.com"); // → "https://github.com"
resolveHref("raw:docs/readme.md"); // → "docs/readme.md"
```
## Pre-computed Page URLs
Every page object includes pre-computed URL data. Plugins can read these directly with zero computation needed.
```javascript
export async function onPostBuild({ pages, config }) {
for (const page of pages) {
console.log(page.urls.slug);
console.log(page.urls.canonical);
console.log(page.urls.pathname);
}
}
```
| Property | Type | Description |
|:---------|:-----|:------------|
| `slug` | `string` | Clean directory-style slug (e.g., `guide/` or `/`) |
| `canonical` | `string` | Full canonical URL (only if `config.url` is set) |
| `pathname` | `string` | Root-relative path (e.g., `/guide/`) |
## Action & Event Dispatch
### `createActionDispatcher(hooks, options)`
Creates a dispatcher that routes WebSocket RPC messages to plugin action/event handlers.
```javascript
import { createActionDispatcher } from "@docmd/api";
const dispatcher = createActionDispatcher(
{ "actions": myPlugin.actions, "events": myPlugin.events },
{ "projectRoot": "/path/to/project", config, broadcast }
);
const { result, reload } = await dispatcher.handleCall("my-action", payload);
```
### `createSourceTools({ projectRoot })`
Creates source editing utilities for markdown file manipulation.
```javascript
import { createSourceTools } from "@docmd/api";
const source = createSourceTools({ "projectRoot": "/path/to/project" });
const block = await source.getBlockAt("docs/page.md", [10, 12]);
await source.wrapText("docs/page.md", [10, 12], "important", 0, "**", "**");
```
### `loadPlugins(config, options)`
Loads, validates, and registers all plugins declared in the config. Returns the populated hooks registry.
```javascript
import { loadPlugins, hooks } from "@docmd/api";
const registeredHooks = await loadPlugins(config, {
"resolvePaths": [__dirname]
});
```
## Engine Loader API
The pluggable engine architecture allows programmatic resolution and instantiation of acceleration layers directly via `@docmd/api`.
### `loadEngine(engineName)`
Resolves and initialises the requested build engine backend. If native architecture binaries are unavailable, it gracefully falls back to the high-performance JavaScript engine.
```javascript
import { loadEngine } from "@docmd/api";
const engine = await loadEngine("rust");
const gitLogResult = await engine.runWorkerTask("git:log", { "paths": ["docs/guide.md"] });
```
### `registerEngine(engineName, engineInstance)`
Allows custom tools or third-party integrators to register custom execution engines programmatically.
```javascript
import { registerEngine } from "@docmd/api";
registerEngine("custom", myCustomEngineImpl);
```
## Type Exports
For TypeScript plugin authors, the following types are available:
```typescript
import type {
PluginModule,
PluginDescriptor,
PluginHooks,
PageContext,
BeforeBuildContext,
PostBuildContext,
Capability,
ActionContext,
ActionHandler,
EventHandler,
SourceTools,
BlockInfo,
TextLocation,
Engine,
} from '@docmd/api';
```
## What's Next
- [Building Plugins](/development/building-plugins) — start here.
- [Plugin Examples](/development/plugin-examples) — see a full plugin walkthrough.
- [Engines & Architecture](/development/engines/overview) — Rust engine, N-API, and engine loader internals.
---
## [Extending docmd with Custom Plugins](https://docs.docmd.io/development/plugin-examples/)
---
title: "Extending docmd with Custom Plugins"
description: "How to use docmd's lifecycle hooks to build custom functionality and extend the documentation engine."
---
## Problem
Sometimes you have specific requirements not covered by built-in features. For example, you might need to fetch data from an internal API during the build process or perform complex transformations on the generated HTML.
## Why it matters
Extensibility separates a static tool from a professional documentation framework. Without a clean way to inject custom logic, teams maintain fragile shell scripts or post-processing wrappers. This makes the build process difficult to manage and debug.
## Approach
docmd features a reliable, hook-based [Plugin API](../../plugins/building-plugins.md). Write simple Node.js modules that intercept the documentation lifecycle at various stages. This allows you to arbitrarily modify content and behaviour from initial configuration to final HTML generation.
## Implementation
### 1. Create a Local Plugin
A plugin is a standard JavaScript module that exports a descriptor and lifecycle hooks.
```javascript
// plugins/version-injector.js
let latestVersion = "0.0.0";
export default {
// Plugin Descriptor
plugin: {
"name": "version-injector",
"version": "1.0.0",
"capabilities": ["init", "build"]
},
// Lifecycle Hooks
async onConfigResolved(config) {
// Fetch external data once during initialisation
const response = await fetch("https://api.example.com/version");
latestVersion = await response.text();
console.log(`[Plugin] Fetched version: ${latestVersion}`);
},
// Modify HTML before writing
async onBeforeRender(page) {
if (!page.html) return;
page.html = page.html.replace(/\{\{VERSION\}\}/g, latestVersion);
page.frontmatter.computedVersion = latestVersion;
}
};
```
### 2. Register the Plugin
Register your local plugin by importing it into your `docmd.config.js` (or `docmd.config.ts`). JSON config files cannot use imports - use the `.js` or `.ts` format for plugin registration.
```javascript
import VersionInjector from "./plugins/version-injector.js";
export default {
"title": "My Project Docs",
"plugins": {
// Inject the local plugin object
"version-injector": VersionInjector
}
};
```
## Trade-offs
Custom plugins run in the Node.js environment during build time. While powerful, they can impact build performance if unoptimised. Any logic in hooks like `onAfterParse` or `onPageReady` runs for *every* page in your site. Ensure your transformations are efficient (e.g., using optimised Regex) to keep build times fast.
---
## [Setup](https://docs.docmd.io/development/setup/)
---
title: "Setup"
description: "Run this docs site locally, link to your global docmd install, and run the full verification pipeline."
---
# Setup
::: callout info
**For docs-site contributors.** Want to *contribute* to docmd itself (the framework)? See the [GitHub Contributing guide](https://github.com/docmd-io/docmd?tab=contributing-ov-file) instead — that's where the framework development workflow lives.
:::
This page covers working on **this documentation site** (`docmd-io/docs`), not on the docmd framework (`docmd-io/docmd`).
## Prerequisites
- **Node.js**: v22.x or later (LTS recommended)
- **pnpm**: v10.x or later
## Local Development
```bash
git clone https://github.com/docmd-io/docs.git
cd docs
pnpm install
npx @docmd/core dev
```
The site is served at `http://localhost:3000` with live reload.
### Watching the framework locally
If you're editing framework code in `docmd-io/docmd` and want to see changes reflected in this docs site:
```bash
# In the framework repo
pnpm build
# In this docs repo, link the local build
npx @docmd/core link ../docmd/packages/core
```
Then restart `npx @docmd/core dev`. Your changes to the framework will be picked up after a framework rebuild.
## Quality Gates
Before opening a Pull Request:
```bash
# Lint Markdown and check for broken links
pnpm lint
# Full verification pipeline (lint + build + dead-link check)
pnpm verify
```
The verification pipeline mirrors what the maintainers run on every PR. A green run is required for merge.
## Translations
Translation workflow for adding/updating `de/` and `zh/` content:
1. Edit the EN source in `docmd-main/v08/en/...`.
2. Mirror the change in `de/` and `zh/` (same path, translated prose, preserved frontmatter keys, preserved container markers, code blocks unchanged).
3. Preserve all file titles on codeblocks (e.g. ` ```json "docmd.config.json"`).
4. Run `pnpm verify` to confirm links and structure still hold.
See the project memory for the translation house style and codeblock file-title rule.
## Project Layout
```
docs/
├── docmd-main/v08/
│ ├── en/ # Canonical English source
│ ├── de/ # German translations (mirrors en/)
│ ├── zh/ # Chinese translations (mirrors en/)
│ └── navigation.json # Single nav, replicated per locale
├── docmd-search/ # Search index assets
├── docs/ # Other doc projects (docmd-search, docmd-main, etc.)
└── package.json
```
## What's Next
- [Building Plugins](/development/building-plugins) — write a custom docmd plugin.
- [Plugin Examples](/development/plugin-examples) — see a complete plugin walkthrough.
- [Building Templates](/development/building-templates) — author a docmd template.
- [Node API Reference](/development/node-api-reference) — programmatic build API.
---
## [Installation](https://docs.docmd.io/getting-started/installation/)
---
title: "Installation"
description: "Install @docmd/core globally, locally within a project, or run containerised via the official Docker image. Requires Node.js 18+."
---
Choose the installation method that fits your workflow. Node.js 18 or higher is required for local builds.
## 1. Local Installation (Recommended)
Running `docmd` locally keeps your documentation configuration versioned with your source code.
::: tabs
== tab "npm" icon:box
```bash
# Install as a development dependency
npm install -D @docmd/core
# Initialise a new project
npx docmd init
```
== tab "pnpm" icon:boxes
```bash
# Install as a development dependency
pnpm add -D @docmd/core
# Initialise a new project
pnpm dlx docmd init
```
== tab "yarn" icon:scroll
```bash
# Install as a development dependency
yarn add -D @docmd/core
# Initialise a new project
yarn dlx docmd init
```
== tab "Bun" icon:zap
```bash
# Install as a development dependency
bun add -D @docmd/core
# Initialise a new project
bunx docmd init
```
== tab "Docker" icon:container
```bash
# Pull the official multi-architecture image
docker pull ghcr.io/docmd-io/docmd:latest
# Build documentation from local docs/ to site/
docker run -v $(pwd)/docs:/docs -v $(pwd)/site:/site ghcr.io/docmd-io/docmd:latest build
```
See the [Docker Deployment Guide](../deployment/docker.md) for Docker Compose and Kubernetes configurations.
:::
::: callout tip "Shorthand Scripts" icon:sparkles
Once installed locally, you can use `npx docmd dev` to start the live preview server, or add scripts directly to your `package.json`.
:::
## 2. Global Installation
Install the package globally to create or preview sites anywhere on your system without creating a local project.
::: tabs
== tab "npm" icon:box
```bash
npm install -g @docmd/core
```
== tab "pnpm" icon:boxes
```bash
pnpm add -g @docmd/core
```
== tab "yarn" icon:scroll
```bash
yarn global add @docmd/core
```
== tab "Bun" icon:zap
```bash
bun add -g @docmd/core
```
:::
Once installed, the `docmd` binary is available everywhere:
```bash
docmd dev # Start a dev server locally
docmd build # Build static output
```
## 3. Browser-Only Integration
Embed the engine directly inside an existing web application via CDN.
::: callout info "Specialised Library Integration" icon:help-circle
This bypasses the CLI and loads the parsing engine in the reader's browser. Use this for dynamic portals, not static SEO websites.
:::
Add the stylesheet and JavaScript engine to your HTML.
```html
```
See the [Browser API Guide](../api/browser-api.md) for full integration details.
## 4. Troubleshooting
### Permission Denied (`EACCES` Errors)
Do not use `sudo` for global installs on macOS or Linux. Fix permission conflicts using a Node.js version manager like [nvm](external:https://github.com/nvm-sh/nvm) or [fnm](external:https://github.com/Schniz/fnm).
### PowerShell Execution Policies (Windows)
If Windows blocks execution, open PowerShell as administrator and enable current-user script execution.
```powershell
Set-ExecutionPolicy RemoteSigned -Scope CurrentUser
```
---
## [Project Structure](https://docs.docmd.io/getting-started/project-structure/)
---
title: "Project Structure"
description: "Learn how `@docmd/core` maps physical folders and Markdown files to dynamic URLs and clean navigation."
---
The compiler uses your local filesystem as the source of truth. Folders become navigation sections. Markdown files become content pages. Your directory hierarchy translates directly into web URLs.
## 1. Standard Project Scaffold
Run `npx @docmd/core init` to establish a minimal workspace layout. This structure keeps source content separated from assets and production builds.
```text
my-docs/
├── docs/ ← Source directory containing your Markdown (.md) pages
│ └── index.md ← The landing page (resolves to /)
├── assets/ ← Static web assets loaded directly by the engine
│ ├── css/ ← Custom stylesheets for customising page layout
│ ├── js/ ← Custom scripts to extend browser-side logic
│ └── images/ ← Brand logos, icons, and inline illustrations
├── docmd.config.json ← Central configuration schema
├── package.json ← Node dependency manifest and scripts
└── site/ ← Optimised production build output folder
```
::: callout info "Configuration File Resolution" icon:settings
`docmd.config.json` (or `docmd.config.ts`) is the recommended, primary configuration format. The legacy `docmd.config.js` format remains supported but acts strictly as a fallback when `.json` or `.ts` configuration files are missing.
:::
## 2. Directory and URL Mapping
The compiler maps files within your source folder directly to public URLs. There are no trailing `.html` extensions or complex routing rules.
| Source File | Resolved URL Path | Purpose |
| :--- | :--- | :--- |
| `docs/index.md` | `/` | Home Landing Page |
| `docs/api.md` | `/api` | Main API Reference |
| `docs/guides/setup.md` | `/guides/setup` | Sub-section Technical Guide |
| `docs/getting-started/quick-start.md` | `/getting-started/quick-start` | Multi-level deep page |
::: callout tip "Automatic Header Parsing" icon:info
If a file lacks a `title` in its YAML frontmatter, the engine extracts the first `H1` tag (`# Heading`). This title represents the page in breadcrumbs and search.
:::
## 3. Workspace Monorepo Structure
For complex layouts or large projects with multiple distinct products (such as a core platform, an SDK, and a CLI tool), `docmd` natively supports a **Workspace Monorepo** directory structure. This allows you to manage multiple independent documentation sites from a single root repository while maintaining unified branding.
```text
my-docs-monorepo/
├── docmd.config.json ← Root configuration (defines global settings)
├── assets/ ← Shared global assets (inherited by all projects)
│ ├── css/ ← Shared global stylesheets
│ └── images/ ← Shared logos and icons
├── package.json ← Root dependency manifest
├── main-site/ ← Root project directory
│ ├── docmd.config.json ← Project-specific config overrides
│ └── docs/ ← Content for main-site (resolves to /)
│ └── index.md
└── sdk-reference/ ← Secondary project directory
├── docmd.config.json ← Project-specific config overrides
└── docs/ ← Content for sdk-reference (resolves to /sdk)
└── index.md
```
### Key Workspace Directory Patterns
* **Global Configuration Cascading:** Any configuration defined in the root `docmd.config.json` (such as `theme` or `menubar`) acts as a fallback default. Individual projects can selectively override these defaults in their own local config files.
* **Asset Sharing and Priority:** Shared logos, global custom styles, and common scripts are placed in the root `assets/` directory. Projects can also define their own local `assets/` directories; in the event of filename conflicts, project-specific assets always take precedence.
* **Output Consolidation:** During the build process (`npx @docmd/core build`), the engine automatically merges all projects into a single consolidated production output directory (e.g. `./site/` and `./site/sdk/`), negating the need for complex reverse proxy setups or isolated build pipeline configuration.
For complete setup steps and advanced cascading rules, refer to the [Workspaces Configuration Guide](../configuration/workspaces.md).
---
## [Quick Start](https://docs.docmd.io/getting-started/quick-start/)
---
title: "Quick Start"
description: "Go from an empty folder to a running documentation site in under a minute."
---
Run docmd inside any folder with Markdown files. No config file, setup, or framework knowledge required.
## 1. Start a dev server
::: tabs
== tab "npm" icon:box
```bash
npx @docmd/core dev
```
== tab "Bun" icon:zap
```bash
bunx @docmd/core dev
```
:::
This opens `http://localhost:3000`. Your documentation is live.
::: callout tip "Automatic Port Failover" icon:info
If port `3000` is in use, docmd automatically finds the next available port (e.g., `3001`).
:::
## 2. Automatic features
The engine sets up everything automatically:
1. **Directory Detection**: Scans for `docs/`, `src/docs/`, `documentation/`, `content/`, or any `.md` files in the project root.
2. **Navigation Structuring**: Builds a nested sidebar from your folder tree.
3. **Title Resolution**: Extracts page titles from the first `H1` tag automatically.
4. **Search Indexing**: Enables built-in full-text search immediately.
5. **Smart Caching**: Triggers sub-200ms rebuilds instantly on file save.
No `docmd.config.json` is required. Add one later to customise layouts, plugins, or versions.
## 3. Build for production
Compile your Markdown files into a static, production-ready site.
::: tabs
== tab "npm" icon:box
```bash
npx @docmd/core build
```
== tab "Bun" icon:zap
```bash
bunx @docmd/core build
```
:::
The compiler outputs a static site to `./site/`.
Host this static output anywhere. Deploy to GitHub Pages, Vercel, Netlify, or any static host.
---
## [Context Preservation for AI-Friendly Documentation](https://docs.docmd.io/guides/ai-optimisation/context-preservation/)
---
title: "Context Preservation for AI-Friendly Documentation"
description: "How to ensure that AI models can understand and utilise the relationships between different parts of your documentation."
---
## Problem
Human readers can click hyperlinks to learn more. AI models often process documentation in isolated "chunks". When an AI encounters a hyperlink, it cannot "click" it to fetch context. If critical information is hidden behind a link, the AI may fail to provide accurate answers. This leads to hallucinations.
## Why it matters
AI models rely on immediate surrounding text to determine meaning. If your documentation is highly fragmented with poor context preservation, AI-driven search tools (like RAG systems) will struggle to provide high-quality responses.
## Approach
Use **Inline Context Unrolling** to provide the minimum viable context alongside every major link. Use docmd's [LLMs Plugin](../../plugins/llms.md) to provide a unified, machine-readable view of your entire documentation set.
## Implementation
### 1. Descriptive Linking and Summaries
Avoid generic link text. Provide a brief, one-sentence summary of the linked concept alongside the link.
- **❌ Poor (Context Lost)**: To configure the timeout, refer to the [General Configuration](../../configuration/overview.md).
- **✅ Better (Context Preserved)**: You can configure the `timeoutMs` parameter within the [General Configuration](../../configuration/overview.md), which defines how long the engine waits before failing a network request.
### 2. Using Collapsible Sections for Detail
[Collapsible Containers](../../content/containers/collapsible.md) are excellent for AI optimisation. The content remains part of the raw Markdown source for the AI, but it is visually tucked away for human readers.
```markdown
### Database Connection
Connect using the primary URI.
::: collapsible "What is the URI format?"
The URI follows the standard PostgreSQL format: `postgresql://user:password@host:port/database`.
:::
```
### 3. Enabling the LLMs Plugin
Enable the [LLMs Plugin](../../plugins/llms.md) in your `docmd.config.json`. This plugin generates a `llms-full.txt` file after every build. It concatenates your entire documentation set into a single, high-context file that LLMs consume easily.
## Trade-offs
Inline context unrolling makes documentation slightly more verbose and introduces minor redundancy. However, this is a small price to ensure your documentation is "AI-ready" and capable of powering high-quality automated support.
---
## [Creating Deterministic and Chunkable Documentation](https://docs.docmd.io/guides/ai-optimisation/deterministic-chunkable-docs/)
---
title: "Creating Deterministic and Chunkable Documentation"
description: "How to structure your documentation to optimise it for Retrieval-Augmented Generation (RAG) and AI ingestion."
---
## Problem
When AI pipelines ingest documentation, they slice the Markdown into smaller "chunks". If a document consists of long paragraphs with unclear boundaries, the algorithm splits context mid-thought. This destroys the chunk's utility and leads to incorrect AI responses.
## Why it matters
If an AI retrieves a code block but misses the preceding paragraph explaining *when* to use it, the answer lacks conditionality. Structuring your documentation for chunkability ensures each segment contains enough context to be useful on its own.
## Approach
Structure your pages as a hierarchy of deterministic, atomic blocks. Use Markdown headers to clearly delineate concepts. Ensure related information (like a warning and the code it applies to) is kept physically close together in the source file.
## Implementation
### 1. Atomic Header Sections
Ensure every `##` or `###` header encapsulates a single, atomic concept. A well-structured section should stand alone as a useful chunk for an AI model.
- **✅ Good**: A header "Authentication via OAuth" followed by a brief explanation and a code example.
- **❌ Poor**: A massive "Getting Started" page with 15 different concepts and no sub-headers.
### 2. Tight Proximity for Critical Information
Do not separate a critical warning from the code it applies to with long paragraphs. Use [Callouts](../../content/containers/callouts.md) to bind them together vertically. This increases the probability that they remain in the same vector chunk during ingestion.
```markdown
::: callout warning "Destructive Action"
Running this command will permanently delete all logs.
:::
`npx @docmd/core logs --clear`
```
### 3. Automated Concatenation
The [LLMs Plugin](../../plugins/llms.md) facilitates chunking by generating a `llms-full.txt` file. This uses standard separators (`---`) between pages. It helps ingestion pipelines recognise natural document boundaries while preserving global context.
## Trade-offs
This approach favours a modular, segmented writing style over long, flowing narratives. While it may feel repetitive to a human reader, it significantly improves the performance of AI-powered search and automated support agents.
---
## [Generating AI-Ready Documentation with docmd](https://docs.docmd.io/guides/ai-optimisation/generating-ai-ready-docs/)
---
title: "Generating AI-Ready Documentation with docmd"
description: "How to use the llms.txt standard and docmd's built-in tools to provide optimised context for AI assistants."
---
## Problem
Developers increasingly rely on AI coding assistants to read and interpret documentation. If your documentation is only accessible via a web browser - cluttered with navigation elements, trackers, and complex HTML - AI agents consume excessive tokens on irrelevant data. This quickly exhausts their context windows.
## Why it matters
Providing a clean, token-optimised text version of your documentation is the modern equivalent of providing a high-quality REST API. It ensures AI agents can quickly ingest your entire documentation set. This results in more accurate code suggestions and better support.
## Approach
Use docmd's built-in **LLMs Plugin**. This plugin natively implements the emerging `llms.txt` standard. It automatically generates token-optimised summaries and full-context files during every build process.
## Implementation
Configure the `llms` plugin in your [Plugin Configuration](../../plugins/llms.md).
### 1. Configure the Site URL
Ensure the `url` property is correctly set in your `docmd.config.json`. This allows the plugin to generate absolute URLs for all pages in the `llms.txt` file.
```json "docmd.config.json"
{
"title": "My Project Docs",
"url": "https://docs.example.com",
"plugins": {
"llms": {}
}
}
```
### 2. Output Files
During the build process, docmd generates two key files in your site root:
- **`llms.txt`**: A concise, structured Markdown summary of all your pages, including their titles, descriptions, and full URLs.
- **`llms-full.txt`**: A comprehensive file containing the raw Markdown content of your entire site, concatenated with standard separators (`---`). This provides the ultimate "source of truth" for AI models.
### 3. Controlling Ingestion
You can exclude specific pages from the AI-ready output using the `llms` property in the [Page Frontmatter](../../content/frontmatter.md).
```yaml
---
title: "Internal Debugging Guide"
llms: false
---
```
## Trade-offs
Generating `llms-full.txt` creates a large single file. For exceptionally large documentation sites, this file could exceed several megabytes. While ideal for modern LLMs with large context windows (like Gemini 1.5 Pro or Claude 3.5 Sonnet), it may be too large for smaller models. Ensure you organise your [Navigation](../../configuration/navigation.md) logically so the AI can prioritise important sections.
---
## [MCP & Agent Skills](https://docs.docmd.io/guides/ai-optimisation/mcp-and-agent-skills/)
---
title: "MCP & Agent Skills"
description: "Optimise your documentation workspace for AI development agents using the Model Context Protocol and custom Skills."
---
Integrating AI development agents into your workflow requires structured interfaces that allow models to query, read, and write documentation context efficiently. `docmd` satisfies this need via a native **Model Context Protocol (MCP)** server and an extensible **Agent Skills** database.
## Model Context Protocol (MCP) Setup
The Model Context Protocol connects LLM environments directly to your local workspace tools.
### 1. Claude Desktop Integration
Add the following to your desktop configuration file (typically at `~/Library/Application Support/Claude/claude_desktop_config.json` on macOS or `%APPDATA%\Claude\claude_desktop_config.json` on Windows):
```json "claude_desktop_config.json"
{
"mcpServers": {
"docmd": {
"command": "npx",
"args": ["@docmd/core", "mcp"],
"cwd": "/path/to/your/docs/project"
}
}
}
```
### 2. IDE Integration (Cursor / Windsurf)
In your editor's MCP settings panel, add a new server using the `stdio` transport:
- **Command**: `npx @docmd/core mcp`
- **Transport**: `stdio`
## Available MCP Tools
Once connected, the following tools become available to the agent:
1. `search_docs(query)`: Performs a workspace-wide full-text search.
2. `list_docs(subdir?)`: Lists every markdown file in the project, optionally scoped to a subdirectory (a locale, a version, a guide section). Use this to navigate the docs tree before reading individual files.
3. `read_doc(route)`: Retrieves the raw Markdown contents of a specific route. The route must resolve inside the project root.
4. `get_config()`: Returns the resolved `docmd.config` — title, source and output directories, locales, versions, and enabled plugins. Sensitive values (API keys, analytics IDs) are stripped from the response.
5. `validate_docs()`: Lints the entire documentation and returns validation errors (e.g., broken links).
6. `get_llms_context()`: Fetches the consolidated `llms-full.txt` context file.
## Leveraging Agent Skills (`SKILL.md`)
When you run `docmd init` in your project, the engine automatically generates a `SKILL.md` file in your root workspace. This file serves as a prompt instruction card for any AI agent working on your repository.
### Best Practices for AI Agents
1. **Read SKILL.md First**: Instruct your agents to read the `SKILL.md` file at the start of a coding session. This teaches the model about custom Callouts, OpenAPI markup, and file structures.
2. **Validate After Edits**: Whenever an agent modifies Markdown files, it should call the `validate_docs` tool (or run `npx @docmd/core validate`) to verify that no relative links or anchor paths are broken.
3. **Synchronize Locales**: If the project uses versioning or multiple languages, agents should use the comparison matrix to ensure all translations stay in sync.
---
## [Minimising AI Hallucinations via Documentation](https://docs.docmd.io/guides/ai-optimisation/minimising-ai-hallucinations/)
---
title: "Minimising AI Hallucinations via Documentation"
description: "How to write explicit, self-contained documentation that prevents AI models from inventing incorrect information."
---
## Problem
AI models are predictive engines, not reasoning engines. If an API usage example is incomplete, uses ambiguous placeholders, or relies on implicit knowledge, the AI will "hallucinate". It invents missing pieces based on general training patterns. These inventions are frequently incorrect, leading to developer frustration.
## Why it matters
Hallucinated code destroys user trust. When a developer asks an AI for help and receives broken code, they blame the software for being "buggy" or "poorly documented". Minimising hallucinations is critical for maintaining your project's professional reputation.
## Approach
Practice **Defensive Documentation**. Write extremely explicit, fully instantiated code blocks that leave no room for ambiguity. Never assume the reader (or the AI) knows the necessary imports, environment variables, or prerequisite configurations.
## Implementation
### 1. Fully-Qualified Code Blocks
Always include the necessary imports or setup code in every snippet. This ensures that when an AI chunks your documentation, the code block remains a self-contained unit of truth.
- **❌ Hallucination Risk**:
```javascript
const config = loadConfig();
```
- **✅ Hallucination Proof**:
```javascript
import { loadConfig } from "@docmd/core";
const config = loadConfig();
```
### 2. Concrete Examples Over Placeholders
Avoid using vague placeholders like `your-api-key` or `env-name`. Provide concrete, valid examples or use comments to specify strict enum requirements.
```javascript
// Valid environments: "development", "staging", "production"
const app = init({ env: "production" });
```
### 3. Inline Code Comments
Place critical requirements *inside* the code block as comments, rather than only in surrounding paragraphs. AI models weigh comments within code highly when generating similar snippets.
```javascript
// REQUIRED: Must be an absolute path
outputPath: "/var/www/html/docs"
```
### 4. Categorised Warnings
Use [Callouts](../../content/containers/callouts.md) to clearly mark deprecated features or breaking changes. AI models are more likely to respect a `::: callout warning` block than a simple sentence in a paragraph.
## Trade-offs
Defensive documentation makes code blocks longer and more repetitive. Human readers may find seeing the same `import` statements tedious. However, the benefit of having "AI-proof" documentation that reduces support tickets far outweighs the minor cost of verbosity.
---
## [OKF Bundles — Deep Dive](https://docs.docmd.io/guides/ai-optimisation/okf-bundles/)
---
title: "OKF Bundles — Deep Dive"
description: "How to organise your docmd content for the best OKF bundle — typed concepts, cross-links, and the discipline that makes an AI-agent-friendly knowledge base."
---
The [`@docmd/plugin-okf`](../../plugins/okf.md) generates an [Open Knowledge Format][okf-spec] bundle from your docmd site. This guide explains what the bundle looks like, how to organise your content for the best AI-agent consumption, and how OKF differs from the [`llms.txt`](../../plugins/llms.md) flat-list format.
[okf-spec]: https://cloud.google.com/blog/products/data-analytics/how-the-open-knowledge-format-can-improve-data-sharing
## The mental model: a wiki, not a sitemap
A traditional docs site is a tree — sections and subsections, with pages hanging off each one. A user navigates the tree top-down to find what they need.
An OKF bundle is a **wiki** — a flat directory of typed concept files with cross-links between them. An AI agent navigates the graph horizontally, following links from one concept to its neighbors.
The two structures look the same on disk (markdown files in directories), but the navigation model is different. The OKF spec's [three design principles][okf-principles] are worth quoting in full:
> 1. **Minimally opinionated.** OKF requires exactly one thing of every concept: a `type` field. Everything else (what types exist, what other fields to include, what sections the body has) is left to the producer.
> 2. **Producer/consumer independence.** A bundle hand-authored by a human can be consumed by an AI agent. A bundle generated by a metadata export pipeline can be browsed in a visualizer. A bundle synthesized by one LLM can be queried by another. The format is the contract; the tooling at each end is independently swappable.
> 3. **Format, not platform.** OKF is not tied to any specific cloud, database, model provider, or agent framework.
[okf-principles]: https://cloud.google.com/blog/products/data-analytics/how-the-open-knowledge-format-can-improve-data-sharing
## What an OKF bundle looks like
```text
site/okf/
├── okf.yaml ← typed manifest
├── index.md ← Karpathy-style catalog
├── graph/ ← opt-in: only when plugins.okf.graph: true
│ ├── index.html ← interactive force-directed viewer
│ ├── graph.json ← graph data
│ ├── graph.js ← viewer runtime
│ └── graph.css ← viewer styles
├── concepts/
│ ├── weekly-active-users.md
│ ├── orders-table.md
│ └── api-authentication.md
└── _meta/
├── bundle.json
└── lint-report.txt
```
Each `concepts/.md` file carries a `type` field in frontmatter plus the full markdown body of the page. The `okf.yaml` manifest lists every concept with its type, path, locale, version, and tags — the catalog that an AI agent uses to decide which concepts to read.
## What goes in a `type`
The `type` field is the only required frontmatter key. It tells the agent what kind of knowledge this concept represents. The `@docmd/plugin-okf` plugin has a path-prefix type-inference map:
| URL prefix | Inferred type |
| :--- | :--- |
| `/api/` | `api` |
| `/guides/` | `guide` |
| `/reference/` | `reference` |
| `/concepts/` | `concept` |
| `/runbooks/` | `runbook` |
| `/datasets/` | `dataset` |
| `/metrics/` | `metric` |
| `/tables/` | `table` |
| (anything else) | `concept` (default) |
You can override the inferred type with explicit frontmatter:
```markdown
---
type: api
title: "Authentication API"
description: "OAuth 2.0 + JWT auth flow for the user API."
---
# Authentication API
...
```
Or use the nested `okf.type` form:
```markdown
---
okf:
type: api
title: "Authentication API"
---
```
The agent reads the `type` field first. A concept with `type: runbook` is treated as a step-by-step playbook (e.g. "how to recover from a partial outage"); a concept with `type: api` is treated as API reference; a concept with `type: dataset` is treated as a data dictionary.
## Cross-links make the graph
OKF is a graph, not a tree. The relationships between concepts are inferred from internal markdown links. If `api-authentication.md` links to `users-table.md`, the OKF bundle records that edge in `graph.json` and the graph viewer draws a line between the two nodes.
The `okf-bundle` (read: "graph of concepts") is more useful than a tree because it lets the agent find related concepts the author didn't think to put in a sub-section. The LLM-wiki pattern that OKF formalises explicitly assumes the agent will follow links to discover adjacent knowledge.
Best practices for cross-links:
- **Link forward** — when introducing a concept, link to the concepts it depends on. "To use this, see [users table](../tables/users.md)".
- **Link backward** — in the concept that depends on this one, link back. "Used by [API auth](../api/auth.md)".
- **Don't over-link** — every link should add information. Linking every word dilutes the graph and confuses the agent.
## Per-page opt-out
Some pages aren't useful to AI agents — legal boilerplate, internal "about the team" pages, marketing copy. Use `frontmatter.okf: false` to exclude a single page from the OKF bundle:
```markdown
---
okf: false
---
# Internal Roadmap (Q3 2026)
...
```
Or use `noindex: true` to exclude a page from every downstream consumer (sitemap, search, llms.txt, OKF). The two flags differ:
- `okf: false` — excluded from OKF only; still in search and llms.txt
- `noindex: true` — excluded from every downstream consumer
## How OKF differs from `llms.txt`
The [`llms.txt` plugin](../../plugins/llms.md) produces a flat list of pages:
```text
- [Page 1](https://example.com/page-1)
- [Page 2](https://example.com/page-2)
- [Page 3](https://example.com/page-3)
```
The OKF plugin produces a typed graph:
```yaml
concepts:
- id: api-authentication
type: api
title: "Authentication API"
path: /api/auth/
file: concepts/api-authentication.md
tags: [auth, security]
- id: users-table
type: table
title: "Users table"
path: /tables/users/
file: concepts/users-table.md
tags: [schema, data]
```
The two complement each other:
- **llms.txt** is for **flat consumption** — "give me everything". An agent reads the file and has the full text in its context window.
- **OKF** is for **typed consumption** — "give me the schema for table X". An agent reads the manifest, picks the concepts it needs, and loads them selectively.
For projects with under 50 pages, llms.txt alone is often enough. For projects with 50+ pages, OKF is the more efficient format — the agent doesn't have to load every page just to find the one it needs.
## Common mistakes
### 1. Skipping the `type` field
The OKF manifest is most useful when every concept has a distinct `type`. If 80% of your pages are inferred as `concept`, the agent can't tell which are reference docs, which are tutorials, and which are runbooks. Set `type: ` explicitly for every page that has a clear category.
### 2. Pages with no cross-links
If a page is a dead end (no internal links to or from it), the graph viewer shows it as an isolated node. The agent will read it in isolation, missing the context. Add at least one inbound link (referenced from another page) for every page you want surfaced.
### 3. Putting internal jargon in `description`
The `description` field is shown in the manifest and in `llms.txt` summaries. An AI agent uses it to decide whether a concept is relevant. Use plain English the agent can match against a user query: "weekly active users for the marketing site, computed from the events stream", not "WAU (ms)".
### 4. OKF for non-AI-agent sites
If your docs site has no AI-agent audience, OKF adds nothing. The `@docmd/plugin-okf` is enabled by default, so disable it explicitly:
```json
{
"plugins": { "okf": false }
}
```
The `llms.txt` plugin is the right tool for "AI-searchable flat text"; OKF is the right tool for "AI-agent typed knowledge graph".
## How to verify
After `docmd build`, inspect the bundle at `site/okf/`:
```bash
# The manifest (every concept, type, path)
cat site/okf/okf.yaml | head -30
# The catalog (Karpathy-style grouped by type)
open site/okf/index.md
# The interactive graph (force-directed, theme-aware)
open site/okf/graph.html
# Warnings the plugin produced
cat site/okf/_meta/lint-report.txt
```
The lint report is the first thing to check — it lists pages without a `type` field, pages with broken internal links, and orphaned concepts (no inbound links). Fix any of those for a cleaner agent experience.
## See also
- [OKF Bundle Plugin docs](../../plugins/okf.md) — the plugin reference: every config option, the per-page opt-out flags, the type resolution precedence.
- [Open Knowledge Format — Google Cloud blog post](https://cloud.google.com/blog/products/data-analytics/how-the-open-knowledge-format-can-improve-data-sharing) — the spec announcement with the design rationale.
- [LLM Context Plugin](../../plugins/llms.md) — the complementary flat-list format. LLMS is "give me everything", OKF is "give me the schema for table X".
- [Building AI-ready docs](../generating-ai-ready-docs.md) — broader guide on AI-agent consumption.
- [Structure for LLMs](../structure-for-llms.md) — how to organise content for machine consumption.
---
## [Designing for Semantic Search and RAG](https://docs.docmd.io/guides/ai-optimisation/semantic-search-design/)
---
title: "Designing for Semantic Search and RAG"
description: "How to structure your documentation to optimise it for vector-based search and Retrieval-Augmented Generation."
---
## Problem
Traditional keyword search relies on exact text matches. If a user searches for "authentication", a basic keyword engine fails to find "Integrating OAuth2" if the exact word is absent. Semantic search uses vector embeddings to understand query meaning. It solves this problem but requires specific documentation structures.
## Why it matters
Modern developers expect intuitive, intent-based search. If documentation fails to surface relevant content because of terminology differences, users abandon your site. Designing for semantic search ensures documentation remains discoverable regardless of the vocabulary used.
## Approach
Structure your documentation to be easily consumed by Retrieval-Augmented Generation (RAG) pipelines. Create "semantically dense" content where concepts are clearly defined. Replace pronouns with explicit entities to preserve context during chunking and vectorisation.
## Implementation
### 1. Rich Frontmatter Metadata
Use [Frontmatter](../../content/frontmatter.md) to provide explicit keywords and descriptions that might not appear naturally in the body text. This gives the search engine extra "hooks" into your content.
```yaml
---
title: "Integrating OAuth2"
description: "Learn how to implement secure user authentication and SSO."
keywords: ["login", "authentication", "sso", "security", "identity"]
---
```
### 2. The "Semantic Density" Strategy
RAG systems slice documents into small vector chunks. The first paragraph of every section should contain the highest density of relevant nouns and verbs related to that topic. This ensures the section's primary "meaning" is captured in the initial vector.
- **✅ Good**: "This guide explains how to implement **OAuth2 Single Sign-On (SSO)** to provide secure **authentication** for your documentation site."
- **❌ Poor**: "In this section, we'll talk about how it works and how you can set it up easily."
### 3. Avoiding Pronoun Ambiguity
In a chunked database, a sentence like "It works with any provider" is useless if the preceding paragraph defining "It" was sliced into a different chunk. Be explicit.
- **❌ Ambiguous**: "It is highly scalable."
- **✅ Explicit**: "The **docmd Search Engine** is designed to be highly scalable."
## Trade-offs
Writing for semantic density can feel more formal or repetitive than traditional narrative writing. However, the resulting improvement in discoverability and AI response accuracy makes this a vital practice for enterprise-grade documentation.
---
## [Structuring Documentation for AI Agents](https://docs.docmd.io/guides/ai-optimisation/structure-for-llms/)
---
title: "Structuring Documentation for AI Agents"
description: "How to move from visual formatting to semantic structuring to improve the accuracy of AI coding assistants."
---
## Problem
Human readers rely on visual cues and inferred context. AI agents consume raw text streams. Without rigorous semantic structure, models struggle to map relationships between concepts. This leads to poor reasoning and inaccurate coding suggestions.
## Why it matters
If your documentation is not optimised for LLMs, developers using tools like GitHub Copilot or Cursor face more hallucinations. This degrades the developer experience. Users often blame your product for errors generated by their AI assistants.
## Approach
Transition from a "visual-first" to a **"semantic-first"** mindset. Use standard Markdown features - strict header hierarchies, explicit code block tags, and descriptive alt text - to provide a machine-readable roadmap. docmd processes this structure into optimised outputs via the [LLMs Plugin](../../plugins/llms.md).
## Implementation
### 1. Strict Header Hierarchy
Avoid skipping header levels for visual effects. A consistent hierarchy allows LLMs to understand the scope and relationships of different sections.
- **`#` Title**: The primary subject of the page.
- **`##` Major Concept**: An atomic, high-level topic.
- **`###` Detail**: A specific sub-task or property.
* **❌ Poor**: Using `###` immediately after `#` for a smaller font size.
* **✅ Good**: `# Installation` followed by `## Prerequisites` and `### System Requirements`.
### 2. Descriptive Metadata for Media
LLMs cannot "see" images or diagrams. Provide architectural context in the alternative text or an adjacent paragraph.
```markdown

```
### 3. Explicit Code Block Labelling
Specify the language for every fenced code block using [Syntax Highlighting](../../content/syntax/index.md). This allows LLMs to parse the Abstract Syntax Tree (AST) correctly.
```json
"plugins": {
"llms": {}
}
```
### 4. Semantic Containers
Use [Callouts](../../content/containers/callouts.md) rather than generic blockquotes to provide intent. docmd's semantic containers help AI models distinguish core instructions from supplementary warnings.
## Trade-offs
Semantic rigour requires discipline. You cannot use Markdown features purely as decorative elements. However, this discipline produces documentation that is significantly more accessible to both AI agents and human readers using assistive technologies.
---
## [Alongside Other Tools](https://docs.docmd.io/guides/integrations/alongside-other-tools/)
---
title: "Alongside Other Tools"
description: "Strategies for integrating docmd into a multi-tool documentation ecosystem to create a seamless user experience."
---
## Problem
Large organisations rarely use a single tool for documentation. You might use Confluence for internal specs, Stoplight for APIs, and GitHub for code. Integrating disparate sources into a unified user journey is a challenge. Users often jump between disconnected portals with different styles and navigation.
## Why it matters
A fragmented documentation experience ruins developer trust and increases cognitive load. If a user switches between completely different interfaces to follow a tutorial, they lose context or abandon your product. Unifying your tools ensures a professional, cohesive experience that encourages exploration.
## Approach
Use docmd as your primary documentation hub. By using the [Menubar](../../configuration/menubar.md) for unified navigation and [Embed Containers](../../content/containers/embed.md) for third-party content, you can create a seamless interface that hides multi-tool complexity.
## Implementation
### 1. Unified Global Navigation
Use the `menubar` configuration to link your various documentation portals together. This ensures users can always find their way back to the main guides, regardless of which subdomain they are on.
```json
"layout": {
"menubar": {
"left": [
{ "text": "Guides", "url": "/" },
{ "text": "API Reference", "url": "https://api.example.com" },
{ "text": "Community", "url": "https://forum.example.com" }
]
}
}
```
### 2. Seamless Embedding
For tools that provide a web interface (like interactive API explorers or dashboard previews), use the `::: embed` container. This displays them directly within your docmd pages, keeping users within your branded environment.
```markdown
# Interactive API Explorer
::: embed "https://api.example.com/v1/explorer"
:::
```
For more information, see the [Embed Reference](../../content/containers/embed.md).
### 3. Content Aggregation
For external content that must be searchable alongside core documentation, consider a build step that fetches data from other sources and converts it into Markdown. This allows docmd to index all information in a single, unified [Search Index](../../plugins/search.md).
## Trade-offs
While embedding provides a unified look, it can introduce performance overhead or "scroll-nesting" issues on mobile devices. Content within an iframe is not natively indexed by docmd's search engine. If search parity is critical, prioritising [OpenAPI Generation](openapi-generation.md) or other Markdown-based ingestion methods is recommended.
---
## [Choosing Your Deployment Method](https://docs.docmd.io/guides/integrations/choosing-deployment-method/)
---
title: "Choosing Your Deployment Method"
description: "A practical guide to choosing between the docmd GitHub App, GitHub Action, Starter Template, and Deployer Package — with a decision matrix and real-world scenarios."
---
# Choosing Your Deployment Method
docmd offers four ways to get your documentation live. They all produce the same output — a static site deployed to GitHub Pages or a hosting provider of your choice — but they differ in how much control you want and where you are starting from.
## Quick Decision Matrix
| | [GitHub App](../../integrations/github-app.md) | [Starter Template](../../integrations/starter-template.md) | [GitHub Action](../../integrations/github-action.md) | [Deployer Package](../../deployment/deployer-package.md) |
|---|---|---|---|---|
| **Starting point** | Existing repo | New repo | Any | Any |
| **Setup effort** | One click | Two clicks | Write YAML | Run a command |
| **Workflow file** | Auto-generated | Included | You write it | Auto-generated |
| **Customisable** | After generation | From the start | Fully | Fully |
| **Hosting target** | GitHub Pages | GitHub Pages | GitHub Pages | Any provider |
| **Monorepo support** | ✓ Auto-detected | — | Manual `--cwd` | ✓ |
| **Non-GitHub hosting** | ✗ | ✗ | Adaptable | ✓ Docker, Nginx, Vercel, Netlify… |
## Scenario Guide
### "I want docs live in under two minutes with zero setup"
Use the **[GitHub App](../../integrations/github-app.md)**. Install it, select your repository, done. It detects your config, generates the workflow, enables GitHub Pages, and deploys — without you touching a single file.
::: button "Install GitHub App" external:https://github.com/apps/docmd/installations/new icon:github color:#2ea44f
---
### "I'm starting a brand-new documentation site"
Use the **[Starter Template](../../integrations/starter-template.md)**. Click "Use this template" on GitHub, update `docmd.config.json` with your title and URL, enable GitHub Pages once, and push. Everything is pre-wired.
::: button "Use Starter Template" external:https://github.com/docmd-io/docmd-template/generate icon:github
---
### "I have an existing CI/CD pipeline and want to add docs to it"
Use the **[GitHub Action](../../integrations/github-action.md)**. Drop `docmd-io/deploy@v1` into your existing workflow. It composes cleanly with other steps — run tests, build your app, then build docs, all in one job.
---
### "I'm deploying to Vercel, Netlify, Docker, or my own server"
Use the **[Deployer Package](../../deployment/deployer-package.md)**. Run `npx @docmd/core deploy --vercel` (or `--netlify`, `--docker`, `--nginx`) to generate provider-specific config files tailored to your `docmd.config.json`.
---
### "I'm in a monorepo with docs in a subdirectory"
Both the **GitHub App** and the **Deployer Package** handle this automatically. The App detects configs anywhere in the repository tree and injects the correct `--cwd` flag. The Deployer Package reads your config from the current working directory.
If you prefer the GitHub Action, pass `--cwd` manually:
```yaml
- run: npx @docmd/core build --cwd packages/docs
```
---
### "I want to preview docs on every pull request"
Use the **GitHub Action** combined with a PR preview service (e.g. Cloudflare Pages preview deployments or a self-hosted preview environment). See [Previewing Changes](../workflows-teams/previewing-changes.md) for a full walkthrough.
---
## How They Fit Together
These methods are not mutually exclusive. A common progression looks like this:
```
Start with the GitHub App (fastest path to live)
↓
Customise the generated workflow file as your needs grow
↓
Add the Deployer Package to generate Nginx/Docker configs for self-hosting
↓
Integrate the Action into a broader CI/CD pipeline
```
You can also mix them: use the Starter Template for a new project, then add the Deployer Package later to generate a Docker image for your staging environment.
## Comparing Build Triggers
| Method | Triggers on push | Manual trigger | PR preview |
|---|---|---|---|
| GitHub App | ✓ (auto-configured) | ✓ `workflow_dispatch` | Requires extra step |
| Starter Template | ✓ `main` / `master` | ✓ `workflow_dispatch` | Requires extra step |
| GitHub Action | You configure | You configure | You configure |
| Deployer Package | Generates the file; triggers depend on your workflow | — | — |
## Further Reading
- [GitHub Action reference](../../integrations/github-action.md)
- [GitHub App reference](../../integrations/github-app.md)
- [Starter Template reference](../../integrations/starter-template.md)
- [Deployer Package reference](../../deployment/deployer-package.md)
- [GitHub Actions CI/CD guide](./github-actions-cicd.md)
- [Previewing Changes](../workflows-teams/previewing-changes.md)
---
## [Existing Markdown Repos](https://docs.docmd.io/guides/integrations/existing-markdown-repos/)
---
title: "Existing Markdown Repos"
description: "How to instantly generate a professional documentation site from your existing Markdown files with zero configuration."
---
## Problem
You have an established repository with hundreds of raw Markdown files - perhaps a legacy wiki, an Obsidian vault, or technical notes. Manually converting frontmatter, fixing broken links, and restructuring files for a new engine is a difficult task that often prevents modernisation.
## Why it matters
Your content should remain portable and tool-agnostic. A high-quality documentation engine adapts to your existing files, rather than forcing files to adapt to the engine. Avoiding vendor lock-in ensures your intellectual property remains standard, readable, and future-proof.
## Approach
docmd adheres to strict CommonMark specifications and is designed to be **zero-config** by default. Point the docmd CLI at any directory containing Markdown files, and it intelligently bootstraps a full-featured documentation site without modifying a single line of source content.
## Implementation
### 1. Instant Bootstrapping
Navigate to your existing Markdown folder and run the development server. docmd scans your directory structure and builds a functional site in memory instantly.
```bash
cd my-existing-docs/
npx @docmd/core dev
```
### 2. Automatic Navigation (Auto-Router)
If no `navigation.json` or `docmd.config.json` is found, docmd triggers its [Auto-Router](../../configuration/navigation.md#automatic-sidebar-generation). It recursively maps your folder structure, prettifies directory names (e.g., `getting-started` becomes `Getting Started`), and generates a logical sidebar taxonomy automatically.
### 3. Intelligent Title Inference
You don't need to add `title` frontmatter to every file. docmd uses a cascading resolution strategy to determine page titles:
1. **Frontmatter**: Checks for a `title` or `h1` key.
2. **First Heading**: Extracts the first `# Heading` found in the file content.
3. **Filename**: Prettifies the filename as a fallback (e.g., `install-guide.md` becomes `Install Guide`).
### 4. Resilient Syntax Handling
docmd is built to be resilient. If existing files contain proprietary syntax or legacy shortcodes from other engines, they render safely as raw text or are skipped. This ensures your build never fails due to unmigrated content.
## Trade-offs
Automatic sidebars are typically sorted alphabetically by filename. While naming files like `01-intro.md` and `02-setup.md` works well, descriptive filenames may appear in an unintuitive order. For production-ready sites, we recommend transitioning to manual [Navigation Configuration](../../configuration/navigation.md) for full control over the user journey.
---
## [GitHub Actions CI/CD](https://docs.docmd.io/guides/integrations/github-actions-cicd/)
---
title: "GitHub Actions CI/CD"
description: "How to automate your documentation builds and deployments using GitHub Actions and docmd for a high-velocity workflow."
---
## Problem
Building and deploying documentation manually from a local machine is prone to errors, environment inconsistencies, and security risks. It creates a bottleneck, as deployments depend on a single individual's availability.
## Why it matters
Continuous Deployment (CD) ensures your documentation is always in sync with your software. When a technical update is merged, it should reach users within minutes. Automation guarantees every build happens in a clean, reproducible environment, maintaining quality and reliability.
## Approach
Use GitHub Actions to run the docmd build pipeline on every push or Pull Request. The resulting static assets can be automatically deployed to hosting providers like GitHub Pages, Cloudflare Pages, or containerised environments using Docker.
## Implementation
### 1. Standard GitHub Pages Workflow
Create `.github/workflows/docs.yml` to automate the build and deployment process.
```yaml ".github/workflows/docs.yml"
name: Deploy Docs
on:
push:
branches: [main]
permissions:
contents: read
pages: write
id-token: write
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: 'npm'
- run: npm install
# Build the site into the 'site/' directory
- run: npx @docmd/core build
- name: Upload Artifact
uses: actions/upload-pages-artifact@v3
with:
path: site/
- name: Deploy to GitHub Pages
uses: actions/deploy-pages@v4
```
### 2. Containerised Deployment (Docker)
If you host your own documentation, use the [Deploy Command](../../deployment/index.md) to generate a production-ready `Dockerfile` and server configurations.
```bash
# Generate Docker and Nginx configs locally
npx @docmd/core deploy --docker --nginx
```
You can update your GitHub Action to build and push this Docker image to a registry (like Docker Hub or GitHub Container Registry) whenever you release a new version.
### 3. Pull Request Previews
Enhance your workflow by generating ephemeral preview environments for every Pull Request. This allows reviewers to see the rendered documentation before merging it into the main branch. See the [Previewing Changes Guide](../workflows-teams/previewing-changes.md) for more details.
## Trade-offs
Automated CI/CD requires initial setup time and management of secrets (e.g., API tokens). However, the long-term benefits of a "hands-off" deployment process - including reduced human error and faster update cycles - far outweigh the initial investment. For large sites, ensure your workflow only triggers when files in your documentation directory change to save CI minutes.
---
## [OpenAPI Generation](https://docs.docmd.io/guides/integrations/openapi-generation/)
---
title: "OpenAPI Generation"
description: "How to integrate OpenAPI/Swagger schemas into your docmd workflow for automated and synchronised API reference documentation."
---
## Problem
Manually maintaining REST API documentation is an operational risk. When an engineer modifies an endpoint or updates a schema in code, documentation becomes obsolete. Keeping these in sync manually is tedious, error-prone, and frequently leads to integration failures for consumers.
## Why it matters
Inaccurate API references cause developer frustration and increase support tickets. Automation ensures your documentation remains the "source of truth", reflecting the actual state of your API at every build. This allows engineers to focus on building features rather than updating tables manually.
## Approach
Implement an asynchronous build pipeline that converts your `openapi.json` or `swagger.yaml` schema into standard Markdown files. Because docmd excels at rendering Markdown with complex [Containers](../../content/containers/index.md), the resulting API reference feels integrated and visually consistent with the rest of your documentation.
## Implementation
### 1. Build Pipeline Integration
Use a tool like `widdershins` or a custom script to generate Markdown from your OpenAPI schema as a pre-build step in your CI/CD pipeline.
```json "package.json"
// package.json
{
"scripts": {
"docs:generate-api": "npx widdershins --search false openapi.yaml -o docs/api/reference.md",
"docs:build": "npm run docs:generate-api && npx @docmd/core build"
}
}
```
### 2. Optimising API Layouts
API references are often content-dense, with large tables for parameters and nested schemas. Use [Frontmatter](../../content/frontmatter.md) to optimise the page layout for readability.
```markdown
---
title: "REST API Reference"
layout: "full" # Maximises horizontal space for dense tables
---
```
Setting `layout: "full"` removes the right-hand Table of Contents sidebar, providing more room for wide code blocks and response examples.
### 3. Enhancing with docmd Containers
Post-process the generated Markdown to inject docmd features like [Tabs](../../content/containers/tabs.md) for multi-language code samples or [Callouts](../../content/containers/callouts.md) for authentication warnings.
````markdown
::: tabs
== tab "cURL"
```bash
curl -X GET "https://api.example.com/v1/users"
```
== tab "Node.js"
```javascript
const users = await client.getUsers();
```
:::
````
## Trade-offs
Machine-generated documentation is excellent for technical accuracy but lacks the "human touch" required for effective learning. We recommend using OpenAPI generation for the **Technical Reference** (endpoints, parameters, schemas) while providing handwritten **Tutorials** and **Conceptual Guides** to explain the context and use cases.
---
## [Guides Overview](https://docs.docmd.io/guides/overview/)
---
title: "Guides Overview"
description: "Pick the right guide cluster for what you're trying to do — writing quality, workflows, search, performance, AI optimisation, or integrations."
---
# Guides Overview
Guides are intermediate-to-advanced "how do I do X well" content. Pick the cluster that matches your goal. Most users start with **Writing Quality** — those guides apply to every docmd site regardless of stack or feature focus.
::: grids
::: grid
::: card "Writing Quality" icon:pen-tool
For every docmd user. Covers scalable technical writing, readability, task-vs-concept, and avoiding anti-patterns. Most universally relevant cluster.
[Start with Writing Quality →](./writing-quality/scalable-technical-writing)
:::
:::
::: grid
::: card "Workflows & Teams" icon:users
For documentation leads and teams. Git-based workflows, preview environments, consistency at scale, and release workflows.
[Explore Workflows & Teams →](./workflows-teams/setting-up-workflow)
:::
:::
::: grid
::: card "Search" icon:search
For everyone with a search box. Improve relevance, speed, local-first indexing, and add semantic search.
[Tune your search →](./search/improving-search-relevance)
:::
:::
::: grid
::: card "Performance" icon:gauge
For teams that care about Time to Interactive. Sub-100ms navigation, CDN deployment, JS payload reduction, low-end devices, caching.
[Speed up your site →](./performance-delivery/sub-100ms-navigation)
:::
:::
::: grid
::: card "AI & LLMs" icon:brain-circuit
For teams that want their docs to be readable by AI agents and LLMs. MCP, agent skills, llms.txt, semantic search design, chunking, context preservation.
[Make docs AI-ready →](./ai-optimisation/mcp-and-agent-skills)
:::
:::
::: grid
::: card "Integrations" icon:plug
For connecting docmd to the rest of your stack. OpenAPI generation, GitHub Actions, existing Markdown repos, parallel tooling.
[See integrations →](./integrations/choosing-deployment-method)
:::
:::
:::
::: callout tip "Not sure where to start?"
If you're brand new to docmd, skip the Guides and read [Getting Started](/getting-started/quick-start) first. Guides assume you're already comfortable with the basics.
:::
---
## [Caching Strategies](https://docs.docmd.io/guides/performance-delivery/caching-strategies/)
---
title: "Caching Strategies"
description: "How to optimise your documentation site's performance using immutable caching, Etag revalidation, and production-ready server configurations."
---
## Problem
When a documentation site is served without proper cache-control headers, browsers unnecessarily re-download images, CSS, and JavaScript bundles. This results in visual stuttering, increased bandwidth consumption, and a poor experience for returning users.
## Why it matters
Effective caching is highly impactful for improving perceived performance. Storing static assets locally in the user's browser eliminates the latency of repeated network requests. This makes navigation feel fluid and reliable, even on unstable connections.
## Approach
Implement a two-tier caching strategy: **Immutable Caching** for static assets (CSS, JS, images) and **Etag Revalidation** for dynamic content (HTML, JSON). docmd facilitates this by generating production-ready configurations that handle cache-busting automatically.
## Implementation
### 1. Production-Ready Server Configs
The easiest way to implement optimal caching is by using the [Deploy Command](../../deployment/index.md) to generate your server configuration.
```bash
# Generate an optimised Nginx configuration
npx @docmd/core deploy --nginx
```
### 2. Immutable Assets
For assets that don't change frequently (like theme styles and core scripts), use long-term caching. docmd appends version hashes to these assets to ensure users only download new versions when you update your documentation.
```nginx
# Example Nginx rule for immutable assets
location ~* \.(?:css|js|webp|png|svg|woff2)$ {
expires 1y;
add_header Cache-Control "public, max-age=31536000, immutable";
}
```
### 3. HTML & Navigation Revalidation
Your HTML files and `navigation.json` should always be checked for updates. This ensures users see the latest content immediately. Use the `no-cache` directive to force the browser to revalidate with the server using Etags.
```nginx
# Example Nginx rule for HTML files
location ~* \.html$ {
add_header Cache-Control "no-cache, must-revalidate";
}
```
## Trade-offs
### Stale Content vs. Performance
Setting long cache times for assets is highly performant but requires a reliable "cache-busting" strategy. docmd handles this automatically for core files. If you manually add assets to your `static/` directory, you must update their references (e.g., by changing the filename or adding a query parameter) when content changes.
### CDN Integration
If you use a CDN (like Cloudflare or AWS CloudFront), ensure it honours your server's `Cache-Control` headers. Most modern CDNs provide "instant purge" capabilities. We recommend triggering this as part of your CI/CD pipeline whenever you deploy a new version.
---
## [CDN & Edge Deployment](https://docs.docmd.io/guides/performance-delivery/deploying-cdn-edge/)
---
title: "CDN & Edge Deployment"
description: "How to minimise global latency by deploying your static documentation to a Content Delivery Network (CDN) or Edge Network."
---
## Problem
Hosting documentation on a single server in one geographic region (e.g., US-East) creates significant network latency for users elsewhere. Every page load, image, and script travels thousands of miles. This makes your documentation feel sluggish for a global audience.
## Why it matters
High latency directly harms the developer experience. Even if your documentation is well-written and lightweight, the "Time to First Byte" (TTFB) is limited by physics. If your site feels slow, developers lose focus or abandon your tool in favour of faster alternatives.
## Approach
The optimal solution is to deploy your site to an Edge CDN. docmd generates pure static assets (HTML, CSS, JS), making it perfectly suited for edge distribution. CDNs replicate files across globally distributed "Edge Nodes" to serve users from the closest data centre.
## Implementation
### 1. Choose a Platform
docmd natively supports all modern static hosting and edge platforms. We recommend the following for their global performance and ease of use:
* **Cloudflare Pages**: Extremely fast global edge network with built-in DDoS protection.
* **Vercel**: Optimised for performance with excellent developer workflow integration.
* **Netlify**: Powerful automation features and a reliable global CDN.
### 2. Automate the Build
Use a CI/CD pipeline to build and deploy your site automatically whenever you push changes. See the [GitHub Actions Guide](../../guides/integrations/github-actions-cicd.md) for detailed examples.
```yaml ".github/workflows/deploy.yml"
# .github/workflows/deploy.yml
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
# Build the site into the default 'site/' directory
- run: npm install && npx @docmd/core build
# Example: Deploying to Cloudflare Pages
- name: Deploy
uses: cloudflare/pages-action@v1
with:
apiToken: ${{ secrets.CF_API_TOKEN }}
accountId: ${{ secrets.CF_ACCOUNT_ID }}
projectName: my-docs
directory: site
```
### 3. Verification
Once deployed, verify global performance using tools like PageSpeed Insights or global ping tests. You should see sub-100ms response times from almost any worldwide location.
## Trade-offs
Global edge networks abstract away server management, which benefits documentation teams. However, debugging regional caching issues can occasionally be more complex than reviewing a single server log. Using platforms with reliable "instant cache invalidation" ensures users always see the latest version immediately after a deployment.
---
## [Low-End Device Optimisation](https://docs.docmd.io/guides/performance-delivery/low-end-devices/)
---
title: "Low-End Device Optimisation"
description: "How to build high-performance, accessible documentation that works easily on low-powered hardware and slow network connections."
---
## Problem
Modern documentation sites often rely on heavy JavaScript runtimes to display static text. For users on older mobile phones or slow connections, these sites take several seconds to load. The processor struggles to parse large JS bundles, resulting in "input lag" and a poor reading experience.
## Why it matters
Technical documentation should be universally accessible. Forcing users on constrained hardware to download a heavy framework creates an unnecessary barrier to learning. A lightweight site ensures product information is available to everyone, regardless of hardware or internet speed.
## Approach
Adopt an **HTML-First** strategy. docmd uses a zero-framework architecture. The primary content is rendered into standard HTML during the build process. This keeps the browser's main thread unblocked, ensuring smooth scrolling and snappy navigation even on budget devices.
## Implementation
### 1. Minimal Runtime Footprint
By default, docmd does not use React or Vue for its core UI. This pre-rendered approach ensures the initial "First Contentful Paint" happens almost immediately. To maintain this performance:
* **Limit Custom Scripts**: Avoid adding large third-party libraries to your `customJs` configuration.
* **Use Native Browser Features**: Rely on standard CSS and HTML5 elements.
### 2. Strategic Plugin Management
While [Plugins](../../plugins/usage.md) add powerful features, they introduce performance overhead. For example, the [Mermaid Plugin](../../plugins/mermaid.md) requires a large engine to render diagrams. If your users are on low-end devices, use static images instead of client-side rendering.
### 3. Responsive and Optimised Media
Avoid serving oversized images to mobile users. Use modern formats like WebP and consider the `` tag for granular control over responsive assets.
```html
```
Using the `loading="lazy"` attribute ensures images are only downloaded as they enter the user's viewport, saving bandwidth.
### 4. Efficient Search Indexing
docmd generates scoped search indices to keep the memory footprint low. However, for extremely large sites, the [Search Plugin](../../plugins/search.md) can still be memory-intensive. Optimise your index as described in the [Local-First Search Guide](../search/local-first-search.md).
## Trade-offs
Prioritising performance for low-end devices means avoiding "heavy" interactive features like complex 3D visualisations. This is a deliberate design choice that values inclusivity and speed over visual complexity. It ensures your documentation remains useful for the widest audience possible.
---
## [Reducing JS Payload](https://docs.docmd.io/guides/performance-delivery/reducing-javascript-payload/)
---
title: "Reducing JS Payload"
description: "How to maintain a high-performance documentation site by optimising your JavaScript dependencies and using docmd's zero-framework architecture."
---
## Problem
Many modern documentation tools rely on heavy JavaScript frameworks (like React or Vue) to render static text. These frameworks add several hundred kilobytes to the initial page load. The browser must download, parse, and execute large amounts of code before the site becomes fully interactive. This leads to slow loading times and "ghost clicks" on low-end devices.
## Why it matters
A large JavaScript payload directly impacts "Time to Interactive" (TTI). In technical documentation, users need answers quickly. Any delay caused by heavy framework initialisation is a significant usability barrier. Keeping your payload small ensures that search, navigation, and theme switching are instantaneous.
## Approach
docmd uses a **zero-framework** architecture for its core client-side logic. By utilising Vanilla JavaScript and native browser APIs instead of a heavy Virtual DOM, we keep the total JS payload for a standard site under **20KB**. This lightweight foundation ensures maximum performance across all devices.
## Implementation
### 1. Use Native Browser APIs
Avoid importing heavy libraries like jQuery or Lodash for simple tasks. Modern browsers have reliable native APIs that handle almost any documentation-related requirement with zero overhead.
```javascript
// Add custom scripts in docmd.config.json
customJs: ["/static/js/my-custom-logic.js"]
```
### 2. Strategic Plugin Management
While [Plugins](../../plugins/usage.md) add powerful features, some significantly increase your JavaScript payload. For example, the [Mermaid Plugin](../../plugins/mermaid.md) requires a large client-side library to render diagrams. Only enable heavy plugins if they are essential to your content.
### 3. Defer Non-Critical Scripts
If you include third-party services like analytics or feedback widgets, ensure they load asynchronously or are deferred. This prevents them from blocking the rendering of your documentation.
```html
```
### 4. Optimise Assets
Ensure any custom JavaScript you provide is minified and compressed. docmd handles the minification of its core assets, but you are responsible for optimising any files you add to your `static/` directory.
## Trade-offs
Building complex interactive features with Vanilla JavaScript requires more manual effort than using a declarative framework. However, for documentation - where 95% of the content is static text and images - the performance gains of a zero-framework approach far outweigh the convenience of a heavy framework.
---
## [Sub-100ms Navigation](https://docs.docmd.io/guides/performance-delivery/sub-100ms-navigation/)
---
title: "Sub-100ms Navigation"
description: "How docmd's native SPA router and intent-based prefetching deliver instant page transitions for an optimal reading experience."
---
## Problem
Traditional multi-page navigation triggers a full browser reload on every click. This creates a disruptive "white flash" and breaks the reader's flow. The browser discards the current state, requests new HTML, and re-parses CSS and JavaScript - even if only the central content area changes.
## Why it matters
Users frequently jump between tutorials, API references, and conceptual guides. If transitions take seconds, cognitive friction discourages exploration. Instant navigation makes documentation feel like a native application, significantly improving user satisfaction and engagement.
## Approach
docmd utilises a high-performance **Single Page Application (SPA) Router** built on pre-generated static files. The browser intercepts link clicks, fetches only necessary content in the background, and updates the page dynamically without a full reload. This preserves the state of the sidebar, table of contents, and theme settings for near-instant transitions.
## Implementation
The docmd SPA router uses advanced techniques to achieve sub-100ms navigation speeds:
### 1. Intent-Based Prefetching
When a user hovers over a navigation link, docmd detects the intent and initiates a background fetch for the target page. By the time the user clicks, the data is often already in the browser's cache. Transitions feel instantaneous.
### 2. Partial DOM Updates
Instead of re-rendering the entire page, docmd intelligently updates only necessary functional zones:
* **Main Content**: The primary Markdown-rendered body.
* **Table of Contents**: Refreshed to match new headers.
* **Navigation State**: Updates active and expanded sidebar states.
### 3. Lifecycle Events for Custom Logic
Because the browser avoids full reloads, standard events like `DOMContentLoaded` only fire once. To execute custom JavaScript after every navigation, listen for the `docmd:page-mounted` event.
```javascript
document.addEventListener("docmd:page-mounted", (event) => {
const currentPath = event.detail.path;
console.log(`Successfully navigated to: ${currentPath}`);
if (currentPath.includes("/api/")) {
initApiConsole();
}
});
```
For more details, see the [Client-Side Events](../../api/client-side-events.md) documentation.
## Trade-offs
### Script Execution
The SPA router automatically re-executes `
```
### `docmd.compile(markdown, config)`
Compiles raw Markdown into a full HTML document string using the default docmd layout.
**Parameters:**
- `markdown` (String): The raw Markdown content.
- `config` (Object): Configuration overrides (same schema as `docmd.config.json`).
**Returns:** `Promise`: The complete HTML document.
### Example: Live Preview
To ensure style isolation, render the output inside an `