The litro:content virtual module provides a server-side Markdown content API. It is compatible with 11ty's data cascade and frontmatter conventions.
content/
docs/
.11tydata.json ← { "tags": ["docs"] }
getting-started.md
api-routes.md
blog/
.11tydata.json ← { "tags": ["blog"] }
first-post.md
_data/
metadata.js ← global data
import { getPosts, getPost, getTags, getGlobalData } from 'litro:content';
getPosts(filter?)Returns all posts, optionally filtered by tag or other criteria:
const posts = await getPosts();
const blogPosts = await getPosts({ tags: ['blog'] });
const docPages = await getPosts({ tags: ['docs'] });
getPost(url)Returns a single post by its URL:
const post = await getPost('/content/docs/getting-started');
getTags()Returns all unique tags across all posts.
getGlobalData()Returns the merged global data from _data/ files.
Each Markdown file can include YAML frontmatter:
---
title: My Post
description: A brief summary.
date: 2026-01-01
tags:
- blog
- announcement
---
# My Post
Content here...
interface Post {
title: string;
description?: string;
date?: string;
tags: string[];
url: string; // e.g. /content/docs/getting-started
body: string; // rendered HTML
rawBody: string; // raw Markdown
}
For SSG, export generateRoutes() from your page file to tell the SSG plugin which dynamic routes to prerender:
export async function generateRoutes(): Promise<string[]> {
const posts = await getPosts({ tags: ['docs'] });
return posts.map(p => '/docs' + p.url.slice('/content/docs'.length));
}