Customization
Customization
Configuration
Configure ReallySimpleDocs in astro.config.mjs:
import { defineConfig } from "astro/config";
import reallySimpleDocs from "reallysimpledocs/astro";
export default defineConfig({
integrations: [
reallySimpleDocs({
docsDir: "./docs",
routeBase: "/docs",
customCss: ["./src/docs.css"],
site: {
title: "Acme Docs",
subtitle: "v1.0.0",
description: "Documentation for Acme.",
url: "https://docs.example.com",
},
}),
],
});Site metadata
site controls the default sidebar header, document titles, and metadata:
reallySimpleDocs({
site: {
title: "Acme Docs",
subtitle: "v1.0.0",
description: "Documentation for Acme.",
url: "https://docs.example.com",
favicon: "favicon.svg",
appleTouchIcon: "apple-touch-icon.png",
socialImage: "social.png",
logo: {
url: "/assets/favicon.svg",
},
},
});Relative asset values resolve from assetsBase, which defaults to /assets. Favicon and social image tags are emitted only when configured.
Sitemap
Use Astro’s sitemap integration when you need a sitemap. ReallySimpleDocs does not generate its own sitemap.
npm install @astrojs/sitemap
import sitemap from "@astrojs/sitemap";
import { defineConfig } from "astro/config";
import reallySimpleDocs from "reallysimpledocs/astro";
export default defineConfig({
site: "https://docs.example.com",
integrations: [reallySimpleDocs(), sitemap()],
});The integration writes sitemap-index.xml and sitemap chunk files during astro build. It is not served by astro dev.
Robots
Add robots.txt as a static file when you need one:
User-agent: *
Allow: /
Sitemap: https://docs.example.com/sitemap-index.xmlComponent overrides
Override layout regions when the default docs shell is not enough:
reallySimpleDocs({
components: {
Head: "./src/docs/Head.astro",
SidebarHeader: "./src/components/SidebarHeader.astro",
SidebarFooter: "./src/components/SidebarFooter.astro",
ContentHeader: "./src/components/ContentHeader.astro",
},
});Head
Use Head to add scripts, styles, preload tags, or site-specific metadata to the document <head>.
The default RSD head scripts and metadata still render.
<script src="/assets/docs.js" defer></script>Head receives config, site, title, description, pagePath, metaTitle, and absoluteUrl.
SidebarHeader
Use SidebarHeader when the default logo/title block is not enough.
---
const { site } = Astro.props;
---
<a href="/" class="btn" data-variant="ghost">
{site.title}
</a>SidebarFooter
Use SidebarFooter for persistent sidebar actions or secondary links.
<div class="p-2 text-xs text-muted-foreground">
v1.0.0
</div>ContentHeader
Use ContentHeader for controls between search and the built-in theme toggle.
Header actions, such as a GitHub link, belong in this component rather than in site metadata.
<nav class="flex items-center gap-2">
<a class="btn" data-variant="outline" data-size="sm" href="https://github.com/acme/project">
GitHub
</a>
</nav>ContentHeader receives site and page.
Custom landing pages, marketing pages, blogs, and app pages should stay as normal Astro routes outside the ReallySimpleDocs docs route.
CSS and Basecoat
ReallySimpleDocs is built on Basecoat. Pick a Basecoat style with style, then add project-specific CSS with customCss:
reallySimpleDocs({
style: "nova",
customCss: ["./src/docs.css"],
});:root {
--primary: oklch(54.6% 0.245 262.881);
}
.dark {
--primary: oklch(70.7% 0.165 254.624);
}By default, ReallySimpleDocs manages one Tailwind/Basecoat stylesheet for the app. It scans:
src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}docs/**/*.{md,mdx}- ReallySimpleDocs runtime components
That means custom Astro pages outside the docs route can use Tailwind and Basecoat classes without a separate stylesheet.
Managed CSS
Disable managed CSS only when you want to own the full stylesheet pipeline:
reallySimpleDocs({
css: false,
});With managed CSS enabled, ReallySimpleDocs inserts:
- Tailwind
- the selected Basecoat style
- RSD layout/component CSS
- each
customCssfile
Use css: false when you bring your own Tailwind/Basecoat/RSD stylesheet.
When managed CSS is disabled, import RSD’s own docs CSS from the public package entry:
@import "reallysimpledocs/css";
@import "reallysimpledocs/css/styles/vega";Use the reallysimpledocs/css/styles/{style} import that matches your Basecoat style.
ReallySimpleDocs always inserts its managed JavaScript: theme initialization, Basecoat JavaScript, copy-code behavior, and command search behavior.
When you disable managed CSS, use these source files as references for what you may need to reproduce:
- DefaultHead.astro: theme initialization, Basecoat JavaScript, and copy-code behavior.
- ThemeToggle.astro: built-in dark-mode button.
- CommandDialog.astro: search dialog behavior.
- Sidebar.astro: sidebar shell and toggle target.
- reallysimpledocs/css: shared RSD-specific CSS layered on top of Basecoat.
- reallysimpledocs/css/styles/*: style-specific RSD CSS for code and tabbed surfaces.
Prefer style and customCss for normal styling changes, and Head or ContentHeader for additive UI. Use css: false only when you are replacing RSD’s stylesheet pipeline.
Public files
Put docs media in public/media/ and reference it with absolute paths:
Put favicon and social assets in public/assets/ when using the default assetsBase.
Search
Search uses a generated Lunr index. ReallySimpleDocs indexes normalized Markdown, including fallback Markdown for known MDX components, while keeping code examples separate so prose matches stay readable.
Render the exported CommandDialog on any Astro page when you want docs search outside the ReallySimpleDocs route:
---
import { CommandDialog } from "reallysimpledocs/components";
---
<CommandDialog />
<button type="button" class="btn" onclick="window.rsdOpenCommandSearch?.()">
Search docs
</button>CommandDialog uses the generated docs search index by default. Pass indexUrl only when you need to point at a different index.
<CommandDialog indexUrl="/docs/search-index.json" />The dialog registers window.rsdOpenCommandSearch() and the ⌘K / Ctrl+K shortcut on pages where it is rendered.
htmx
Install htmx first:
npm install htmx.org
Use bodyAttrs to boost links into the docs content container.
Because the sidebar is outside #content, update its current-page state from Head after htmx swaps:
reallySimpleDocs({
bodyAttrs: {
"hx-boost": "true",
"hx-target": "#content",
"hx-select": "#content",
"hx-swap": "outerHTML",
"hx-push-url": "true",
},
components: {
Head: "./src/docs/Head.astro",
},
});---
import htmxUrl from "htmx.org/dist/htmx.min.js?url";
---
<script is:inline>
(() => {
const key = (href) => {
const url = new URL(href, window.location.origin);
return `${url.pathname.replace(/\/$/, "")}${url.search}`;
};
const syncSidebar = () => {
for (const link of document.querySelectorAll("#sidebar a[href]")) {
const isCurrent = key(link.href) === key(window.location.href);
if (isCurrent) {
link.setAttribute("aria-current", "page");
link.closest("details")?.setAttribute("open", "");
} else {
link.removeAttribute("aria-current");
}
}
};
document.addEventListener("DOMContentLoaded", syncSidebar);
document.addEventListener("htmx:afterSettle", () => {
window.basecoat?.initAll?.();
syncSidebar();
});
window.addEventListener("popstate", syncSidebar);
})();
</script>
<script src={htmxUrl} defer></script>Using a CDN is also fine; see the htmx installation docs for options.