# dioxus-docs-kit
Reusable documentation site kit for [Dioxus](https://dioxuslabs.com/) applications.
Drop-in layout with sidebar navigation, full-text search, page navigation, OpenAPI API reference pages, mobile drawer, and theme toggle. Built on [dioxus-mdx](https://crates.io/crates/dioxus-mdx) for content rendering.
## Quick Start
Add to your `Cargo.toml`:
```toml
[dependencies]
dioxus-docs-kit = "0.5"
```
### 1. Build a Registry
The `DocsRegistry` holds all parsed content, the navigation tree, the search index, and any OpenAPI specs:
```rust
use dioxus_docs_kit::{DocsConfig, DocsRegistry};
use std::sync::LazyLock;
include_str!("../docs/_nav.json"),
doc_content_map(), // HashMap<&'static str, &'static str> generated by build.rs
)
.with_openapi("api-reference", include_str!("../docs/api-reference/spec.yaml"))
.build()
});
```
### 2. Wire Into Your Router
Create a thin layout wrapper that provides the `DocsContext` and registry to the library components. The `use_docs_providers` hook bundles all the context setup into one call:
```rust
use dioxus::prelude::*;
use dioxus_docs_kit::{DocsLayout, use_docs_context, use_docs_providers};
#[component]
fn MyDocsLayout() -> Element {
let nav = use_navigator();
let route = use_route::<Route>();
// A plain String — extract the slug from your route, e.g. slug.join("/").
let current_path = /* ... */;
// `use_docs_context` rewraps the path reactively for you. Building the
// signal yourself with a plain `use_memo(move || ...)` captures the first
// route and never updates — the sidebar highlight would freeze.
let docs_ctx = use_docs_context(
current_path,
"/docs",
Callback::new(move |path: String| {
nav.push(/* build route from path */);
}),
)
.with_site_url("https://your-site.com"); // optional: canonical/OG URLs
let providers = use_docs_providers(&DOCS, docs_ctx);
// providers.search_open / providers.drawer_open are available for
// wiring a custom header.
rsx! {
DocsLayout {
Outlet::<Route> {}
}
}
}
```
### 3. Add Routes
```rust
#[derive(Routable, Clone, PartialEq)]
enum Route {
#[layout(MyDocsLayout)]
#[route("/docs")]
DocsIndex {},
#[route("/docs/:..slug")]
DocsPage { slug: Vec<String> },
}
```
That's it. The library handles sidebar rendering, search, page content, previous/next navigation, and mobile responsiveness.
## Components
| `DocsLayout` | Full page layout with sidebar, content area, and table of contents |
| `DocsSidebar` | Navigation sidebar built from `_nav.json` |
| `DocsPageContent` | Renders MDX docs or OpenAPI endpoint pages |
| `DocsPageNav` | Previous/next page navigation |
| `SearchModal` | Full-text search across all docs |
| `SearchButton` | Trigger button for the search modal |
| `MobileDrawer` | Mobile navigation drawer |
| `ThemeToggle` | Light/dark theme switcher |
## Navigation Config
Define your docs structure in `_nav.json`. `tabs` is a flat list of tab names, and each group optionally names the tab it belongs to:
```json
{
"tabs": ["Docs", "Guides"],
"groups": [
{
"group": "Getting Started",
"tab": "Docs",
"pages": [
"getting-started/introduction",
"getting-started/quickstart"
]
}
]
}
```
## Content Pipeline
All doc content is embedded at compile time via `include_str!()`. A typical `build.rs` reads `_nav.json`, collects all referenced `.mdx` files, and generates a `HashMap<&'static str, &'static str>` mapping paths to content.
See the [example project](https://github.com/hauju/dioxus-docs-kit) for a complete `build.rs` implementation.
## Styling Setup
### Zero-setup: the precompiled stylesheet
The crate ships a compiled stylesheet (`DOCS_KIT_CSS`) covering every class its
components emit — Tailwind utilities, DaisyUI dark/light themes, typography
prose, and the `--dk-*` theming tokens. Link it and skip the rest of this
section — no Tailwind, no Bun, no safelist:
```rust
rsx! {
document::Stylesheet { href: dioxus_docs_kit::DOCS_KIT_CSS }
}
```
It only contains the *kit's* classes; if your own pages use Tailwind utilities
the kit doesn't, run your own build instead. That path requires **Tailwind CSS
4**, **DaisyUI 5**, and **@tailwindcss/typography**:
### Install dependencies
```sh
bun add tailwindcss @tailwindcss/typography daisyui
```
### Configure Tailwind
Add to your `tailwind.css`:
```css
@import "tailwindcss";
@plugin "@tailwindcss/typography";
@plugin "daisyui" {
themes: dark --default, light;
}
@source "./src/**/*.{rs,html,css}";
```
### Include dioxus-docs-kit classes
When using as a **crates.io dependency**, Tailwind can't scan the crate source
(it lives in `~/.cargo` with machine-specific paths). Copy `safelist.html` from the
crate into your project root and add it as a source:
```css
@source "./safelist.html";
```
The safelist includes all classes from both `dioxus-docs-kit` and `dioxus-mdx`, including
dynamic runtime classes that Tailwind cannot detect from source scanning alone.
When using as a **workspace path dependency**, you can point directly at the source instead:
```css
@source "./crates/dioxus-docs-kit/src/**/*.rs";
@source "./crates/dioxus-mdx/src/**/*.rs";
```
## Theming
The kit exposes a public theming surface so consumers can restyle without
forking or fighting DaisyUI internals.
### Public CSS variables
Copy `theme.css` from the crate into your project and import it alongside
Tailwind:
```css
@import "tailwindcss";
@import "./theme.css";
```
All tokens have DaisyUI fallbacks, so DaisyUI users inherit the current theme;
non-DaisyUI users get a sensible neutral default. Override any of them:
```css
.my-site .dk-root {
--dk-accent: #f0a57c;
--dk-font-heading: 'Instrument Serif', serif;
--dk-radius-lg: 20px;
}
```
| `--dk-bg`, `--dk-bg-sub`, `--dk-bg-alt` | Surface colors |
| `--dk-fg`, `--dk-muted`, `--dk-dim` | Foreground / text colors |
| `--dk-border` | Border color |
| `--dk-accent`, `--dk-accent-fg`, `--dk-accent-soft` | Accent |
| `--dk-radius-sm`, `--dk-radius`, `--dk-radius-lg` | Corner radii |
| `--dk-font-body`, `--dk-font-heading`, `--dk-font-mono` | Typography |
| `--dk-article-width`, `--dk-sidebar-width`, `--dk-toc-width` | Layout widths |
### Stable `dk-*` classes
Structural nodes carry semver-stable `dk-*` class names. Target these in your
own CSS instead of DaisyUI or internal classes:
| `dk-root`, `dk-docs-root` | Outermost docs wrapper |
| `dk-header`, `dk-shell`, `dk-sidebar`, `dk-main`, `dk-toc` | Layout regions |
| `dk-tabs`, `dk-tab`, `dk-tab-active` | Tab bar |
| `dk-nav`, `dk-nav-group`, `dk-nav-group-title`, `dk-nav-item`, `dk-nav-item-active` | Sidebar navigation |
| `dk-article`, `dk-article-header`, `dk-article-title`, `dk-article-description`, `dk-article-body` | Article regions |
| `dk-pagination`, `dk-page-prev`, `dk-page-next` | Previous/next links |
| `dk-search-trigger`, `dk-search-dialog`, `dk-search-input`, `dk-search-results`, `dk-search-result` | Search |
| `dk-drawer` | Mobile drawer |
### Slots on `DocsLayout`
`DocsLayout` accepts optional element slots. Each slot is wrapped in a
`dk-*-slot` class for CSS hooks.
```rust
DocsLayout {
announcement_bar: Some(rsx!{ AnnouncementBar {} }),
sidebar_header: Some(rsx!{ MyProductSwitcher {} }),
sidebar_footer: Some(rsx!{ EditOnGitHub {} }),
footer: Some(rsx!{ SiteFooter {} }),
Outlet::<Route> {}
}
```
`DocsPageContent` additionally accepts an `article_footer` slot (rendered
below the article body, before pagination) — useful for "Was this helpful?"
widgets.
### Density variants
`DocsLayout` accepts a `variant` prop for two built-in density presets:
```rust
DocsLayout { variant: DocsVariant::Reference, Outlet::<Route> {} }
```
| `Prose` (default) | Wide margins, serif-friendly, long-form reading | `72ch` |
| `Reference` | Tighter column, smaller type, denser headings | `64ch` |
The variant is emitted as a class on `dk-root` (`dk-variant-prose` or
`dk-variant-reference`) so consumers can layer further tweaks.
### Pre-built theme examples
`examples/themes/` ships three drop-in visual identities built entirely on
top of `--dk-*` tokens:
- `warm-editorial.css` — amber accent, serif headings, cream surfaces
- `brutalist-light.css` — high contrast, zero-radius, monospace
- `default.css` — baseline (nothing overridden)
See [THEMING.md](./THEMING.md) for the full roadmap and proposals open for
community contribution.
## Features
- `web` (default) — enables web-specific features (propagated to `dioxus-mdx`)
- `mermaid` (default) — renders ` ```mermaid ` fences as diagrams
- `highlight` (default) — syntax-highlights code blocks via [`dioxus-code`](https://crates.io/crates/dioxus-code). Disable (`default-features = false`) to drop the dependency and its C-compiling tree-sitter grammars: no C toolchain (or wasm `stderr` shim) is needed and the binary is smaller, but code blocks render as plain (uncolored) text. Turning it off also removes the `dioxus-code` re-exports and `DocsConfig::with_code_theme[s]`.
- `server` — Axum route builders for crawler-facing endpoints
With the `server` feature, `SeoRouter` generates per-page raw-Markdown routes, `llms.txt` / `llms-full.txt`, sitemaps, blog RSS, and robots.txt as plain Axum routes (server functions would JSON-encode the bodies):
```rust
use dioxus_docs_kit::server::SeoRouter;
.with_docs(&DOCS, "/docs")
.into_router();
Ok(dioxus::server::router(App).merge(seo))
});
```
## Syntax Highlighting
Code blocks render through [`dioxus-code`](https://crates.io/crates/dioxus-code). The kit ships highlighting for a common set of languages out of the box:
`bash`, `c#`, `c++`, `css`, `dockerfile`, `html`, `javascript`, `json`, `markdown`, `python`, `rust`, `toml`, `tsx`, `typescript`, `yaml`
To add another language (e.g. Go, Zig, Kotlin), depend on `dioxus-code` directly with its `lang-*` feature flag — Cargo unifies it into the kit's transitive dependency, so no kit changes are needed:
```toml
[dependencies]
dioxus-docs-kit = "0.5"
dioxus-code = { version = "0.1", default-features = false, features = ["lang-go", "lang-zig"] }
```
For everything dioxus-code supports in one go:
```toml
dioxus-code = { version = "0.1", default-features = false, features = ["all-languages"] }
```
See the [dioxus-code feature list](https://github.com/DioxusLabs/dioxus-code/blob/main/Cargo.toml) for every available `lang-*` flag.
## License
MIT