Customizing Your Site
A site generated by Riebeckite is a normal HonoX application. You do not need to learn a Riebeckite-specific UI framework; pages, components, CSS, and interactive UI all change the same way as in plain HonoX / Hono JSX.
Riebeckite mainly handles:
- Reading content such as Markdown
- Passing content and plugin information to the site
- Connecting pages and UI that plugins provide to the site
The app/ directory that actually shapes the site is site-owned code.
Where to make changes
Start by finding the location that matches what you want to change.
| What you want to change | Main location |
|---|---|
| Pages and URLs | app/routes/ |
| 404 (page not found) | app/routes/_404.tsx |
| Header / Footer | app/components/ and app/routes/_renderer.tsx |
| Article page structure | app/components/article.tsx |
| Buttons and other UI | app/components/ |
| Interactive UI | app/islands/ |
| Color, spacing, type, and layout | app/style.css and local CSS |
| Header / Footer links | the navigation plugin in riebeckite.config.ts |
As a rule, editing files under app/ changes how the site looks and is
structured. The exception is app/.riebeckite/, which Riebeckite generates
automatically; it is refreshed on every build, so do not edit it directly.
Creating components
You can write ordinary Hono JSX components under app/components/.
// app/components/callout.tsx
export function Callout({ children }: { children?: unknown }) {
return <aside class="callout">{children}</aside>;
}
Import the component from a route or another component as usual.
Riebeckite UI primitives
When building article pages you can also use the UI primitives that
@riebeckite/honox/ui provides:
ArticleArticleLayoutArticleContent
They are small pieces for assembling Riebeckite's article structure. They are not a dedicated component framework, so you do not have to use them; a site can build its own HTML structure instead.
See UI primitives.
Changing the article page layout
In the starter, the article page is mainly assembled in two places:
app/components/article.tsx
app/routes/_renderer.tsx
app/components/article.tsx
SiteArticle here decides the article page layout.
Typical changes made here include:
- Moving the article title
- Adding UI before or after the article
- Moving breadcrumbs
- Moving backlinks or related posts
- Adding a sidebar
SiteArticle is built on the Article primitive from @riebeckite/honox/ui,
but it is a site component, so you can edit it freely.
Note that the Article used in a route example is this site component, not the
@riebeckite/honox/ui Article with the same name.
app/routes/_renderer.tsx
_renderer.tsx is the outer layout that wraps the whole site. It mainly
manages:
<head>- Header
- Footer
- Navigation
- Shared UI for the whole page
- Head tags and styles generated by Riebeckite or plugins
RiebeckiteHeadfor rendering standard head contents
Edit it when you want to change something shared across the site.
To delegate the standard head contents to the framework while adding custom head elements:
import { RiebeckiteHead, ThemeRoot } from "@riebeckite/honox/ui";
export default jsxRenderer(({ children }, c) => (
<ThemeRoot
theme={config.theme}
lang={c.get("htmlLanguage") ?? config.site.locale}
>
<head>
<RiebeckiteHead
title={config.site.title}
headTags={c.get("headTags") ?? []}
/>
<meta name="custom-site-value" content="..." />
</head>
<body class="riebeckite-page rb-site">{children}</body>
</ThemeRoot>
);
See Head tag handoff
for the RiebeckiteHead pattern and how the site keeps ownership of <head>
while delegating standard head rendering to the framework.
Navigation
The links shown in the Header and Footer come from the
@riebeckite/plugin-navigation
plugin, which you register in riebeckite.config.ts.
plugins: [
navigation({
items: [
{ label: "Guide", href: "/guide" },
{
label: "Notes",
href: "/notes/planning",
children: [
{ label: "Planning", href: "/notes/planning" },
{ label: "Writing", href: "/notes/writing" },
],
},
],
secondary: [{ label: "GitHub", href: "https://github.com/example/site" }],
}),
]
Called with no arguments, navigation() derives the links from the vault. Pass
items to curate them yourself and secondary for supplementary links.
The plugin produces the model and renders each tree through SiteNav; the site
decides where the rendered list is placed. In the starter,
app/components/site-header.tsx uses SiteNav, and app/routes/_renderer.tsx
passes the resolved model and the current path in. So it helps to think in terms
of:
- Adding or removing a link → change
navigation({ items }) - Deriving links from the vault → call
navigation()with no arguments - Changing the Header look or HTML → change
site-header.tsx - Changing where the Header / Footer sits → change
_renderer.tsx
Rendering mechanics — recursive children, active-path detection, locale-aware
normalization, external links, and aria-current — live in SiteNav, so the
site never has to reimplement them.
See the configuration reference for the fields you can set.
Pages and links that plugins provide
Besides the Header / Footer navigation, plugins can add pages and links. These fall into two broad kinds.
Pages you browse (Content Discovery)
For example:
- Search
- Tag / Folder indexes
- Taxonomy pages
- Feeds
- Sitemap
Plugins generate the pages or endpoints these need. Enabling a plugin does not
by itself add a link to the Header or Footer. To show one there, add the page to
navigation like any other page.
UI that links articles (Content Relationship)
For example:
- Breadcrumbs
- Backlinks
- Related Posts
- Series previous/next links
- Local Graph
These are mostly rendered inside article pages. A UI fragment a plugin provides
can be placed through a body slot such as article.header or
article.footer.
In short:
Header / Footer links
↓
navigation in riebeckite.config.ts
Pages such as search, tags, and folders
↓
plugin page types
Breadcrumbs, backlinks, and so on
↓
components / body slots
Adding routes
app/routes/ is a normal HonoX route directory, so you can add site-specific
pages as usual. To create /about, for example, add it as a HonoX route.
For Markdown content and pages a plugin provides, you generally do not add a
route yourself. The starter's catch-all route uses contentRouteSsgParams,
riebeniteSsgParams, and resolveRiebeckiteContentRequest to resolve content
and plugin page types automatically.
To show a plugin page in the Header or Footer, add a link to navigation rather
than creating a new route.
404 (page not found)
Unknown URLs are handled by HonoX's standard _404.tsx route. It is an
ordinary site file, so editing it changes the "page not found" screen:
// app/routes/_404.tsx
import type { NotFoundHandler } from "hono";
const handler: NotFoundHandler = (c) => {
c.status(404);
return c.render(
<main class="not-found">
<h1>Page not found</h1>
<p>The page you requested does not exist or is not available.</p>
<a href="/">Back to home</a>
</main>,
);
};
export default handler;
Riebeckite decides that a request is not found, but the response is rendered
through _renderer.tsx, so the 404 screen reuses the site's theme, head, Header,
and Footer. Two rules matter:
-
Always keep the status at 404. The generated presets call
c.status(404); a pretty screen served as200would be wrong. - Presentation is site-owned. The markup, copy, links, and CSS are all yours. Riebeckite does not ship a default 404 component to override.
The 404 screen only ever sees requests that are not found. Draft, future-dated, and otherwise unpublished content never reaches it, so a 404 cannot reveal that private content exists.
Runtime errors (optional)
Hono's default error handling already logs the error and returns a plain
500 Internal Server Error, so a site does not have to add anything.
If you want a site-owned visitor-facing error screen, HonoX supports
app/routes/_error.tsx (an ErrorHandler). The generated presets do not add
it: configuration, plugin, and build errors are developer-facing and should
stay visible instead of being disguised as a page.
Using plugin components
Some plugins provide Hono JSX components you can use directly from the site. For example, you can place Backlinks or a Table of Contents anywhere you like:
import { Backlinks } from "@riebeckite/plugin-backlinks";
import { TableOfContents } from "@riebeckite/plugin-toc";
export function ArticleAside({ items, backlinks }: Props) {
return (
<aside>
<TableOfContents items={items} />
<Backlinks backlinks={backlinks} />
</aside>
);
}
Check each plugin page or package README for the available components and props.
Many components can also be imported as the default export from ./components:
import Backlinks from "@riebeckite/plugin-backlinks/components";
color-mode is the exception: it exports ColorModeScript and
ColorModeToggle from the package root.
Plugins that use body slots
Some plugins do not place a component directly; instead they add HTML to fixed
locations on the article page. This is the body slot mechanism. The fragment
appears automatically once the plugin is enabled and the site's SiteArticle
renders the matching slot.
See Body slots and Providing UI or output.
Building interactive UI
Create UI that needs browser behavior, such as clicks or state, as a normal
HonoX island under app/islands/. Import the island from a route or component
as usual. There is no Riebeckite-specific island mechanism.
app/client.ts initializes both:
createClient();
initRiebeckiteClient();
createClient() initializes the site's client behavior, and
initRiebeckiteClient() starts the browser-side behavior that plugins and
themes provide. Site islands live under app/islands/, while plugin browser
behavior lives in the plugin, keeping the responsibilities separate.
Changing CSS
You can change the site design from app/style.css and each component's CSS.
Plain CSS and Tailwind both work.
Import the CSS Riebeckite generates from plugins and themes once from the site CSS:
/* app/style.css */
@import "./.riebeckite/framework-styles.css";
@import "./.riebeckite/plugin-styles.css";
@import "./.riebeckite/theme-styles.css";
Do not edit files under app/.riebeckite/; they are generated. To override
plugin appearance use the rr-<feature> root hook, and to adjust the Riebeckite
UI primitives use the rb-* structural hooks. See
CSS hooks.
Rules of thumb
If you are unsure where to make a change, this usually helps:
| What you want to do | Where to change it |
|---|---|
| Add a link to the Header | riebeckite.config.ts |
| Change the Header appearance | app/components/site-header.tsx |
| Change the site-wide shell | app/routes/_renderer.tsx |
| Change the article page structure | app/components/article.tsx |
| Add a custom page | app/routes/ |
| Change the 404 screen | app/routes/_404.tsx |
| Create a custom component | app/components/ |
| Build interactive UI | app/islands/ |
| Change color or spacing | app/style.css |
| Place plugin UI | plugin component / body slot |
The basic idea is that Riebeckite connects content and plugins to the site, and the site decides the final look and structure of each page.
Where to look next
- HonoX Integration — how Riebeckite connects to HonoX and the UI primitives
- Plugins in Depth — extension points for building plugins
- Plugin API — the contracts for body slots, pages, assets, and CSS hooks