Build your first Theme
This tutorial takes you from nothing to a live Theme. You'll start from the sample 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. You need Node.js 22+, npm, and a revenexx account with a tenant. If you want to deploy from your terminal, install and sign in to the revenexx CLI first.
1. Scaffold from the sample Theme
The fastest start is the sample Theme — a minimal, runnable Nuxt SSR site with a valid manifest. Copy it into a new project directory and install dependencies.
# Start from the sample Theme as your own repository
npx degit revenexx/sample-theme my-theme
cd my-theme
npm install
The sample is deliberately small: a single page, a manifest, and a billing file. You'll add blocks and styling on top of it in the next tutorials.
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.
{
"$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": "rich-text", "title": "Rich Text", "description": "A WYSIWYG text block for editorial content." }
],
"presets": [
{ "id": "home", "title": "Home page", "blocks": ["hero", "product-grid", "rich-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 points the customer at the app that provides it. |
permissions[] | What the Theme is granted access to, consented at install time. |
blokkli.blocks[] | The blocks editors can place on pages. Each id maps to a block component you write. |
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 and preview it the same way the platform will run it:
npm run build # → .output
npm run preview # serves .output/server/index.mjs locally
4. Deploy a first version
You have two ways to ship a Theme. Pick one now; Deploy your storefront covers both in depth.
Git-connected (recommended). Push your Theme repository to your Git provider and connect it to a Storefront. Every push to your production branch builds (npm install && npm run build) and deploys automatically. Full walkthrough: Deploy from Git.
From the revenexx CLI. Deploy straight from your terminal — useful for a first push or for CI. Full walkthrough: Deploy from CLI.
revenexx login --token <your-token> --tenant <your-tenant-slug> # if you haven't already
# then create a site deployment — see the Deploy from CLI guide
Either path produces a deployment with a preview URL on the generated *.sites.revenexx.io domain. Open it to confirm the Theme runs in production.
5. Activate it on a domain
A successful deployment lists your Theme in the Cockpit Theme marketplace. To put it on a real domain:
- In Cockpit → Experience Studio → Marketplace, install your Theme on the tenant. It then appears under Installed Themes.
- Activate it for the domain you want it to serve.
From then on, requests to that domain are served by your Theme, with the tenant resolved per request.
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 — Cockpit names it in the error — then retry. - "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.
What's next
- Customize blocks — write your own Blökkli block and connect one to live catalog data.
- Theme tokens and styling — brand the Theme per tenant.
- Reference: Blökkli blocks and Storefronts.