Claude
Skills
Sign in
Back

vtex-io-storefront-theme-app

Included with Lifetime
$97 forever

Apply when designing or modifying a VTEX IO storefront theme app — the app that owns `store/blocks.json`, `store/routes.json`, `store/templates/`, `store/contentSchemas.json`, and the storefront page tree assembled from `store.home`, `store.product`, `store.search`, `store.custom`, and other native page templates. Covers how a theme app extends a base theme, declares routes, composes blocks across pages, and how its `store/` files relate to merchant Site Editor content. Use for theme scaffolding, custom page routes, theme-level overrides, or reviewing whether a change belongs in the theme app, in a component app, or in app settings.

Design

What this skill does


# Storefront Theme App

## When this skill applies

Use this skill when working on the app that assembles the storefront — the theme app that owns the page tree, custom routes, and Site Editor surface area for a store.

- Scaffolding `vendor.store-theme` or any app with the `store` builder that ships pages
- Adding or changing `store/blocks.json` or per-template files under `store/blocks/`
- Adding or modifying a custom route in `store/routes.json`
- Composing the block tree for `store.home`, `store.product`, `store.search`, `store.custom`, or any native page template
- Extending or overriding a base theme such as `vtex.store-theme`
- Reviewing whether a change belongs in the theme app, in a component app, or in app settings

Do not use this skill for:

- registering a new block in a component app (`store/interfaces.json`) — use `vtex-io-render-runtime-and-blocks`
- implementing the React component behind a block — use `vtex-io-storefront-react`
- changing the theme app's installed version on `master` — use `vtex-io-storefront-theme-versioning`
- localized strings — use `vtex-io-messages-and-i18n`

## Decision rules

- A theme app is a regular VTEX IO app with the `store` builder declared in `manifest.json`. It almost never ships React code; it composes blocks declared by component apps and by base themes.
- The theme app declares its base theme in `manifest.json#dependencies`, typically `vtex.store-theme`, and inherits every page template, block declaration, and default content the base ships.
- `store/blocks.json` (or per-template files under `store/blocks/`) defines the **block tree per page template** by referencing block IDs. Block IDs come from the component apps' `store/interfaces.json` and are scoped by the declaring app's MAJOR version.
- `store/routes.json` defines **custom storefront routes** and binds them to a page context (`store.custom`, `store.product`, `store.search`, etc.). Native routes (`/`, `/{slug}/p`, search) come from the base theme and rarely need to be redeclared.
- `store/contentSchemas.json` declares **Site Editor-editable props** for blocks. Merchant edits to those props are stored by `vtex.pages-graphql` under a key that includes the theme app's MAJOR version.
- Three change locations exist for storefront behavior. Pick consciously:
  1. Theme app `store/` JSON — composition, routes, default content, allowed children. Affects all shoppers immediately on promote.
  2. Component app code — the React behavior of a block. Released on the component app's own version cadence.
  3. Site Editor — merchant-managed content overrides on top of the theme's defaults. Stored by `vtex.pages-graphql` and scoped by the declaring app's installed major.
- Prefer extending a base theme over forking it. Forking a base theme moves the responsibility for every block, route, and template to your app forever, including upstream bug fixes.
- A storefront page is a tree of blocks. The leaves are component blocks; the branches are container blocks (`flex-layout.row`, `flex-layout.col`, etc.). Keep the tree as shallow as the design allows; deep trees inflate render and content footprint.
- The theme app is a **content-holding app** in the sense of `vtex-io-storefront-theme-versioning`. Its installed major version is part of the key the platform uses for every Site Editor change a merchant has ever saved against blocks declared by this app. Treat its version contract as merchant-facing, not developer-facing.

## Hard constraints

### Constraint: Theme apps must declare the `store` builder and a base theme

A storefront theme app MUST declare `"store"` in `manifest.json#builders` and MUST depend on a base theme (typically `vtex.store-theme`) unless it explicitly takes ownership of every native page template, block, and route.

**Why this matters**

Without the `store` builder, none of the files under `store/` are processed and the theme contributes nothing to the storefront. Without a base theme, the app is responsible for declaring every native page template (`store.home`, `store.product`, `store.search`, etc.) from scratch — including upstream maintenance forever.

**Detection**

If a theme app ships `store/blocks.json` or `store/routes.json` but its manifest does not declare `"store"` in `builders`, STOP and add the builder. If the manifest also omits `vtex.store-theme` (or another base theme) from `dependencies` or `peerDependencies` without an explicit reason, STOP and confirm the app intends to own every native template.

**Correct**

```json
{
  "vendor": "acme",
  "name": "store-theme",
  "version": "1.0.0",
  "title": "ACME Store Theme",
  "builders": {
    "store": "0.x",
    "messages": "1.x"
  },
  "dependencies": {
    "vtex.store-theme": "2.x",
    "acme.product-widgets": "0.x"
  }
}
```

**Wrong**

```json
{
  "vendor": "acme",
  "name": "store-theme",
  "version": "1.0.0",
  "builders": {
    "messages": "1.x"
  },
  "dependencies": {}
}
```

The `store/` files exist on disk but the platform ignores them, and the theme inherits nothing.

### Constraint: Block IDs in `store/blocks.json` must resolve to a registered block in an installed app

Every block ID referenced in `store/blocks.json` (or per-template files) MUST be declared in an `interfaces.json` of an installed app whose MAJOR version matches what the platform resolves at render time. Unresolved block IDs cause `Missing block` errors at the GraphQL layer and break the page.

**Why this matters**

`vtex.pages-graphql` resolves a block ID by looking up `[email protected]:block-id` against the installed apps. If the declaring app is not installed, or is installed at a different major than the merchant content was authored against, the block does not exist from the resolver's point of view and the page fails to render.

**Detection**

If `store/blocks.json` references a block ID, verify that some app in `manifest.json#dependencies` declares it in `store/interfaces.json` at the major version range listed in the dependency. If the dependency is `[email protected]`, the block must exist in the `0.x` line of that app, not the `5.x` line.

**Correct**

```json
// manifest.json
{
  "dependencies": {
    "vtex.store-theme": "2.x",
    "acme.product-widgets": "0.x"
  }
}
```

```json
// store/blocks.json
{
  "store.product": {
    "children": [
      "flex-layout.row#product-main",
      "acme-related-products"
    ]
  },
  "acme-related-products": {
    "props": { "limit": 8 }
  }
}
```

```json
// [email protected] ships store/interfaces.json with:
{
  "acme-related-products": {
    "component": "RelatedProducts"
  }
}
```

**Wrong**

```json
// manifest.json depends on [email protected]
// but store/blocks.json references a block that only exists in @5.x
{
  "store.product": {
    "children": ["acme-new-related-products"]
  }
}
```

The render-time resolver returns `Missing block [email protected]:acme-new-related-products` and the page fails.

### Constraint: Custom routes in `store/routes.json` must bind to a real page context and template

Every entry in `store/routes.json` MUST set a valid `path`, a `context` (or rely on the built-in context for native page IDs such as `store.product`), and a template that exists in the block tree (the page ID itself, or a `store.custom#<id>` declared in `store/blocks.json`).

**Why this matters**

A route entry without a resolvable template renders nothing. A route entry with the wrong context (e.g., a custom institutional page typed as `store.product`) executes the wrong data resolver and crashes or returns empty product data.

**Detection**

For each route in `store/routes.json`, confirm: (a) the `path` does not collide with native VTEX paths, (b) the bound page ID exists as a key in `store/blocks.json`, and (c) the `context` matches the data the page actually needs.

**Correct**

```json
// store/routes.json
{
  "store.custom#about": {
    "path": "/institucional/sobre",
    "context": "vtex.store-resources/Institu

Related in Design