Publishing a theme
A deployed theme is not yet an installable theme. Getting from a successful build to a storefront serving a customer's domain has four steps, and they happen in this order.
a `ready` deployment
↓ register
the theme registry
↓ publish
the Cockpit Theme Marketplace
↓ install
a tenant
↓ activate
a domain
1. Registration needs a ready deployment
A theme is registered from a deployment that built successfully. A failed deployment has no artifact and a building one isn't finished, so neither can be registered — see Deployments.
The one-command path does the registration for you:
revenexx deploy theme .
deploy theme runs a site deploy plus automatic registry registration, then publish and install. It expects a theme.json in the directory. Skip the last two steps with --no-publish and --no-install when you want to register a build without offering it to anyone yet.
What the registry reads is the manifest, not your code: name, vendor, version, title, type, the requires[] capabilities, the permissions[], and the blokkli payload. See Theme anatomy.
2. Publishing lists it in the marketplace
Publishing makes a registered theme visible in the Cockpit Theme Marketplace, where a customer can find and install it.
Whether it can be listed at all is theme.json#type:
type | Who can install it |
|---|---|
public | Any tenant, once the operator publishes it. Listed in the Marketplace. |
private | Only the owner tenant. Never listed. |
Use private for a bespoke theme built for one customer — which is most agency work. There is no reason to list a customer's own storefront in a public marketplace, and private keeps the install surface honest.
billing.json carries the commercial side of the listing — pricing, support contacts, categories, availability. It is deliberately separate from type: public and paid are independent decisions.
3. Installing on a tenant
In Cockpit, a customer installs the theme from Experience Studio → Marketplace; once installed it appears under Installed Themes, listed with its engine, version and which sites it is live on. Installation is where two manifest declarations get enforced:
requires[] is a hard dependency. A theme that requires products.list cannot be installed on a tenant with no products app routed. Cockpit blocks the install and names the app that provides the capability. This is a feature — it stops a theme being installed into an environment where half its pages would error.
permissions[] is consented at install time. The customer sees what the theme is asking for and grants it. Ask for what you use; a theme requesting broad access it doesn't need is a theme a customer's security review will stop.
4. Activating per domain
Installing makes a theme available. Activation binds it to a domain.
In Experience Studio → Installed Themes, activate the installed theme for the domain it should serve; the list's Live on column shows where each installed theme is currently serving. From then on, requests to that domain are served by your theme, with the tenant resolved per request.
Activation is per domain, not per tenant, and that distinction is what makes the multi-tenant model work: one deployed theme can serve many tenants, each activating it on its own domain. You don't deploy a copy per customer. See Multi-tenant themes.
Why you must bump theme.json#version
version in theme.json — strict semver — before you publish again.The reason is what immutability buys the customer: a tenant that installed 1.2.0 has installed a build that cannot change underneath them. Without that, "install this theme" would be a promise nobody could keep.
Two practical consequences:
- Code-only changes deploy normally. Immutability applies to the published registry version, not to every deployment. Pushing a CSS fix to a Git-connected site does not require a version bump.
- Manifest changes need a bump. A new block in
blokkli.blocks[], a new required capability, a changed permission — anything the registry reads — reaches tenants only through a new published version.
Which means: a new block is not live for editors until you publish a new theme version. Deploying the component is necessary and not sufficient. See Registering blocks.
Shipping an update to an installed theme
The sequence for a real change, in order:
- Bump
versionintheme.jsonif the manifest changed. - Deploy and register —
revenexx deploy theme ., or a push to a Git-connected site plus a registration. - Verify on the preview host before anything is activated.
- Publish, so the new version reaches the marketplace.
- The customer updates their installed version.
Step 3 is the one people skip and shouldn't. A preview runs the real build with the real configuration, which is where you find out that a domain you thought was on api is still on mock.
Troubleshooting
- "Already registered" on publish. A published version is immutable. Bump
versionintheme.json. - The install is blocked on the tenant. The theme
requiresa capability the tenant doesn't have routed. Install the app that provides it — Cockpit names it — then retry. - A new block doesn't appear in the editor. The manifest reaches editors through a published version. Bump, deploy, publish. And check the three matching strings in Registering blocks.
- The domain still serves the old theme. Confirm the new deployment is the active one for that site, and that the theme is activated on the right domain for the right tenant.
- The theme isn't in the marketplace.
typeisprivate, or it's registered but not published.
Related
- Theme anatomy — the manifest the registry reads
- Deployments — what
readymeans - Previews — verifying before you publish
- Deploy from the CLI —
revenexx deploy theme .and its flags - Deploy your storefront — the end-to-end tutorial
- Multi-tenant themes — one theme, many tenants