Overview
This guide walks you through adding dioxus-docs-kit to an existing Dioxus 0.7 project. By the end, you'll have a fully functional documentation site with sidebar navigation, search, and optional OpenAPI reference pages.
Prerequisites
- A working Dioxus 0.7 project with
features = ["router", "fullstack"] - The Dioxus CLI (
dx):curl -sSL http://dioxus.dev/install.sh | sh
Setup
Add dependencies
Add dioxus-docs-kit and the build helper to your Cargo.toml:
[dependencies]
dioxus = { version = "0.7", features = ["router", "fullstack"] }
dioxus-docs-kit = "0.7"
[build-dependencies]
dioxus-docs-kit-build = "0.7"
[features]
default = ["web"]
web = ["dioxus/web", "dioxus-docs-kit/web"]
server = ["dioxus/server", "dioxus-docs-kit/server"]The kit's defaults bring mermaid and openapi. Syntax highlighting is always on and needs no feature — see the crate README's Syntax Highlighting section. To ship less, set default-features = false and list the features you need.
Create the docs directory
Create your content structure:
docs/
├── _nav.json
├── getting-started/
│ └── introduction.mdx
└── api-reference/
└── overview.mdxEach .mdx file needs frontmatter:
---
title: Introduction
description: Welcome to the docs
sidebarTitle: Introduction
icon: book-open
---
Your content here.Configure navigation
Create docs/_nav.json:
{
"tabs": ["Docs", "API Reference"],
"groups": [
{
"group": "Getting Started",
"tab": "Docs",
"pages": ["getting-started/introduction"]
},
{
"group": "API Reference",
"tab": "API Reference",
"pages": ["api-reference/overview"]
}
]
}Create build.rs
Add a build.rs at the project root to parse all content at compile time:
fn main() {
dioxus_docs_kit_build::DocsBuild::new("docs/_nav.json").generate();
}This reads _nav.json, emits cargo:rerun-if-changed for all MDX files, parses every page (rendering its prose to HTML), builds the search index, and writes docs_bundle.json to OUT_DIR.
To include an OpenAPI spec, chain .with_openapi() with the spec's path — it is parsed here, not in the browser:
dioxus_docs_kit_build::DocsBuild::new("docs/_nav.json")
.with_openapi("api-reference", "docs/api-reference/openapi.yaml")
.generate();Wire up the registry
In main.rs, load the bundle with the docs_bundle!() macro and create the DocsRegistry:
use dioxus::prelude::*;
use dioxus_docs_kit::{
DocsConfig, DocsContext, DocsLayout, DocsPageContent,
DocsRegistry, use_docs_providers,
};
use std::sync::LazyLock;
static DOCS: LazyLock<DocsRegistry> = LazyLock::new(|| {
DocsConfig::new(dioxus_docs_kit::docs_bundle!())
.with_default_path("getting-started/introduction")
.build()
});Keep dioxus-docs-kit and dioxus-docs-kit-build on the same version. The bundle carries a format version and the registry refuses one it does not understand.
Create the docs layout wrapper
Create a layout component that provides DocsContext and the registry. The use_docs_providers hook bundles all the context setup into a single call:
#[component]
fn MyDocsLayout() -> Element {
let nav = use_navigator();
let route = use_route::<Route>();
let current_path = use_memo(move || match route.clone() {
Route::DocsPage { slug } => slug.join("/"),
_ => String::new(),
});
// The constructor defaults the meta fields (`auto_meta: true`,
// no site_url, no markdown alternate links); use the `with_*`
// setters to override them.
let docs_ctx = DocsContext::new(
current_path,
"/docs",
Callback::new(move |path: String| {
let slug: Vec<String> =
path.split('/').map(String::from).collect();
nav.push(Route::DocsPage { slug });
}),
)
// Public origin used for canonical URLs and OG tags — set it
// in production.
.with_site_url("https://your-site.com");
let providers = use_docs_providers(&*DOCS, docs_ctx);
rsx! {
DocsLayout {
// Use providers.search_open / providers.drawer_open
// in a custom header if needed
Outlet::<Route> {}
}
}
}The DocsProviders struct returned by the hook gives you access to search_open and drawer_open signals for wiring up custom header buttons.
Add routes
Add the docs routes to your Route enum:
#[derive(Debug, Clone, Routable, PartialEq)]
enum Route {
#[layout(MyDocsLayout)]
#[redirect("/docs", || Route::DocsPage {
slug: vec!["getting-started".into(), "introduction".into()]
})]
#[route("/docs/:..slug")]
DocsPage { slug: Vec<String> },
}
#[component]
fn DocsPage(slug: Vec<String>) -> Element {
rsx! {
DocsPageContent { path: slug.join("/") }
}
}Build for production
Always bundle with debug symbols off:
dx bundle --web --release --debug-symbols falsedx keeps DWARF by default, wasm-opt aborts on it, and dx then silently ships the unoptimized wasm — this site was 25 MB instead of 7 MB until that flag was added. Add brotli sidecars (.br next to each .wasm/.js/.css in the bundle) and dioxus-server serves them as content-encoding: br automatically; the wasm then costs about 1.4 MB on the wire.
What You Get
Once integrated, your docs site includes:
Sidebar Navigation
Auto-generated from _nav.json with tab switching and group headings
Full-Text Search
Built-in search modal (Cmd+K) across all doc content
API Reference
Two-column endpoint pages generated from OpenAPI specs
Page Navigation
Previous/next page links at the bottom of every page
Next Steps
Customization
Learn how to theme your site and configure navigation
Basic Usage
Explore all available MDX components
Page metadata and not-found responses
With auto_meta enabled (the default), documentation and blog metadata updates during client navigation. The kit removes its previous canonical URL, description, social tags, Markdown alternate, and structured data when leaving a page. Consumer-supplied head tags are left alone. Use with_auto_meta(false) if your app manages page metadata itself.
Missing documentation paths, API operations, blog posts, and category pages render a not-found view with noindex. With the kit's server feature enabled, direct requests also return HTTP 404. The not-found title and noindex remain enabled when automatic metadata is off. Navigating to an existing page removes the not-found metadata.
Client-only hosting controls HTTP status codes separately; enable server rendering to use the kit's HTTP 404 handling.