963 markdown files reformatted with the repository's pinned prettier so pnpm format:check covers the folded tree like every other repo file. The formatter's embedded-language pass also normalized code fences (TS semicolons, closed HTML tags in examples, lowercased CSS hex colors, one renumbered list that skipped an index). Alphanumeric token deltas vs the fold commit were audited file-by-file; all are formatter-equivalent markup normalizations plus the four sanitized skills.
4.3 KiB
4.3 KiB
name, description
| name | description |
|---|---|
| vitepress-dynamic-routes | Generate multiple pages from a single markdown template using paths loader files |
Dynamic Routes
Generate many pages from a single markdown file and dynamic data. Useful for blogs, package docs, or any data-driven pages.
Basic Setup
Create a template file with parameter in brackets and a paths loader:
.
└─ packages/
├─ [pkg].md # Route template
└─ [pkg].paths.js # Paths loader
The paths loader exports a paths method returning route parameters:
// packages/[pkg].paths.js
export default {
paths() {
return [{ params: { pkg: 'foo' } }, { params: { pkg: 'bar' } }, { params: { pkg: 'baz' } }];
},
};
Generated pages:
/packages/foo.html/packages/bar.html/packages/baz.html
Multiple Parameters
.
└─ packages/
├─ [pkg]-[version].md
└─ [pkg]-[version].paths.js
// packages/[pkg]-[version].paths.js
export default {
paths() {
return [
{ params: { pkg: 'foo', version: '1.0.0' } },
{ params: { pkg: 'foo', version: '2.0.0' } },
{ params: { pkg: 'bar', version: '1.0.0' } },
];
},
};
Dynamic Path Generation
From local files:
// packages/[pkg].paths.js
import fs from 'node:fs';
export default {
paths() {
return fs.readdirSync('packages').map((pkg) => ({
params: { pkg },
}));
},
};
From remote API:
// packages/[pkg].paths.js
export default {
async paths() {
const packages = await fetch('https://api.example.com/packages').then((r) => r.json());
return packages.map((pkg) => ({
params: {
pkg: pkg.name,
version: pkg.version,
},
}));
},
};
Accessing Params in Page
Template globals:
<!-- packages/[pkg].md -->
# Package: {{ $params.pkg }}
Version: {{ $params.version }}
In script:
<script setup>
import { useData } from 'vitepress';
const { params } = useData();
</script>
<template>
<h1>{{ params.pkg }}</h1>
</template>
Passing Content
For heavy content (raw markdown/HTML from CMS), use content instead of params to avoid bloating the client bundle:
// posts/[slug].paths.js
export default {
async paths() {
const posts = await fetch('https://cms.example.com/posts').then((r) => r.json());
return posts.map((post) => ({
params: { slug: post.slug },
content: post.content, // Raw markdown or HTML
}));
},
};
Render content in template:
## <!-- posts/[slug].md -->
## title: {{ $params.title }}
<!-- @content -->
The <!-- @content --> placeholder is replaced with the content from the paths loader.
Watch Option
Auto-rebuild when template or data files change:
// posts/[slug].paths.js
export default {
watch: ['./templates/**/*.njk', '../data/**/*.json'],
paths(watchedFiles) {
const dataFiles = watchedFiles.filter((f) => f.endsWith('.json'));
return dataFiles.map((file) => {
const data = JSON.parse(fs.readFileSync(file, 'utf-8'));
return {
params: { slug: data.slug },
content: renderTemplate(data),
};
});
},
};
Complete Example: Blog
// posts/[slug].paths.js
import fs from 'node:fs';
import matter from 'gray-matter';
export default {
watch: ['./posts/*.md'],
paths(files) {
return files
.filter((f) => !f.includes('[slug]'))
.map((file) => {
const content = fs.readFileSync(file, 'utf-8');
const { data, content: body } = matter(content);
const slug = file.match(/([^/]+)\.md$/)[1];
return {
params: {
slug,
title: data.title,
date: data.date,
},
content: body,
};
});
},
};
## <!-- posts/[slug].md -->
## layout: doc
# {{ $params.title }}
<time>{{ $params.date }}</time>
<!-- @content -->
Key Points
- Template file uses
[param]syntax in filename - Paths loader file must be named
[param].paths.jsor.ts paths()returns array of{ params: {...}, content?: string }- Use
$paramsin templates oruseData().paramsin scripts - Use
contentfor heavy data to avoid client bundle bloat watchenables HMR for template/data file changes