Theme overview
What a theme provides — templates, samples, assets.
You've seen one theme already — the shipped default theme rendering the Minimal sample inside /builder.php. To put your brand on top of BuilderJS you'll customise this layer, not the engine. This page tells you what a theme is, when to author one, and when authoring just a sample is enough.
What ships with a theme
A theme is a single folder under demo/themes/<name>/. It contains four kinds of files, and that's it:
- Templates (
*.template.html) — one EJS template per element type.Image.template.htmlrenders anImageElement,Button.template.htmlrenders aButtonElement, and so on. The shippeddefaulttheme has 30+ templates covering every element the engine surfaces. They are the visual contract — change them and the rendered HTML changes. - Samples (
master/sample/email/*.json+master/sample/page/*.json) — pre-authored page trees ready to drop intobuilder.load(themeJson, …). The default theme ships 70+ samples between email + page; every card on/gallery.phpis one of them. - Assets (
master/assets/image/) — content images. The shipped library is 100% AI-generated photo + illustration imagery, royalty-free for any buyer use (RULE-H1 — no copyrighted images, ever). Replace with your brand photography by dropping files in this folder; the JSONsrckeys reference them by relative path. - Manifest (
index.json) — metadata: themename,title, thepagesarray (which page templates apply), thetemplatesarray (which element templates ship), and an optional_brandblock of brand tokens.
The whole theme directory is portable — copy it to another BuilderJS install and it works. No SQL migrations, no custom build step. That portability is why themes are the buyer customisation surface, not the engine.
When to author a new sample vs a new theme
Short version:
- I want one new email layout for my campaign → author a sample. A new
.jsonfile underthemes/default/master/sample/email/<your-name>.jsonplus a thumbnail, and the gallery picks it up. - I want my brand colours, my fonts, my logo, my domain-specific element layout → fork the default theme. Copy
themes/default/→themes/<your-brand>/, editindex.json+ replace logos / fonts / templates / samples, register the new theme dir.
Most buyer projects start with the first and graduate to the second. The line crosses when a single sample isn't enough to express your brand — for example, when every email needs the same custom header that no shipped template renders, or when you need a primary colour that isn't in the shipped palette.
Why themes are pluggable, not configurable
You might expect a theme to be "the same default templates plus a CSS override". BuilderJS goes further: a theme owns its templates wholesale. Two reasons:
- Templates control rendered HTML, not just styling. A theme that wants
<h2 class="brand-headline">instead of<h1>for a heading element changes one template, not a CSS file. That kind of structural override would be impossible with CSS-only theming. - Email-client compatibility lives in templates. The shipped templates are tuned for the Litmus 22-client matrix (Outlook, Apple Mail, Gmail web, …). When you author a custom theme, you inherit those compatibility decisions; when you fork a template you can re-tune for your audience.
The trade-off: you maintain the templates yourself. The shipped default theme is your reference; the example-mini/ starter is what an author-from-scratch theme looks like (just Page.template.html + Block.template.html + a tiny manifest — under 100 LOC total before you customise it).
The shipped default theme tour
Open demo/themes/default/ and look at the layout:
When demo/builder.php boots, the buyer-modifiable line is $builderConfig['defaultTheme'] = 'default' — change it to your fork name and every page in the demo loads your theme instead.
Common buyer questions answered up front
Q: Can I have multiple themes side-by-side? A: Yes. Drop a second theme dir under
themes/, register it inThemeRegistry::SEED(one row per theme), and/gallery.phpshows both. The builder accepts a?theme=<name>query param so each rendered page can pin its theme.Q: Can I share assets between themes? A: Two patterns. (1) Use the shipped
defaulttheme's images by leaving JSONsrcpaths pointing atmaster/assets/image/...— the engine resolves relative to the active theme but you can hard-link to a known-stable shared path. (2) Symlink the assets dir at the filesystem level —themes/<your-fork>/master/assets → themes/default/master/assets. Both work; the symlink approach is cleaner if your themes share imagery wholesale.Q: Do I need to write EJS, or can templates be plain HTML? A: They're EJS —
<%= var %>and<% if %>blocks. But 90% of every shipped template is plain HTML; the EJS bits inject the JSON-driven values. Read three of the shipped templates and the pattern is obvious.
Did you make it?
You should now be able to:
- ✅ State the smallest unit you can ship to add your brand to BuilderJS — answer: one new
.jsonsample (or one new theme dir for a deeper change). - ✅ Distinguish theme-level customisation from sample-level customisation.
- ✅ Open
themes/default/and recognise each top-level dir's purpose.
What's next?
- → Theme anatomy — open every file in
themes/default/and learn what each one renders. - 🔗
/gallery.php— the 43-theme + 50+ sample browser. - 🔗
examples/basic/3-custom-theme/— a runnable end-to-end demo that loads a custom theme.