Theme Inheritance
Dune supports theme inheritance — a child theme can extend a parent theme, overriding only the templates and assets it needs to change.
How it works
Declare a parent in your theme manifest:
# themes/my-theme/theme.yaml
name: my-theme
version: 1.0.0
parent: default # inherits from themes/default/
You can also set parent on the active theme in config/site.yaml — it overrides theme.yaml for the active theme only:
theme:
name: my-brand
parent: paper # local themes/my-brand/ extends package "paper"
Parents can be a local theme name, an import-map alias, or a pinned package specifier:
themes:
- name: paper
src: jsr:@dune/theme-paper@1.0.0
theme:
name: my-brand
parent: jsr:@dune/theme-paper@1.0.0
Resolution order
When Dune looks for a template, it checks:
- Child theme
templates/directory - Parent theme
templates/directory - Error if not found in either
This means:
- Override
post.tsxin your child theme, and all posts use your version - Leave
default.tsxalone, and the parent's version is used - You only customize what you need
Example
Parent theme (themes/default/):
templates/
├── default.tsx ← used for default.md pages
├── post.tsx ← used for post.md pages
├── blog.tsx ← used for blog listing
└── error.tsx ← 404 page
Child theme (themes/my-theme/):
templates/
├── post.tsx ← overrides parent's post template
└── landing.tsx ← adds a new template type
Result:
default.md→ parent'sdefault.tsx(not overridden)post.md→ child'spost.tsx(overridden)blog.md→ parent'sblog.tsx(not overridden)- Pages with
template: landing→ child'slanding.tsx(new)
Static assets
Static assets also follow the inheritance chain. If your child theme provides static/styles.css, it overrides the parent's. If it doesn't, the parent's is served.
For package-backed themes, static files live in the package's static/ directory and are served at /themes/{name}/static/... without copying them into your site tree.
Locale strings
Theme UI strings in locales/{lang}.json are merged per key across the inheritance chain: parent keys are applied first, child keys last, so a child theme can override or add individual strings without copying the parent's whole locale file. For languages other than en, the chain-merged en locale is layered underneath as a fallback for missing keys.
For example, to relabel a language in a language switcher, a stub child theme only needs:
// themes/my-theme/locales/en.json
{ "de": "German" }
All other keys from the parent's en.json remain available via t(key).
Practical use cases
Branding customization. Start from a well-tested base theme, override just the layout and styles.
Template additions. Add new content types (portfolio, gallery, documentation) without duplicating the base templates.
Seasonal variants. Create holiday themes that override just the header and color scheme.