Build your first Theme
This guide takes you from nothing to a live Theme. You'll scaffold a Theme, learn what the manifest declares, run it locally, and ship a first deployment you can open in a browser.
This page is for frontend developers building a Storefront's front end. Prerequisites: Node.js 22 or newer, npm, a revenexx account with a tenant (who grants Cockpit access, find your tenant slug), and the revenexx CLI installed and signed in.
1. Scaffold from the sample Theme
The CLI stamps the whole Theme contract for you: manifest, billing file, icon, Nuxt config and a deploy script. See Experience Studio: getting started for what each file is.
revenexx create theme my-theme
cd my-theme
npm install
create comes from a CLI plugin, so it is present only when your CLI is built with it. See Scaffolding. If the command is missing, clone the sample Theme instead.
The scaffold is deliberately small. You'll add blocks and styling on top of it in the next guides.
2. Understand theme.json
theme.json is the manifest. It tells the platform that this site is a Theme, what it needs to run, and which blocks editors can compose pages with. Open it. The one below is representative; the file your scaffold writes may differ in detail.
{
"$schema": "https://schemas.revenexx.com/theme.schema.json",
"kind": "theme",
"engine": "blokkli",
"name": "my-theme",
"vendor": "my-agency",
"version": "1.0.0",
"title": "My Storefront",
"description": "A Nuxt storefront that renders a tenant's products and pages.",
"icon": "icon.svg",
"type": "public",
"site": {
"framework": "nuxt",
"adapter": "ssr",
"installCommand": "npm install",
"buildCommand": "npm run build",
"outputDirectory": ".output",
"domains": ["{{tenant_domain}}"]
},
"requires": [
{
"capability": "products.list",
"compatible": "^1.0",
"reason": "renders the product grid on the storefront home and category pages"
}
],
"permissions": [
{ "capability": "products.list", "compatible": "^1.0" },
{ "entity": "pages", "access": ["read"] },
{ "storage": "theme-media", "access": ["read"] }
],
"blokkli": {
"blocks": [
{ "id": "hero", "title": "Hero", "description": "Full-width hero banner with headline, subline, and a call-to-action." },
{ "id": "product_grid", "title": "Product Grid", "description": "Renders products from the products.list capability.", "requiresCapability": "products.list" },
{ "id": "text", "title": "Text", "description": "A text block for editorial content." }
],
"presets": [
{ "id": "home", "title": "Home page", "blocks": ["hero", "product_grid", "text"] }
]
}
}
The fields that matter:
| Field | What it does |
|---|---|
kind: "theme" + engine: "blokkli" | Marks the site as a Blökkli Theme so Cockpit lists it in the Theme marketplace. |
name, vendor, version | Identity. version is strict semver — a published version is immutable, so bump it before you re-publish. |
site | How the platform builds and runs the site: Nuxt, server-side rendering, npm install + npm run build, output in .output. |
requires[] | Capabilities the Theme needs to install. A Theme that requires products.list can't be installed on a tenant that has no products app routed; Cockpit blocks the install and names the missing capability (as of 2026-10, see Theme anatomy). |
permissions[] | What the Theme is granted access to, consented at install time. |
blokkli.blocks[] | The blocks editors can place on pages. Each id is snake_case and maps to a component at app/components/blokkli/<id>/index.vue; Build a block shows the three places that must agree. |
blokkli.presets[] | Starter page layouts an editor can pick from. |
billing.json sits next to it and carries pricing only. The sample ships a free Theme:
{
"$schema": "https://schemas.revenexx.com/billing.schema.json",
"type": "free",
"support": { "email": "support@my-agency.com", "url": "https://my-agency.com/support" },
"categories": ["theme", "storefront"],
"available_countries": ["*"]
}
3. Run it locally
A Theme is a Nuxt app, so the dev loop is the standard one.
npm run dev
Open the printed URL. The sample page server-renders and shows the request context it received. The build config is plain Nuxt SSR — ssr: true with the node server output that the platform runs in production:
export default defineNuxtConfig({
compatibilityDate: '2025-01-01',
// Server-side rendering, served by the Nuxt server in production.
ssr: true,
devtools: { enabled: false },
})
To check the production output before you ship, build it into .output and preview it the same way the platform will run it (npm run preview serves .output/server/index.mjs locally):
npm run build
npm run preview
4. Deploy a first version
From the Theme directory, one command finds or creates the site, uploads and builds it, registers the Theme and installs it on your tenant:
revenexx deploy theme .
It needs a signed-in CLI with your tenant selected, as in the prerequisites. The deployment gets a preview URL on the generated *.sites.revenexx.io domain; open it to confirm the Theme runs in production. Flags, the step-by-step revenexx sites commands and Git-connected deploys are covered in Deploy from the CLI and Deploy from Git; rolling back is in Instant rollbacks.
5. Activate it on a domain
The install from step 4 lists your Theme under Cockpit → Experience Studio → Marketplace → Installed Themes. Activate it there for the domain you want it to serve. The per-domain details are in Publishing a theme.
Troubleshooting
- Install is blocked on the tenant. The Theme
requiresa capability (e.g.products.list) the tenant doesn't have routed yet. Install the app that provides it, then retry. Cockpit names the missing capability, not necessarily the app. - "Already registered" when you re-publish. A published Theme version is immutable. Bump
versionintheme.jsonand ship again. - Build runs locally but fails on deploy. The platform runs
npm install(not a workspace install), so every dependency must resolve from npm. Commit your lockfile and avoid workspace-only links.
Where to go next
- Build a block — write your own Blökkli block and connect one to live catalog data.
- Brand a Theme with design tokens — brand the Theme per tenant.
- Publishing a theme — registry, marketplace, per-domain activation and versioning.
- Reference: Blökkli blocks and Storefronts.