Blog Integration
Add a compile-time blog with post listing, tag filtering, search, and reading time to your Dioxus project
Overview
dioxus-docs-kit includes a full blog engine that embeds all posts at compile time. You get post listing with pagination, tag filtering, full-text search, reading time estimates, author metadata, and MDX rendering — all from a single binary with zero runtime file I/O.
Content Structure
Create a blog/ directory at your project root with a _blog.json manifest and .mdx post files:
blog/
├── _blog.json
├── hello-world.mdx
├── building-with-dioxus.mdx
└── rust-web-future.mdxThe _blog.json Manifest
The manifest defines authors and lists all post slugs (filenames without .mdx):
{
"authors": {
"hauke": {
"name": "Hauke Jung",
"bio": "Rust developer and Dioxus enthusiast",
"url": "https://github.com/hauju"
}
},
"posts": [
"hello-world",
"building-with-dioxus",
"rust-web-future"
]
}Author fields
| Field | Required | Description |
|---|---|---|
name |
Yes | Display name |
avatar |
No | URL or asset path for the author's avatar image |
bio |
No | Short biography |
url |
No | Link to the author's website or profile |
Post Frontmatter
Each .mdx file needs YAML frontmatter at the top:
---
title: "Hello World"
description: "Welcome to our blog!"
date: "2026-03-20"
author: "hauke"
tags: ["announcement", "dioxus"]
coverImage: "/assets/cover.png"
draft: false
---
Your post content in MDX format...Frontmatter fields
| Field | Required | Description |
|---|---|---|
title |
Yes | Post title |
description |
No | Short summary shown in post cards |
date |
Yes | ISO 8601 date string (e.g. "2026-03-20") — used for sorting |
author |
Yes | Author ID matching a key in _blog.json authors |
tags |
No | Array of tag strings for filtering |
coverImage |
No | Cover image path |
draft |
No | Set to true to hide from listing (default: false) |
Setup
Add the build script
In your build.rs, add the blog content map generation alongside your docs generation:
fn main() {
dioxus_docs_kit_build::generate_content_map("docs/_nav.json");
dioxus_docs_kit_build::generate_blog_content_map("blog/_blog.json");
}The blog directory is inferred from the manifest path parent (e.g. "blog/_blog.json" reads from blog/).
Create the blog registry
In main.rs, use the blog_content_map!() macro and build a BlogRegistry:
use dioxus_docs_kit::{
BlogConfig, BlogContext, BlogLayout, BlogList, BlogPostView,
BlogRegistry, BlogSearchButton, use_blog_providers,
};
use std::sync::LazyLock;
dioxus_docs_kit::blog_content_map!();
static BLOG: LazyLock<BlogRegistry> = LazyLock::new(|| {
BlogConfig::new(
include_str!("../blog/_blog.json"),
blog_content_map(),
)
.with_posts_per_page(9)
.with_theme_toggle("light", "dark", "dark")
.build()
});Add routes
Add blog routes to your Route enum. The blog needs two routes: an index (list) page and a single post page:
#[derive(Debug, Clone, Routable, PartialEq)]
enum Route {
#[layout(MyBlogLayout)]
#[route("/blog")]
BlogIndex {},
#[route("/blog/:slug")]
BlogPage { slug: String },
}Create the blog layout wrapper
The blog layout wrapper provides BlogContext and wires up the registry, just like the docs layout does for documentation:
#[component]
fn MyBlogLayout() -> Element {
let nav = use_navigator();
let route = use_route::<Route>();
let current_slug = use_memo(move || match route.clone() {
Route::BlogPage { slug } => slug,
_ => String::new(),
});
let blog_ctx = BlogContext {
current_slug: current_slug.into(),
base_path: "/blog".into(),
navigate: Callback::new(move |slug: String| {
if slug.is_empty() {
nav.push(Route::BlogIndex {});
} else {
nav.push(Route::BlogPage { slug });
}
}),
};
let providers = use_blog_providers(&BLOG, blog_ctx);
let search_open = providers.search_open;
let mut drawer_open = providers.drawer_open;
rsx! {
BlogLayout {
header: rsx! {
div { class: "navbar bg-base-200 border-b border-base-300 px-4",
div { class: "flex-1",
// Your site logo / nav links
}
div { class: "flex-none flex items-center gap-1",
BlogSearchButton { search_open }
}
}
},
Outlet::<Route> {}
}
}
}The BlogProviders struct gives you search_open, drawer_open, active_tag, and current_page signals to wire into your header.
Create the page components
Add the index and post view components:
#[component]
fn BlogIndex() -> Element {
rsx! {
BlogList {
hero: rsx! {
div { class: "text-center mb-12",
h1 { class: "text-4xl font-bold tracking-tight mb-3",
"Blog"
}
p { class: "text-lg text-base-content/60",
"Thoughts on Rust, Dioxus, and web development."
}
}
}
}
}
}
#[component]
fn BlogPage(slug: String) -> Element {
rsx! {
BlogPostView { slug }
}
}The BlogList component accepts an optional hero prop for custom content above the post grid. It includes built-in tag filtering and pagination.
Configuration Options
The BlogConfig builder supports these options:
| Method | Default | Description |
|---|---|---|
.with_posts_per_page(n) |
9 |
Number of posts per page |
.with_date_format(fmt) |
"%B %d, %Y" |
Date display format (%Y, %m, %d, %B for month name) |
.with_theme(name) |
— | Set a fixed theme (no toggle) |
.with_theme_toggle(light, dark, default) |
— | Enable light/dark theme switching |
Available Components
BlogLayout
Main layout shell with header slot, search modal, and mobile drawer
BlogList
Post grid with tag filtering, pagination, and optional hero section
BlogPostView
Full post renderer with author info, reading time, tags, and prev/next navigation
BlogSearchModal
Full-text search across all blog posts
BlogSearchButton
Button that opens the search modal
TagFilter
Tag filter bar (included in BlogList, also usable standalone)
AuthorInfo
Author avatar, name, and bio display
ReadingTimeBadge
Estimated reading time badge
BlogPostNav
Previous/next post navigation links
BlogThemeToggle
Light/dark theme toggle for the blog layout
RSS Feed Generation
The BlogRegistry can generate an RSS feed:
let rss_xml = BLOG.generate_rss(
"My Blog", // site title
"https://example.com", // site URL
"/blog", // blog base path
);You can serve this from a server function at /blog/rss.xml.
LLMs.txt Generation
Similar to the docs registry, the blog supports llms.txt output:
let llms_txt = BLOG.generate_llms_txt(
"My Blog",
"A blog about Rust and Dioxus",
"https://example.com",
"/blog",
);