Configuration
Site title, navigation, theme colors, and search behavior — everything that customizes how the docs feel.
Most of the site's behavior is controlled by three files. Once you know what each one owns, customization is straightforward.
Where things live
Site title and navigation
Edit lib/layout.shared.ts — this is the single source of truth for the brand title, nav links, and footer.
export const baseOptions = {
nav: {
title: "nxt-whitepapers",
},
links: [
{ text: "GitHub", url: "https://github.com/your-org/nxt-whitepapers" },
],
};The nav.title shows in the top-left of both the docs sidebar and any home pages you add.
Theme colors
Fumadocs uses CSS variables for color — primary, background, foreground, muted, and so on. To match the brand, override them in app/globals.css:
:root {
--brand-blue-300: #93c5fd;
--brand-sky-500: #0ea5e9;
--gradient-primary: linear-gradient(
to bottom,
var(--brand-blue-300),
var(--brand-sky-500)
);
--color-fd-primary: var(--brand-sky-500);
}Why OKLCH?
OKLCH is perceptually uniform — same lightness value reads as the same brightness across hues. HSL fakes this and tends to produce muddier mid-tones. If you prefer HSL, both formats work; just be consistent across light and dark variants.
Sidebar order
The order of pages in the sidebar comes from meta.json files. Each folder can have one:
{
"pages": [
"index",
"ai-machine-learning",
"coding-tutorials",
"security",
"ui-ux",
"performances"
]
}"index"and"ai-machine-learning"are file/folder names (no extension)"---Label---"becomes a section divider with the given text
Pages not listed in meta.json are appended to the end in alphabetical order.
To hide a page, prefix the filename with _ — Fumadocs ignores those.
Search
Search is configured in app/api/search/route.ts. It uses Fumadocs' advanced mode, which indexes headings and body text:
import { source } from "@/lib/source";
import { createSearchAPI } from "fumadocs-core/search/server";
export const { GET } = createSearchAPI("advanced", {
indexes: source.getPages().map((page) => ({
title: page.data.title,
structuredData: page.data.structuredData,
id: page.url,
url: page.url,
})),
});Switch to "simple" if you only want to match titles — faster, less useful.
Feedback collector
The thumbs-up/thumbs-down widget at the bottom of every page is wired in app/docs/[[...slug]]/page.tsx. Right now it console.logs the response; replace the server action body with a fetch to your analytics endpoint.
{async (feedback) => {
"use server";
console.log("[feedback]", feedback);
return {};
}}{async (feedback) => {
"use server";
await posthog.capture("docs_feedback", feedback);
return {};
}}{async (feedback) => {
"use server";
await fetch("https://api.example.com/feedback", {
method: "POST",
body: JSON.stringify(feedback),
});
return {};
}}How is this guide?