Blog
Add a blog with post, index and tag pages.
Turn a content source into a blog. Each post gets a page, like /blog/hello-world, with an index at /blog and tag pages at /blog/tags.
Setup
Install
npm i @fumapress/blogAdd the Styles
Add this after the imports in your stylesheet:
@import "@fumapress/blog/css/preset.css";Add the Collection
Add a collection for your posts, with the blog schema, and pass its key to the plugin:
import { defineConfig } from "astro/config";
import press from "@fumapress/astro";
export default defineConfig({
integrations: [
press({
collections: {
docs: { dir: "content/docs" },
blog: { dir: "content/blog" },
},
}),
],
});Writing Posts
---
title: My Blog Post
description: Hello World!!!
date: 2026-09-06
tags: [my-tag]
authors: [fuma]
image: /covers/hello.png
---
## Introduction
Welcome to my first blog post.Posts are ordered by date. Each tag gets a page, and image shows on post cards.
Authors
The keys of authors are the IDs in the frontmatter. An ID that isn't registered shows as the plain ID.
blogPlugin({
source: "blog",
authors: {
fuma: {
name: "Fuma Nama",
title: "Maintainer",
url: "https://fuma-nama.dev",
image: "https://github.com/fuma-nama.png",
},
},
});Prop
Type
Customization
Other Content Sources
Posts can come from any content source, like Notion. The date comes from the getPageCreatedAt hook of its plugin. These options read the other fields when they aren't in the frontmatter:
blogPlugin({
source: "blog",
getTags: (page) => page.data.tags,
getAuthors: (page) => page.data.authors,
getImage: (page) => page.data.image,
});To use multiple sources as posts, pass their keys:
blogPlugin({
source: ["blog", "news"],
getTags: (page) => page.data.tags ?? [page.type],
});URLs
Set a path to move a page, or false to remove it:
blogPlugin({
source: "blog",
paths: {
index: "/news",
tags: false,
},
});Custom UI
In renderPage, a post has the type of its source key, like blog. The other pages are blog.index, blog.tags and blog.tag. This example replaces the index, and renders the built-in post in another layout:
import { BlogPostView } from "@fumapress/blog";
import { HomeLayout } from "fumadocs-ui/layouts/home";
renderPage(props) {
const { renderDefault } = props;
// the Home Layout takes these props, the others are for docs layouts
const { nav, links, githubUrl, i18n, themeSwitch, searchToggle } = props.layoutProps;
const home = { nav, links, githubUrl, i18n, themeSwitch, searchToggle };
switch (props.type) {
case "blog.index":
return (
<HomeLayout {...home}>
<ul>
{props.posts.map((post) => (
<li key={post.page.url}>{post.page.data.title}</li>
))}
</ul>
</HomeLayout>
);
case "blog":
return (
<HomeLayout {...home}>
<BlogPostView post={props.post} newer={props.newer} older={props.older}>
{props.body}
</BlogPostView>
</HomeLayout>
);
default:
return renderDefault();
}
},@fumapress/blog also exports BlogIndexView, BlogTagsView and BlogTagView.
Listing Posts
getBlogPosts() returns all posts, newest first:
---
import HomeLayout from "@fumapress/astro/components/HomeLayout.astro";
import { getPressContext } from "@fumapress/astro/runtime";
import { getBlogPosts } from "@fumapress/blog";
import type { app } from "../../press.config";
const ctx = getPressContext<typeof app>();
const posts = await getBlogPosts(ctx);
---
<HomeLayout title="Acme">
<ul>
{posts.slice(0, 3).map((post) => <li><a href={ctx.withBase(post.page.url)}>{post.page.data.title}</a></li>)}
</ul>
</HomeLayout>With internationalization, pass the language: getBlogPosts(ctx, { lang }).
Options
Prop
Type
Last updated on