Themes
Build and customise themes using Preact TSX components.
Theme structure
A theme is a directory under themes/:
themes/
└── my-theme/
├── theme.yaml # metadata
├── components/
│ └── layout.tsx # shared layout wrapper
├── templates/
│ ├── default.tsx # used when no template is specified
│ ├── home.tsx # used for template: home
│ └── blog.tsx # used for template: blog
├── locales/
│ ├── en.yaml # translation strings
│ └── de.yaml
└── static/
├── style.css
└── logo.svg # served at /themes/my-theme/static/logo.svg
theme.yaml
name: my-theme
description: My custom Dune theme
version: "1.0.0"
parent: starter # optional: inherit templates from parent
Templates
Templates are Preact components. They receive these props:
| Prop | Type | Description |
|---|---|---|
page |
Page |
Current page (frontmatter, route, content) |
pageTitle |
string |
Resolved <title> string |
site |
SiteConfig |
site.yaml contents |
config |
DuneConfig |
Full config including theme settings |
nav |
NavItem[] |
Top-level navigation items |
pathname |
string |
Request pathname |
children |
JSX.Element |
Rendered markdown as HTML |
t |
(key: string) => string |
i18n translation function |
/** @jsxImportSource preact */
import { h } from "preact";
export default function BlogTemplate({ page, site, nav, children }: any) {
return (
<html lang="en">
<head>
<title>{page.frontmatter.title} — {site.title}</title>
<link rel="stylesheet" href="/themes/my-theme/static/style.css" />
</head>
<body>
<nav>
{nav.map((item: any) => (
<a key={item.route} href={item.route}>{item.navTitle ?? item.title}</a>
))}
</nav>
<main>
<h1>{page.frontmatter.title}</h1>
<div>{children}</div>
</main>
</body>
</html>
);
}
Theme inheritance
Set parent in theme.yaml to inherit templates from another theme:
name: my-theme
parent: starter
Dune resolves templates from your theme first, then falls back to the parent. Override only the templates you need to change.
Static assets
Files in themes/my-theme/static/ are served under
/themes/my-theme/static/. Reference them with absolute paths in your TSX.
Custom theme values
Pass arbitrary values in config/site.yaml:
theme:
name: my-theme
custom:
primaryColor: "#C9943A"
heroImage: "/static/hero.jpg"
Access them in templates via config.theme.custom:
const accent = config?.theme?.custom?.primaryColor ?? "#000";
i18n in themes
Create locale files in themes/my-theme/locales/:
# locales/en.yaml
nav.home: Home
nav.about: About
footer.copyright: "© {year} {author}"
Use the t prop to look up translations:
<footer><p>{t("footer.copyright").replace("{year}", String(new Date().getFullYear()))}</p></footer>