Files
agent-skills/skills/vitepress/references/features-data-loading.md
Jason Woltje f5792c40be feat: Complete fleet — 94 skills across 10+ domains
Pulled ALL skills from 15 source repositories:
- anthropics/skills: 16 (docs, design, MCP, testing)
- obra/superpowers: 14 (TDD, debugging, agents, planning)
- coreyhaines31/marketingskills: 25 (marketing, CRO, SEO, growth)
- better-auth/skills: 5 (auth patterns)
- vercel-labs/agent-skills: 5 (React, design, Vercel)
- antfu/skills: 16 (Vue, Vite, Vitest, pnpm, Turborepo)
- Plus 13 individual skills from various repos

Mosaic Stack is not limited to coding — the Orchestrator and
subagents serve coding, business, design, marketing, writing,
logistics, analysis, and more.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 16:27:42 -06:00

4.3 KiB

name, description
name description
vitepress-data-loading 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:

// example.data.ts
export default {
  load() {
    return {
      hello: 'world',
      timestamp: Date.now()
    }
  }
}

Import the data named export:

<script setup>
import { data } from './example.data.ts'
</script>

<template>
  <pre>{{ data }}</pre>
</template>

Async Data

Fetch remote data:

// 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:

// 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):

// posts.data.ts
import { createContentLoader } from 'vitepress'

export default createContentLoader('posts/*.md')

Returns array of ContentData:

interface ContentData {
  url: string                    // e.g. /posts/hello.html
  frontmatter: Record<string, any>
  src?: string                   // raw markdown (opt-in)
  html?: string                  // rendered HTML (opt-in)
  excerpt?: string               // excerpt HTML (opt-in)
}

With options:

// 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

// 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))
  }
})
<!-- posts/index.md -->
<script setup>
import { data as posts } from './posts.data.ts'
</script>

<template>
  <ul>
    <li v-for="post in posts" :key="post.url">
      <a :href="post.url">{{ post.frontmatter.title }}</a>
      <span>{{ post.frontmatter.date }}</span>
    </li>
  </ul>
</template>

Typed Data Loaders

// 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<Data> {
    // ...
    return { posts: [] }
  }
})

In Build Hooks

Use in config for generating additional files:

// .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

// 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