Guides

In-depth guides for configuring, writing, and deploying your documentation.


Configuration

mdsvr can be configured via a settings.json file in the _mdsvr/ directory in your docs root. All settings are optional — the server works out of the box with sensible defaults.

Creating settings.json

Use the CLI to create a starter configuration:

npx mdsvr . --init

Or create it manually:

{
  "$schema": "https://mdsvr.js.org/schema/v2.json",
  "site": {
    "title": "My Docs",
    "description": "Project documentation"
  }
}

Site Settings

{
  "site": {
    "title": "My Documentation",
    "description": "A great documentation site",
    "baseUrl": "https://docs.example.com",
    "language": "en",
    "logo": {
      "src": "./assets/logo.svg",
      "alt": "My Project",
      "href": "/"
    },
    "favicon": "./assets/favicon.ico"
  }
}

Appearance

Control the look and feel of your documentation:

{
  "appearance": {
    "defaultTheme": "system",
    "allowThemeToggle": true,
    "accentColor": "#0969da",
    "codeTheme": {
      "light": "github",
      "dark": "github-dark"
    }
  }
}

Theme Options:

  • defaultTheme: "light", "dark", or "system" (follows OS preference)
  • allowThemeToggle: Show/hide the theme toggle button in the header
  • accentColor: Primary color for links, buttons, and accents

Configure the sidebar, breadcrumbs, and table of contents:

{
  "navigation": {
    "sidebar": {
      "enabled": true,
      "autoGenerate": true,
      "collapsible": true,
      "defaultOpen": true
    },
    "breadcrumbs": true,
    "prevNextLinks": true,
    "tocEnabled": true,
    "tocMaxDepth": 3
  }
}

Enable and configure the built-in search:

{
  "search": {
    "enabled": true,
    "placeholder": "Search docs...",
    "maxResults": 10
  }
}

Press ⌘+K (or Ctrl+K) to open the search modal.

SEO

Configure search engine optimization features:

{
  "seo": {
    "titleTemplate": "%s | My Docs",
    "defaultImage": "./assets/og-image.png",
    "twitterCard": "summary_large_image",
    "twitterSite": "@myhandle",
    "generateSitemap": true,
    "generateRssFeed": true,
    "og": {
      "enabled": true,
      "imageFormat": "jpg",
      "fontFamily": "Inter",
      "colors": {
        "background": "#0a0a0f",
        "text": "#ffffff"
      }
    },
    "rss": {
      "title": "My Docs Updates",
      "feedUrl": "/feed.xml",
      "siteUrl": "https://docs.example.com"
    }
  }
}

When enabled, these endpoints are automatically generated:

  • /sitemap.xml — XML sitemap for search engines
  • /feed.xml — RSS feed for blog posts (files with date in frontmatter)

MDX

Enable MDX support and configure available components:

{
  "mdx": {
    "enabled": true,
    "components": {
      "Callout": true,
      "CodeGroup": true,
      "Steps": true,
      "Card": true,
      "CardGroup": true,
      "Tabs": true,
      "Accordion": true,
      "Badge": true,
      "Mermaid": true
    }
  }
}

Files

Control which files are served, blocked, or hidden:

{
  "files": {
    "extensions": {
      "serve": [".md", ".mdx", ".png", ".jpg", ".svg", ".css", ".js"],
      "block": [".env", ".key", ".pem"],
      "hidden": ["settings.json", ".git", "node_modules"]
    },
    "indexFiles": ["README.md", "index.md", "index.html"],
    "ignorePatterns": ["**/node_modules/**", "**/.git/**"],
    "staticFolders": ["assets", "public", "images"]
  }
}
  • Files starting with _ are automatically hidden
  • Blocked extensions return 403 Forbidden
  • Hidden files return 404 Not Found (as if they don’t exist)
  • staticFolders: Array of folder names to serve as static assets (all files in these folders are served directly)

Customize the site footer:

{
  "footer": {
    "text": "Built with mdsvr",
    "links": [
      { "label": "GitHub", "href": "https://github.com/..." },
      { "label": "Twitter", "href": "https://twitter.com/..." }
    ]
  }
}

Hot Reload

Settings are automatically reloaded when _mdsvr/settings.json changes (except when using --no-watch flag). You’ll see a message in the console:

[mdsvr] Settings reloaded

Validation

Validate your settings.json:

npx mdsvr . --validate

This will check your configuration and report any errors.

Generate Settings

Configure static export generation options:

{
  "generate": {
    "basePath": "/docs",
    "outputDir": "dist"
  }
}

Options:

  • basePath: Base path for generated URLs (default: ""). Useful when deploying to a subdirectory (e.g., /docs)
  • outputDir: Default output directory for static exports (default: "dist")

These settings are used during static export with the --export flag.

Complete Example

Here’s a complete _mdsvr/settings.json with all available options:

{
  "$schema": "https://mdsvr.js.org/schema/v2.json",
  "site": {
    "title": "My Docs",
    "description": "Project documentation",
    "baseUrl": "https://docs.example.com",
    "language": "en",
    "logo": {
      "src": "./assets/logo.svg",
      "alt": "My Project",
      "href": "/"
    },
    "favicon": "./assets/favicon.ico"
  },
  "appearance": {
    "defaultTheme": "system",
    "allowThemeToggle": true,
    "accentColor": "#0969da"
  },
  "navigation": {
    "sidebar": {
      "enabled": true,
      "autoGenerate": true,
      "collapsible": true,
      "defaultOpen": true
    },
    "breadcrumbs": true,
    "prevNextLinks": true,
    "tocEnabled": true,
    "tocMaxDepth": 3
  },
  "search": {
    "enabled": true,
    "placeholder": "Search docs...",
    "maxResults": 10
  },
  "seo": {
    "titleTemplate": "%s | My Docs",
    "defaultImage": "./assets/og-default.png",
    "twitterCard": "summary_large_image",
    "generateSitemap": true,
    "generateRssFeed": false,
    "og": {
      "enabled": true,
      "imageFormat": "jpg"
    }
  },
  "files": {
    "extensions": {
      "serve": [
        ".md",
        ".mdx",
        ".txt",
        ".pdf",
        ".png",
        ".jpg",
        ".svg",
        ".css",
        ".js",
        ".json"
      ],
      "block": [".env", ".key", ".pem", ".p12"],
      "hidden": ["_mdsvr", "settings.json", ".git", "node_modules", ".DS_Store"]
    },
    "indexFiles": ["README.md", "index.md", "INDEX.md"]
  },
  "mdx": {
    "enabled": true,
    "components": {
      "Callout": true,
      "CodeGroup": true,
      "Steps": true,
      "Card": true,
      "CardGroup": true,
      "Tabs": true,
      "Accordion": true,
      "Badge": true,
      "Mermaid": true
    }
  },
  "footer": {
    "text": "Built with mdsvr",
    "links": [{ "label": "GitHub", "href": "https://github.com/..." }]
  }
}

Writing Content

Tips and best practices for creating great documentation.

File Organization

docs/
├── README.md              # Homepage
├── 01-getting-started/    # Getting started guides
├── 02-settings/           # Settings & configuration
├── 03-features/           # Feature documentation
└── 04-reference/          # API/reference docs

Frontmatter

Every page should have frontmatter:

---
title: Page Title
description: Brief description for SEO
---

Optional fields:

---
title: Page Title
description: Description
date: 2025-01-15 # For RSS feed
author: John Doe # Attribution
tags: [setup, guide] # For organization
noIndex: true # Exclude from search
---

Markdown Tips

Use Descriptive Headings

<!-- Good -->

## Installing via npm

<!-- Less clear -->

## Installation

Use relative paths:

See [configuration guide](./README.md)

Code Blocks

Always specify language:

```javascript
const x = 1;
```

MDX Components

Use components for interactive content:

<Callout type="warning">Important information here</Callout>

<Steps>
  ### Step 1
  First step content

### Step 2

Second step content

</Steps>

See MDX Components for all available components.

Images & Assets

Place assets in docs/assets/:

![Alt text](../assets/screenshot.png)

Note: The assets folder is optional - create it if you need to include images in your documentation.

Best Practices

Follow these guidelines to ensure your documentation passes validation and works correctly in both server mode and static export.

File Naming

  • Avoid dots in filenames — Don’t use dots in the middle of filenames (except for the extension)
    • 0.1.quickstart.md or config.v2.md
    • 01-quickstart.md or config-v2.md
  • Use descriptive namesquickstart.md not page1.md
  • Use kebab-case — Separate words with hyphens for clean URLs
  • Numbered prefixes — Use 01-, 02- for ordering in sidebar
  • Use relative paths — Never use absolute paths starting with /
    • [Guide](/docs/guide)
    • [Guide](./guide) or [Guide](../guide)
  • Remove file extensions — Links without .md/.mdx work in both modes
    • [Setup](./setup)
    • [Setup](./setup)
  • Link to directories, not index files — Use directory paths instead of README/index
    • [Getting Started](./01-getting-started/README)
    • [Getting Started](./01-getting-started)
  • Validate anchors — Ensure #section links match actual headings

Heading Structure

  • Always include an H1 — Every page must have a top-level heading (# Title)
  • Maintain hierarchy — Don’t skip heading levels
    • ❌ H1 → H3 (skips H2)
    • ✅ H1 → H2 → H3
  • Use descriptive headings — Make them searchable and meaningful

Assets & Images

  • Use relative paths for assets — Same rules as links
    • ![Screenshot](/assets/screenshot.png)
    • ![Screenshot](../assets/screenshot.png)
  • Ensure files exist — Validator checks for missing images and assets
  • Use supported formats — PNG, JPG, SVG, WebP, GIF, ICO

General Guidelines

  1. Start with a README.md — Required for homepage
  2. Keep pages focused — One topic per page
  3. Link liberally — Help users navigate between related topics
  4. Use MDX components — Callouts for warnings, Steps for procedures
  5. Add frontmatter — Include title and description for SEO
  6. Test with validator — Run npx mdsvr . --validate to check your docs

Validation

Run the validator to check your documentation:

npx mdsvr ./docs --validate

The validator checks for:

  • 🔗 Broken internal links
  • ⚠️ Absolute paths (should use relative)
  • ⚠️ Links with .md/.mdx extensions
  • ⚠️ Links to index files (use directory paths)
  • 🔗 Broken anchor links
  • 🖼️ Missing assets
  • 📁 Filenames with dots in the middle
  • 📌 Missing H1 headings
  • 📝 Heading hierarchy violations

Use --autofix to automatically fix some issues:

npx mdsvr ./docs --validate --autofix

Deployment

Deploy your documentation to any static hosting platform.

Export Static Site

Generate static HTML files:

npx mdsvr ./docs --export

This creates _html/public/ with your static site.

Custom Output

npx mdsvr ./docs --export ./dist

Export Caching

mdsvr uses intelligent caching to speed up exports:

  • File-level caching: Only re-renders pages whose source files changed
  • OG image caching: Only regenerates OG images when content changes
  • Settings tracking: Detects when _mdsvr/settings.json changes and forces full re-export

The export state is stored in _mdsvr/export-state.json.

Force Full Export

To bypass cache and force a complete re-export:

# Delete export state only
rm _mdsvr/export-state.json

# Then export
npx mdsvr ./docs --export

Note: Only delete export-state.json, not the entire _mdsvr directory, to preserve your settings.

Firebase Hosting

# Export
npx mdsvr ./docs --export

# Deploy
cd _html
firebase init
firebase deploy

Netlify

Via CLI

npx mdsvr ./docs --export
netlify deploy --dir=_html/public --prod

Via Git

  1. Push docs to GitHub
  2. Connect repo to Netlify
  3. Set build command: npx mdsvr . --export _html/public
  4. Publish directory: _html/public

Vercel

# vercel.json
{
  "buildCommand": "npx mdsvr . --export _html/public",
  "outputDirectory": "_html/public"
}

GitHub Pages

# Export to docs folder
npx mdsvr ./src --export ./docs

# Or use GitHub Actions

GitHub Actions Workflow

# .github/workflows/deploy.yml
name: Deploy
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: npx mdsr . --export
      - uses: peaceiris/actions-gh-pages@v3
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          publish_dir: ./_html/public

Docker

Using Pre-built Image

docker pull ghcr.io/satoshiman/mdsvr:latest
docker run -d \
  -p 1800:1800 \
  -v /path/to/docs:/app/docs \
  ghcr.io/satoshiman/mdsvr:latest

Build Your Own

FROM ghcr.io/satoshiman/mdsvr:latest
COPY ./docs /app/docs
docker build -t my-docs .
docker run -d -p 1800:1800 my-docs

VPS / Self-Hosted

Export and serve with any web server:

# Export
npx mdsvr ./docs --export /var/www/docs

# Nginx config
server {
    listen 80;
    root /var/www/docs;
    try_files $uri $uri/ $uri.html =404;
}

Clean URLs

Exported sites use clean URLs:

  • /guide/setup/ instead of /guide/setup.html
  • Works on all modern static hosts