vtex-io-storefront-theme-app

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

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
当您处理负责组装店铺前端的应用时,可使用本技能——即拥有页面树、自定义路由和商家Site Editor操作界面的主题应用。
  • 搭建
    vendor.store-theme
    或任何带有
    store
    构建器、可发布页面的应用
  • 添加或修改
    store/blocks.json
    store/blocks/
    目录下的模板专属文件
  • store/routes.json
    中添加或修改自定义路由
  • store.home
    store.product
    store.search
    store.custom
    或任何原生页面模板组合块树
  • 扩展或覆盖如
    vtex.store-theme
    这类基础主题
  • 判断某项修改应放在主题应用、组件应用还是应用设置中
请勿将本技能用于以下场景:
  • 在组件应用中注册新块(
    store/interfaces.json
    )——请使用
    vtex-io-render-runtime-and-blocks
  • 实现块背后的React组件——请使用
    vtex-io-storefront-react
  • master
    环境中修改主题应用的已安装版本——请使用
    vtex-io-storefront-theme-versioning
  • 本地化字符串——请使用
    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.
  • 主题应用是在
    manifest.json
    中声明了
    store
    构建器的常规VTEX IO应用。它几乎从不包含React代码;而是组合由组件应用和基础主题声明的块。
  • 主题应用在
    manifest.json#dependencies
    中声明其基础主题,通常为
    vtex.store-theme
    ,并继承基础主题提供的所有页面模板、块声明和默认内容。
  • store/blocks.json
    (或
    store/blocks/
    下的模板专属文件)通过引用块ID来定义每个页面模板的块树。块ID来自组件应用的
    store/interfaces.json
    ,并由声明应用的主版本限定作用域。
  • store/routes.json
    定义自定义店铺前端路由,并将其绑定到页面上下文(
    store.custom
    store.product
    store.search
    等)。原生路由(
    /
    /{slug}/p
    、搜索路由)来自基础主题,几乎无需重新声明。
  • store/contentSchemas.json
    声明块的Site Editor可编辑属性。商家对这些属性的修改由
    vtex.pages-graphql
    存储,存储键包含主题应用的主版本。
  • 店铺前端行为的修改有三个可选位置,请谨慎选择:
    1. 主题应用的
      store/
      目录下JSON文件——组合配置、路由、默认内容、允许的子块。发布后会立即影响所有购物者。
    2. 组件应用代码——块的React行为。按照组件应用自身的版本节奏发布。
    3. Site Editor——在主题默认内容之上的商家管理内容覆盖。由
      vtex.pages-graphql
      存储,作用域限定为声明应用的已安装主版本。
  • 优先选择扩展基础主题而非复刻它。复刻基础主题会将所有块、路由和模板的维护责任永久转移到您的应用,包括上游的错误修复。
  • 店铺前端页面是块的树形结构。叶子节点是组件块;分支节点是容器块(
    flex-layout.row
    flex-layout.col
    等)。在设计允许的范围内,尽量保持树形结构扁平化;过深的树会增加渲染和内容的开销。
  • vtex-io-storefront-theme-versioning
    的角度来看,主题应用是一个内容存储应用。其已安装的主版本是平台用于存储商家针对该应用声明的块所做的所有Site Editor修改的键的一部分。请将其版本协议视为面向商家,而非面向开发者。

Hard constraints

硬性约束

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

约束:主题应用必须声明
store
构建器和基础主题

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.
店铺前端主题应用必须在
manifest.json#builders
中声明
"store"
,并且必须依赖一个基础主题(通常为
vtex.store-theme
),除非它明确承担所有原生页面模板、块和路由的所有权。
重要性说明
如果没有
store
构建器,
store/
目录下的所有文件都不会被处理,主题对店铺前端没有任何贡献。如果没有基础主题,应用需要从头开始声明所有原生页面模板(
store.home
store.product
store.search
等)——包括永久承担上游维护工作。
检测方式
如果主题应用包含
store/blocks.json
store/routes.json
,但其
manifest
未在
builders
中声明
"store"
,请立即停止并添加该构建器。如果
manifest
dependencies
peerDependencies
中也未包含
vtex.store-theme
(或其他基础主题)且无明确理由,请立即停止并确认该应用是否有意承担所有原生模板的所有权。
正确示例
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"
  }
}
错误示例
json
{
  "vendor": "acme",
  "name": "store-theme",
  "version": "1.0.0",
  "builders": {
    "messages": "1.x"
  },
  "dependencies": {}
}
磁盘上存在
store/
文件,但平台会忽略它们,且主题不会继承任何内容。

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

约束:
store/blocks.json
中的块ID必须能解析为已安装应用中注册的块

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
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
// acme.product-widgets@0.x ships store/interfaces.json with:
{
  "acme-related-products": {
    "component": "RelatedProducts"
  }
}
Wrong
json
// 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.
store/blocks.json
(或模板专属文件)中引用的每个块ID必须在某个已安装应用的
interfaces.json
中声明,且该应用的主版本与渲染时平台解析的版本匹配。无法解析的块ID会在GraphQL层导致
Missing block
错误,进而破坏页面。
重要性说明
vtex.pages-graphql
会通过对照已安装应用查找
vendor.app@MAJOR.x:block-id
来解析块ID。如果声明应用未安装,或安装的主版本与商家内容创作时使用的版本不同,从解析器的角度来看该块不存在,页面将无法渲染。
检测方式
如果
store/blocks.json
引用了某个块ID,请验证
manifest.json#dependencies
中的某个应用是否在其依赖列表中指定的主版本范围内的
store/interfaces.json
中声明了该块。如果依赖是
acme.product-widgets@0.x
,则该块必须存在于该应用的
0.x
版本系列中,而非
5.x
版本系列。
正确示例
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
// acme.product-widgets@0.x 包含的 store/interfaces.json:
{
  "acme-related-products": {
    "component": "RelatedProducts"
  }
}
错误示例
json
// manifest.json 依赖 acme.product-widgets@0.x
// 但 store/blocks.json 引用了仅存在于 @5.x 版本中的块
{
  "store.product": {
    "children": ["acme-new-related-products"]
  }
}
渲染时解析器会返回
Missing block acme.product-widgets@0.x:acme-new-related-products
,页面加载失败。

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

约束:
store/routes.json
中的自定义路由必须绑定到真实的页面上下文和模板

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/InstitutionalPageContext"
  }
}
json
// store/blocks.json
{
  "store.custom#about": {
    "blocks": ["rich-text#about-body"]
  }
}
Wrong
json
// 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
.
store/routes.json
中的每个条目必须设置有效的
path
context
(或依赖原生页面ID如
store.product
的内置上下文),以及块树中存在的模板(页面ID本身,或
store/blocks.json
中声明的
store.custom#<id>
)。
重要性说明
没有可解析模板的路由条目无法渲染任何内容。上下文错误的路由条目(例如,将自定义企业页面标记为
store.product
)会执行错误的数据解析器,导致崩溃或返回空的产品数据。
检测方式
对于
store/routes.json
中的每个路由,请确认:(a)
path
不与VTEX原生路径冲突;(b) 绑定的页面ID在
store/blocks.json
中存在对应的键;(c)
context
与页面实际需要的数据匹配。
正确示例
json
// store/routes.json
{
  "store.custom#about": {
    "path": "/institucional/sobre",
    "context": "vtex.store-resources/InstitutionalPageContext"
  }
}
json
// store/blocks.json
{
  "store.custom#about": {
    "blocks": ["rich-text#about-body"]
  }
}
错误示例
json
// store/routes.json
{
  "store.custom#about": {
    "path": "/p/sobre"
  }
}
路径
/p/sobre
与原生PDP路由格式冲突,未声明
context
,且
store/blocks.json
中没有匹配的
store.custom#about
模板。

Preferred pattern

推荐模式

Recommended theme app structure:
text
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:
json
{
  "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:
json
// store/routes.json
{
  "store.custom#about": {
    "path": "/institucional/sobre",
    "context": "vtex.store-resources/InstitutionalPageContext"
  }
}
json
// 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:
json
// 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.
推荐的主题应用结构:
text
acme.store-theme/
├── manifest.json                     # store构建器、基础主题依赖
├── messages/                         # 本地化字符串(对应单独技能)
└── store/
    ├── blocks.json                   # 全局块声明
    ├── routes.json                   # 自定义路由
    ├── contentSchemas.json           # Site Editor可编辑属性
    └── blocks/                       # 按模板划分的块树
        ├── home.jsonc
        ├── product.jsonc
        ├── search.jsonc
        ├── category.jsonc
        └── custom/
            ├── about.jsonc
            └── stores.jsonc
推荐的
store.home
组合配置:
json
{
  "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"
    }
  }
}
推荐的自定义企业路由:
json
// store/routes.json
{
  "store.custom#about": {
    "path": "/institucional/sobre",
    "context": "vtex.store-resources/InstitutionalPageContext"
  }
}
json
// 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."
    }
  }
}
推荐的Site Editor可编辑界面配置:
json
// store/contentSchemas.json
{
  "rich-text#about-copy": {
    "type": "object",
    "properties": {
      "text": {
        "type": "string",
        "title": "Body copy",
        "widget": { "ui:widget": "textarea" }
      }
    }
  }
}
商家对
text
的修改会由
vtex.pages-graphql
存储在包含主题应用主版本的键下。关于主版本升级时这些内容的变化,以及如何使用
updateThemeIds
mutation进行迁移,请参考
vtex-io-storefront-theme-versioning

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.
  • 复刻
    vtex.store-theme
    而非依赖它,导致永久丢失所有上游块修复和路由新增内容。
  • store/blocks.json
    中引用的块ID存在于声明应用的其他主版本中,而非依赖范围允许的版本。
  • 在两个不同应用中声明相同的块ID,导致渲染时解析结果不确定。
  • 将Site Editor可编辑的文案直接硬编码在
    store/blocks.json
    props
    中,未使用
    contentSchemas.json
    ,导致商家无法修改。
  • store/routes.json
    中添加自定义路由,但未在
    store/blocks.json
    中添加匹配的
    store.custom#<id>
    模板。
  • 将主题应用的版本视为面向开发者,为了“整理”而升级主版本,导致新版本没有商家内容,必须在发布前使用
    vtex.pages-graphql@2.x
    中的
    updateThemeIds
    进行迁移(参考
    vtex-io-storefront-theme-versioning
    )。
  • 将React组件代码放在主题应用中,而非专用的组件应用,导致块声明与块使用混合,增加复用难度。
  • 将运营或购物者特定数据存储在主题的
    store/
    文件中。主题是全局的;针对特定购物者或细分群体的数据应存储在其他位置。

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?
  • manifest.json
    是否声明了
    store
    构建器?
  • 主题是否依赖基础主题,或者是否明确有意承担所有原生模板的所有权?
  • store/blocks.json
    中的每个块ID是否能解析为已安装应用中对应主版本的
    interfaces.json
    条目?
  • store/routes.json
    中的每个条目是否有有效的
    path
    context
    ,以及
    store/blocks.json
    中匹配的模板?
  • 商家可编辑的文案和图片字段是否通过
    contentSchemas.json
    暴露,而非硬编码在
    props
    中?
  • React组件是否被排除在主题应用之外,放在专用的组件应用中?
  • 团队是否考虑过计划中的修改是否会强制这个内容存储应用升级主版本?

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.
  • vtex-io-storefront-theme-versioning
    — 当您需要了解如何安全修改
    master
    环境中已安装的主题应用版本时使用。
  • vtex-io-render-runtime-and-blocks
    — 当您需要了解块ID如何转换为React组件,或组件应用应如何声明供主题使用的块时使用。
  • vtex-io-storefront-react
    — 当您需要了解块的React实现而非其页面组合方式时使用。
  • vtex-io-app-contract
    — 当您需要了解主题应用的
    manifest
    协议应声明哪些内容,以及它如何与基础主题和组件应用交互时使用。

Reference

参考资料