--- name: vitepress-data-loading description: Build-time data loaders for fetching remote data or processing local files --- # Data Loading VitePress data loaders run at build time to load arbitrary data that's serialized as JSON in the client bundle. ## Basic Usage Create a file ending with `.data.js` or `.data.ts`: ```ts // example.data.ts export default { load() { return { hello: 'world', timestamp: Date.now() } } } ``` Import the `data` named export: ```vue ``` ## Async Data Fetch remote data: ```ts // api.data.ts export default { async load() { const response = await fetch('https://api.example.com/data') return response.json() } } ``` ## Local Files with Watch Process local files with hot reload: ```ts // posts.data.ts import fs from 'node:fs' import { parse } from 'csv-parse/sync' export default { watch: ['./data/*.csv'], load(watchedFiles) { // watchedFiles = array of absolute paths return watchedFiles.map(file => { return parse(fs.readFileSync(file, 'utf-8'), { columns: true, skip_empty_lines: true }) }) } } ``` ## createContentLoader Helper for loading markdown content (common for blogs/archives): ```ts // posts.data.ts import { createContentLoader } from 'vitepress' export default createContentLoader('posts/*.md') ``` Returns array of `ContentData`: ```ts interface ContentData { url: string // e.g. /posts/hello.html frontmatter: Record src?: string // raw markdown (opt-in) html?: string // rendered HTML (opt-in) excerpt?: string // excerpt HTML (opt-in) } ``` With options: ```ts // posts.data.ts import { createContentLoader } from 'vitepress' export default createContentLoader('posts/*.md', { includeSrc: true, // Include raw markdown render: true, // Include rendered HTML excerpt: true, // Include excerpt (content before first ---) transform(rawData) { // Sort by date, newest first return rawData .sort((a, b) => +new Date(b.frontmatter.date) - +new Date(a.frontmatter.date)) .map(page => ({ title: page.frontmatter.title, url: page.url, date: page.frontmatter.date, excerpt: page.excerpt })) } }) ``` ## Usage Example: Blog Index ```ts // posts.data.ts import { createContentLoader } from 'vitepress' export default createContentLoader('posts/*.md', { excerpt: true, transform(data) { return data .filter(post => !post.frontmatter.draft) .sort((a, b) => +new Date(b.frontmatter.date) - +new Date(a.frontmatter.date)) } }) ``` ```vue ``` ## Typed Data Loaders ```ts // example.data.ts import { defineLoader } from 'vitepress' export interface Data { posts: Array<{ title: string; url: string }> } declare const data: Data export { data } export default defineLoader({ watch: ['./posts/*.md'], async load(): Promise { // ... return { posts: [] } } }) ``` ## In Build Hooks Use in config for generating additional files: ```ts // .vitepress/config.ts import { createContentLoader } from 'vitepress' export default { async buildEnd() { const posts = await createContentLoader('posts/*.md').load() // Generate RSS feed, sitemap, etc. } } ``` ## Accessing Config ```ts // example.data.ts import type { SiteConfig } from 'vitepress' export default { load() { const config: SiteConfig = (globalThis as any).VITEPRESS_CONFIG return { base: config.site.base } } } ``` ## Key Points - Data loaders run only at build time in Node.js - File must end with `.data.js` or `.data.ts` - Import the `data` named export (not default) - Use `watch` for local file hot reload during dev - `createContentLoader` simplifies loading markdown collections - Keep data small - it's inlined in the client bundle - Heavy data should use `transform` to reduce payload