Now liveThe Skillselion MCP - thousands of ranked skills, loaded into your agent mid-task. No install.Get it →
vtex avatar

Vtex Io Storefront Theme App

  • 4 installs
  • 39 repo stars
  • Updated June 16, 2026
  • vtex/ai-skills

Designs and modifies VTEX IO storefront theme apps that own blocks.json, routes.json, templates, and the storefront page tree across native page templates.

About

This skill covers the VTEX IO theme app that assembles the storefront page tree, custom routes, and Site Editor surface. A developer uses it when scaffolding a theme, editing blocks.json or routes.json, composing page templates, or extending a base theme.

  • Composes block trees for store.home, store.product, store.search, and custom pages
  • Decides whether a change belongs in the theme app, a component app, or settings

Vtex Io Storefront Theme App by the numbers

  • 4 all-time installs (skills.sh)
  • +1 installs in the week ending Aug 5, 2026 (Skillselion tracking)
  • Ranked #1,817 of 2,245 Frontend Development skills by installs in the Skillselion catalog
  • Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/vtex/ai-skills --skill vtex-io-storefront-theme-app

Add your badge

Show developers this skill is listed on Skillselion. Paste this into your README.

Listed on Skillselion
Installs4
repo stars39
Last updatedJune 16, 2026
Repositoryvtex/ai-skills

What it does

Designs and modifies VTEX IO storefront theme apps that own blocks.json, routes.json, templates, and the storefront page tree across native page templates.

Files

SKILL.mdMarkdownGitHub ↗

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

{
  "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

{
  "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 vendor.app@MAJOR.x: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 acme.product-widgets@0.x, the block must exist in the 0.x line of that app, not the 5.x line.

Correct

// manifest.json
{
  "dependencies": {
    "vtex.store-theme": "2.x",
    "acme.product-widgets": "0.x"
  }
}
// store/blocks.json
{
  "store.product": {
    "children": [
      "flex-layout.row#product-main",
      "acme-related-products"
    ]
  },
  "acme-related-products": {
    "props": { "limit": 8 }
  }
}
// acme.product-widgets@0.x ships store/interfaces.json with:
{
  "acme-related-products": {
    "component": "RelatedProducts"
  }
}

Wrong

// manifest.json depends on acme.product-widgets@0.x
// 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 acme.product-widgets@0.x: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

// store/routes.json
{
  "store.custom#about": {
    "path": "/institucional/sobre",
    "context": "vtex.store-resources/InstitutionalPageContext"
  }
}
// store/blocks.json
{
  "store.custom#about": {
    "blocks": ["rich-text#about-body"]
  }
}

Wrong

// store/routes.json
{
  "store.custom#about": {
    "path": "/p/sobre"
  }
}

Path /p/sobre collides with the native PDP route shape, no context is declared, and there is no matching store.custom#about template in store/blocks.json.

Preferred pattern

Recommended theme app structure:

acme.store-theme/
├── manifest.json                     # store builder, base theme dependency
├── messages/                         # localized strings (separate skill)
└── store/
    ├── blocks.json                   # global block declarations
    ├── routes.json                   # custom routes
    ├── contentSchemas.json           # Site Editor-editable props
    └── blocks/                       # per-template block trees
        ├── home.jsonc
        ├── product.jsonc
        ├── search.jsonc
        ├── category.jsonc
        └── custom/
            ├── about.jsonc
            └── stores.jsonc

Recommended store.home composition:

{
  "store.home": {
    "blocks": [
      "flex-layout.row#home-hero",
      "shelf#home-best-sellers",
      "rich-text#home-newsletter"
    ]
  },
  "flex-layout.row#home-hero": {
    "children": ["image#hero-banner"]
  },
  "image#hero-banner": {
    "props": {
      "src": "/arquivos/home-hero.png",
      "alt": "Promotional banner"
    }
  }
}

Recommended custom institutional route:

// store/routes.json
{
  "store.custom#about": {
    "path": "/institucional/sobre",
    "context": "vtex.store-resources/InstitutionalPageContext"
  }
}
// store/blocks/custom/about.jsonc
{
  "store.custom#about": {
    "blocks": ["flex-layout.row#about-body"]
  },
  "flex-layout.row#about-body": {
    "children": ["rich-text#about-copy"]
  },
  "rich-text#about-copy": {
    "props": {
      "text": "About ACME — see Site Editor for the live copy."
    }
  }
}

Recommended Site Editor-editable surface:

// store/contentSchemas.json
{
  "rich-text#about-copy": {
    "type": "object",
    "properties": {
      "text": {
        "type": "string",
        "title": "Body copy",
        "widget": { "ui:widget": "textarea" }
      }
    }
  }
}

Merchant edits to text are persisted by vtex.pages-graphql under a key that includes the theme app's MAJOR version. See vtex-io-storefront-theme-versioning for what happens to that content on a major bump and how to migrate it with the updateThemeIds mutation.

Common failure modes

  • Forking vtex.store-theme instead of depending on it, then losing every upstream block fix and route addition forever.
  • Referencing a block ID in store/blocks.json that exists in a different major of the declaring app than the dependency range allows.
  • Declaring the same block ID in two different apps and getting non-deterministic resolution at render time.
  • Putting Site Editor-editable copy directly in store/blocks.json props without contentSchemas.json, so merchants cannot change it.
  • Adding a custom route to store/routes.json without adding the matching store.custom#<id> template in store/blocks.json.
  • Treating the theme app's version as developer-facing and bumping the major to "tidy up", which leaves the new major with no merchant content and forces an updateThemeIds migration in vtex.pages-graphql@2.x before promote (see vtex-io-storefront-theme-versioning).
  • Putting React component code in the theme app instead of in a dedicated component app, which mixes block declaration with block consumption and complicates reuse.
  • Storing operational or shopper-specific data in theme store/ files. The theme is global; per-shopper or per-segment data belongs elsewhere.

Review checklist

  • [ ] Does manifest.json declare the store builder?
  • [ ] Does the theme depend on a base theme, or is full ownership of native templates explicit and intentional?
  • [ ] Does every block ID in store/blocks.json resolve to a real interfaces.json entry in an installed app at a matching major?
  • [ ] Does every entry in store/routes.json have a valid path, context, and matching template in store/blocks.json?
  • [ ] Are merchant-editable copy and image fields exposed through contentSchemas.json rather than hardcoded in props?
  • [ ] Are React components kept out of the theme app and in dedicated component apps?
  • [ ] Has the team considered whether a planned change would force a major version bump on this content-holding app?

Related skills

  • `vtex-io-storefront-theme-versioning` — Use when the question is how to safely change which version of this theme app is installed on master.
  • `vtex-io-render-runtime-and-blocks` — Use when the question is how a block ID becomes a React component, or how a component app should declare blocks for themes to consume.
  • `vtex-io-storefront-react` — Use when the question is the React implementation of a block, not its composition into pages.
  • `vtex-io-app-contract` — Use when the question is what the theme app's manifest contract should declare and how it interacts with base themes and component apps.

Reference

Related skills

This week in AI coding

Five minutes, every Monday - the tools, releases and tactics for developers.

unsubscribe anytime.