post-purchase-ui-extension

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Post-Purchase UI Extension

售后UI扩展

Component catalog, lifecycle contract, and sandbox rules for
@shopify/post-purchase-ui-extensions-react
(post-purchase upsell surface, npm
0.13.5
, package in maintenance — no newer version exists; the modern
<s-*>
checkout-extensions SDK has no post-purchase target as of writing).
@shopify/post-purchase-ui-extensions-react
的组件目录、生命周期约定和沙箱规则(售后追加销售界面,npm版本
0.13.5
,该包处于维护状态——无更新版本;撰写本文时,现代
<s-*>
结账扩展SDK尚未支持售后追加销售场景)。

⚠️ MANDATORY: Validate with tsc (do not skip)

⚠️ 强制要求:使用tsc验证(请勿跳过)

Run after writing or editing any post-purchase JSX:
bash
cd extensions/<your-extension>
npx tsc --noEmit
TypeScript resolves types automatically via the bundled
.d.ts
at
node_modules/@shopify/post-purchase-ui-extensions-react/build/ts/index.d.ts
. If types fail twice on the same artifact, stop and surface the error to the user.
NEVER call
validate_component_codeblocks
,
validate_graphql_codeblocks
, or
search_docs_chunks
for this SDK.
The Shopify Dev MCP doesn't index
@shopify/post-purchase-ui-extensions-react
. The validator's
polaris-checkout-extensions
enum value covers the modern
@shopify/ui-extensions
web-component SDK only and rejects every post-purchase component (
BlockStack
,
Button
, …) as "not a Polaris web component."
编写或编辑任何售后JSX代码后,请运行:
bash
cd extensions/<your-extension>
npx tsc --noEmit
TypeScript会通过
node_modules/@shopify/post-purchase-ui-extensions-react/build/ts/index.d.ts
中的捆绑
.d.ts
文件自动解析类型。如果同一代码片段两次类型验证失败,请停止操作并向用户展示错误信息。
请勿为此SDK调用
validate_component_codeblocks
validate_graphql_codeblocks
search_docs_chunks
Shopify Dev MCP未索引
@shopify/post-purchase-ui-extensions-react
。验证器的
polaris-checkout-extensions
枚举值仅适用于现代
@shopify/ui-extensions
Web组件SDK,会将所有售后组件(
BlockStack
Button
等)判定为“非Polaris Web组件”而拒绝。

⚠️ Skip if a different surface

⚠️ 若为其他场景请跳过

  • Admin App Home
    <s-*>
    markup
    → use
    shopify-polaris-app-home
    instead.
  • Modern checkout extensions (
    @shopify/ui-extensions-react
    ,
    <s-*>
    web components) → use
    shopify-polaris-checkout-extensions
    instead.
  • Customer account extensions → use
    shopify-polaris-customer-account-extensions
    instead.
  • Admin App Home
    <s-*>
    标记
    → 改用
    shopify-polaris-app-home
  • 现代结账扩展
    @shopify/ui-extensions-react
    <s-*>
    Web组件)→ 改用
    shopify-polaris-checkout-extensions
  • 客户账户扩展 → 改用
    shopify-polaris-customer-account-extensions

Doc lookup (WebFetch only)

文档查询(仅支持WebFetch)

The MCP doesn't index this SDK — use WebFetch:
  • Component props:
    https://shopify.dev/docs/api/checkout-extensions/post-purchase/components/<name>
    (lowercase — PascalCase URLs return 404, e.g.
    /components/blockstack
    works,
    /components/BlockStack
    does not)
  • Lifecycle,
    useExtensionInput
    ,
    Changeset
    ,
    InputData
    ,
    ChangesetErrorCode
    :
    https://shopify.dev/docs/api/checkout-extensions/post-purchase/api
  • End-to-end tutorials:
    https://shopify.dev/docs/apps/build/checkout/product-offers/build-a-post-purchase-offer
    and
    https://shopify.dev/docs/apps/build/checkout/product-offers/create-a-post-purchase-subscription
  • UX guidance (only when working on copy, layout, or offer framing — not for API/prop questions):
    https://shopify.dev/docs/apps/build/checkout/product-offers/ux-for-post-purchase-product-offers
    and
    https://shopify.dev/docs/apps/build/checkout/product-offers/ux-for-post-purchase-subscriptions
If a component, prop, lifecycle field, or error code is missing from the Component Catalog or Lifecycle Contract below, WebFetch the canonical reference.
MCP未索引此SDK,请使用WebFetch:
  • 组件属性
    https://shopify.dev/docs/api/checkout-extensions/post-purchase/components/<name>
    (需小写——大驼峰URL会返回404,例如
    /components/blockstack
    可用,
    /components/BlockStack
    不可用)
  • 生命周期、
    useExtensionInput
    Changeset
    InputData
    ChangesetErrorCode
    https://shopify.dev/docs/api/checkout-extensions/post-purchase/api
  • 端到端教程
    https://shopify.dev/docs/apps/build/checkout/product-offers/build-a-post-purchase-offer
    https://shopify.dev/docs/apps/build/checkout/product-offers/create-a-post-purchase-subscription
  • UX指南(仅在处理文案、布局或优惠框架时使用——不适用于API/属性问题):
    https://shopify.dev/docs/apps/build/checkout/product-offers/ux-for-post-purchase-product-offers
    https://shopify.dev/docs/apps/build/checkout/product-offers/ux-for-post-purchase-subscriptions
如果组件、属性、生命周期字段或错误代码未在下方的【组件目录】或【生命周期约定】中列出,请通过WebFetch查询官方参考文档。

Rules

规则

  • Two extension points, two phases.
    extend("Checkout::PostPurchase::ShouldRender", …)
    is a data-prefetch hook;
    render("Checkout::PostPurchase::Render", App)
    is the React mount. Render runs only if ShouldRender returned
    { render: true }
    . See Lifecycle Contract.
  • storage.update(data)
    in ShouldRender;
    storage.initialData
    in Render.
    Storage is the only hand-off between the two phases — they run in separate JS contexts.
  • applyChangeset
    takes a signed JWT string, not a Changeset object.
    Sign the changeset on your backend with the app's API secret, return the token to the extension, then call
    await applyChangeset(token)
    . Never sign client-side.
  • Always call
    done()
    , including on error paths.
    Documented behavior:
    done()
    "indicates that the extension has finished running" and "redirects customers to the Order status page." Build-guide code samples call it in both accept and decline branches. Operational rule (not stated in shopify.dev): if an accept handler throws or rejects before
    done()
    runs, the buyer is stuck on a blank screen — wrap accept handlers in try/finally to guarantee the call.
  • Treat
    ShouldRender
    as potentially called more than once per checkout.
    The shopify.dev pages do not document call frequency. Operational observation in production: it can fire on payment-page load and again after the buyer clicks Pay. Make the handler idempotent (backend dedupe of identical fetches keyed by
    referenceId
    ).
  • Sandbox: no DOM, no CSS, no
    window
    , no external scripts.
    All visual customization happens through component props. There is no
    <style>
    , no
    className
    , no inline
    style={…}
    . Spacing comes from prop tokens — but the scale differs per component (see Spacing scales).
  • .jsx
    /
    .js
    extension required in every import.
    The Shopify CLI bundler does not auto-resolve.
    import { X } from "./foo"
    fails;
    import { X } from "./foo.jsx"
    works.
  • Only import what the SDK re-exports from
    "react"
    .
    The runtime bundles its own React — importing additional React entry points causes duplicate-React errors.
    useState
    ,
    useEffect
    , etc. work because they pass through.
  • Validate with
    tsc
    , not the MCP.
    See MANDATORY block above.
  • 两个扩展点,两个阶段
    extend("Checkout::PostPurchase::ShouldRender", …)
    是数据预获取钩子;
    render("Checkout::PostPurchase::Render", App)
    是React挂载方法。仅当ShouldRender返回
    { render: true }
    时,Render才会执行。详见【生命周期约定】。
  • 在ShouldRender中使用
    storage.update(data)
    ;在Render中使用
    storage.initialData
    。Storage是两个阶段之间唯一的数据传递方式——它们运行在独立的JS上下文环境中。
  • applyChangeset
    接受签名后的JWT字符串,而非Changeset对象
    。在后端使用应用的API密钥对changeset进行签名,将令牌返回给扩展,然后调用
    await applyChangeset(token)
    。切勿在客户端进行签名操作。
  • 始终调用
    done()
    ,包括错误路径
    。文档说明:
    done()
    表示“扩展已完成运行”并“将客户重定向到订单状态页面”。构建指南中的代码示例在接受和拒绝分支中均调用了该方法。操作规则(shopify.dev未明确说明):如果接受处理程序在调用
    done()
    之前抛出错误或拒绝,买家会停留在空白页面——请将接受处理程序包裹在try/finally中以确保调用
    done()
  • 将ShouldRender视为可能在每次结账时被多次调用。shopify.dev页面未记录调用频率。生产环境中的操作观察:它可能在支付页面加载时触发,也可能在买家点击“支付”后再次触发。请确保处理程序具有幂等性(后端通过
    referenceId
    对相同请求进行去重)。
  • 沙箱限制:无DOM、无CSS、无
    window
    、无外部脚本
    。所有视觉自定义均通过组件属性实现。不支持
    <style>
    className
    或内联
    style={…}
    。间距通过属性令牌设置——但不同组件的间距尺度不同(详见【间距尺度】)。
  • 每个导入必须使用
    .jsx
    /
    .js
    扩展名
    。Shopify CLI打包器不会自动解析扩展名。
    import { X } from "./foo"
    会失败;
    import { X } from "./foo.jsx"
    可正常工作。
  • 仅导入SDK从
    react
    中重新导出的内容
    。运行时会捆绑自身的React——导入额外的React入口点会导致重复React错误。
    useState
    useEffect
    等可正常使用,因为它们是通过SDK传递的。
  • 使用tsc而非MCP进行验证。详见上方【强制要求:使用tsc验证(请勿跳过)】部分。

Common Patterns

常见模式

Generic SDK patterns. Repo-specific architecture (layouts/templates/components, normalize functions, config/token systems) lives in the consuming repo's
architecture.md
, not here.
通用SDK模式。仓库特定的架构(布局/模板/组件、规范化函数、配置/令牌系统)位于消费仓库的
architecture.md
中,不在本文档范围内。

Boilerplate entry point

样板文件入口

The two-phase contract — every post-purchase extension starts with this skeleton.
jsx
import { extend, render } from "@shopify/post-purchase-ui-extensions-react";

extend("Checkout::PostPurchase::ShouldRender", async ({ inputData, storage }) => {
  const data = await fetchOffer(inputData);    // your backend
  if (!data) return { render: false };
  await storage.update(data);
  return { render: true };
});

render("Checkout::PostPurchase::Render", App);

function App({ storage, applyChangeset, done }) {
  const offer = storage.initialData;
  return <BlockStack spacing="loose">{/* … */}</BlockStack>;
}
两阶段约定——所有售后扩展均以此框架开始。
jsx
import { extend, render } from "@shopify/post-purchase-ui-extensions-react";

extend("Checkout::PostPurchase::ShouldRender", async ({ inputData, storage }) => {
  const data = await fetchOffer(inputData);    // 你的后端接口
  if (!data) return { render: false };
  await storage.update(data);
  return { render: true };
});

render("Checkout::PostPurchase::Render", App);

function App({ storage, applyChangeset, done }) {
  const offer = storage.initialData;
  return <BlockStack spacing="loose">{/* … */}</BlockStack>;
}

Loading state on accept Button

接受按钮的加载状态

Button
has a built-in
loading
prop — flip it on press to disable double-clicks during the sign-changeset round trip. No reset needed;
done()
navigates away.
jsx
const [loading, setLoading] = useState(false);
<Button
  loading={loading}
  loadingLabel="Processing"
  onPress={async () => {
    setLoading(true);
    const token = await signChangeset(variantId);
    await applyChangeset(token);
    done();
  }}
>
  Add to order
</Button>
Button
内置
loading
属性——点击时切换该属性,以在签名changeset的往返过程中禁用双击。无需重置;
done()
会导航离开当前页面。
jsx
const [loading, setLoading] = useState(false);
<Button
  loading={loading}
  loadingLabel="Processing"
  onPress={async () => {
    setLoading(true);
    const token = await signChangeset(variantId);
    await applyChangeset(token);
    done();
  }}
>
  添加到订单
</Button>

Image with locked aspect ratio

锁定宽高比的图片

Use
aspectRatio
+
fit="cover"
to prevent layout shift and align cards in a
Tiles
grid when source images have varying intrinsic ratios.
jsx
<Image source={url} description={alt} aspectRatio={1} fit="cover" />
使用
aspectRatio
+
fit="cover"
防止布局偏移,并在
Tiles
网格中对齐卡片,避免源图片固有比例不同导致的布局问题。
jsx
<Image source={url} description={alt} aspectRatio={1} fit="cover" />

Heading semantics via HeadingGroup

通过HeadingGroup实现标题语义化

Heading levels are derived from
HeadingGroup
nesting depth — never set
level
manually unless you need to override visuals.
jsx
<HeadingGroup>
  <Heading>Section title</Heading>
  <HeadingGroup>
    <Heading>Subsection title</Heading>
  </HeadingGroup>
</HeadingGroup>

标题级别由
HeadingGroup
的嵌套深度决定——除非需要覆盖视觉样式,否则请勿手动设置
level
jsx
<HeadingGroup>
  <Heading>章节标题</Heading>
  <HeadingGroup>
    <Heading>子章节标题</Heading>
  </HeadingGroup>
</HeadingGroup>

Lifecycle Contract

生命周期约定

Extension points

扩展点

PointStringPurpose
ShouldRender
Checkout::PostPurchase::ShouldRender
Data prefetch. Decide whether to render.
Render
Checkout::PostPurchase::Render
Mount the React tree.
名称字符串标识用途
ShouldRender
Checkout::PostPurchase::ShouldRender
数据预获取。决定是否渲染扩展。
Render
Checkout::PostPurchase::Render
挂载React树。

ShouldRender API

ShouldRender API

ts
(api: PostPurchaseShouldRenderApi) => { render: boolean } | Promise<{ render: boolean }>
api
exposes
inputData: InputData
,
storage
, plus
version
/
locale
/
extensionPoint
from the standard surface.
  • storage.update(data: any): Promise<void>
    — persist data for the Render phase. Returns a Promise —
    await
    it before returning.
  • Return
    { render: true }
    to mount,
    { render: false }
    to skip silently. May return synchronously or as a
    Promise
    .
ts
(api: PostPurchaseShouldRenderApi) => { render: boolean } | Promise<{ render: boolean }>
api
暴露
inputData: InputData
storage
,以及标准界面提供的
version
/
locale
/
extensionPoint
  • storage.update(data: any): Promise<void>
    — 为Render阶段持久化数据。返回Promise——返回前需使用
    await
  • 返回
    { render: true }
    以挂载扩展,返回
    { render: false }
    则静默跳过。可同步返回或返回
    Promise

Render API

Render API

ts
render("Checkout::PostPurchase::Render", (api: PostPurchaseRenderApi) => ReactElement)
FieldTypeUse
inputData
InputData
Same shape as ShouldRender.
storage.initialData
unknown
Data written by ShouldRender via
storage.update
. Read-only.
calculateChangeset
(changeset: Readonly<Changeset> | string) => Promise<CalculateChangesetResult>
Preview cost impact without applying. Pass either a raw
Changeset
object or the signed JWT string.
applyChangeset
(changeset: string, options?: ApplyChangesetOptions) => Promise<ApplyChangesetResult>
Apply the order edit and charge the buyer. The
changeset
parameter is a JWT string signed by your backend with the app secret — despite the name, this overload does not accept a raw object.
done
() => Promise<void>
Navigate to thank-you page. Always call this, success or error.
version
/
locale
/
extensionPoint
from
StandardApi
Available alongside
inputData
.
ts
render("Checkout::PostPurchase::Render", (api: PostPurchaseRenderApi) => ReactElement)
字段类型用途
inputData
InputData
与ShouldRender中的结构相同。
storage.initialData
unknown
ShouldRender阶段通过
storage.update
写入的数据。只读。
calculateChangeset
`(changeset: Readonly<Changeset> \string) => Promise<CalculateChangesetResult>`
applyChangeset
(changeset: string, options?: ApplyChangesetOptions) => Promise<ApplyChangesetResult>
应用订单修改并向买家收费。
changeset
参数是后端使用应用密钥签名后的JWT字符串——尽管名称如此,此重载不接受原始对象。
done
() => Promise<void>
导航到感谢页。无论成功或失败,都必须调用此方法。
version
/
locale
/
extensionPoint
来自
StandardApi
inputData
一同可用。

InputData

InputData

FieldType
extensionPoint
string
initialPurchase
Purchase
(referenceId, customerId?, destinationCountryCode?, totalPriceSet, lineItems[])
locale
string
shop
Shop
(id: number, domain, metafields)
token
string
(JWT — pass to your backend for verification)
version
string
(current value:
'unstable'
)
LineItem
:
product
,
quantity
,
totalPriceSet
,
sellingPlanId?
.
Product
:
id
,
title
,
variant
,
metafields
.
Metafield.value
is
string | number
;
valueType
is
'integer' | 'string' | 'json_string'
.
字段类型
extensionPoint
string
initialPurchase
Purchase
(包含referenceId、customerId?、destinationCountryCode?、totalPriceSet、lineItems[])
locale
string
shop
Shop
(包含id: number、domain、metafields)
token
string
(JWT——需传递到后端进行验证)
version
string
(当前值:
'unstable'
LineItem
:包含
product
quantity
totalPriceSet
sellingPlanId?
Product
:包含
id
title
variant
metafields
Metafield.value
类型为
string | number
valueType
'integer' | 'string' | 'json_string'

Changeset shape

Changeset结构

ts
Changeset { changes: Changes }
Changes = (AddVariantChange | AddShippingLineChange | SetMetafieldChange | AddSubscriptionChange)[]
ts
Changeset { changes: Changes }
Changes = (AddVariantChange | AddShippingLineChange | SetMetafieldChange | AddSubscriptionChange)[]

ApplyChangesetOptions

ApplyChangesetOptions

OptionTypeUse
buyerConsentToSubscriptions?
boolean
Set when changes include
add_subscription
; pair with
BuyerConsent
component. Optional in the type, but required by the server for subscription changes.
选项类型用途
buyerConsentToSubscriptions?
boolean
当修改包含
add_subscription
时设置;需与
BuyerConsent
组件配合使用。类型中标记为可选,但服务器要求订阅修改必须提供此参数。

ChangesetErrorCode

ChangesetErrorCode

payment_required
·
insufficient_inventory
·
changeset_already_applied
·
unsupported_payment_method
·
invalid_request
·
server_error
·
buyer_consent_required
·
subscription_vaulting_error
·
subscription_contract_creation_error
·
subscription_no_shipping_address_error
·
subscription_limit_error
·
order_released_error

payment_required
·
insufficient_inventory
·
changeset_already_applied
·
unsupported_payment_method
·
invalid_request
·
server_error
·
buyer_consent_required
·
subscription_vaulting_error
·
subscription_contract_creation_error
·
subscription_no_shipping_address_error
·
subscription_limit_error
·
order_released_error

Component Catalog

组件目录

29 components total, served by 28 doc URLs under
/components/<name>
(lowercase).
FormLayoutGroup
shares the
formlayout
page rather than having its own URL — when looking it up, fetch
/components/formlayout
.
All importable from
@shopify/post-purchase-ui-extensions-react
.
共29个组件,对应
/components/<name>
(小写)下的28个文档URL。
FormLayoutGroup
共享
formlayout
页面,无独立URL——查询时请获取
/components/formlayout
所有组件均可从
@shopify/post-purchase-ui-extensions-react
导入。

Spacing scales

间距尺度

There is no single "spacing scale" — three different scales coexist. Match the literal exactly to the consumer's
.d.ts
:
ScaleAllowed valuesUsed by
Stack scale
'xtight' | 'tight' | 'loose' | 'xloose'
BlockStack.spacing
,
InlineStack.spacing
,
Bookend.spacing
Stack scale +
none
'none' | 'xtight' | 'tight' | 'loose' | 'xloose'
Tiles.spacing
Compact scale
'none' | 'tight' | 'loose'
TextContainer.spacing
,
CalloutBanner.spacing
View padding scale
'extraTight' | 'tight' | 'base' | 'loose' | 'extraLoose'
View.inlinePadding
,
View.blockPadding
xtight
/
xloose
and
extraTight
/
extraLoose
are NOT interchangeable — each is rejected by the component that doesn't list it.
没有统一的“间距尺度”——存在三种不同的尺度。请严格匹配消费者
.d.ts
文件中的字面量:
尺度允许值适用组件
Stack尺度`'xtight' \'tight' \
Stack尺度 +
none
`'none' \'xtight' \
Compact尺度`'none' \'tight' \
View内边距尺度`'extraTight' \'tight' \
xtight
/
xloose
extraTight
/
extraLoose
不可互换——未列出该值的组件会拒绝使用。

Layout & Structure

布局与结构

ComponentPurposeKey Props / Gotchas
BlockStack
Vertical stack
spacing
: stack scale (no
none
).
alignment
:
'leading' | 'center' | 'trailing'
.
InlineStack
Horizontal row
spacing
: stack scale.
alignment
:
'leading' | 'center' | 'trailing' | 'baseline'
.
Bookend
Pin first/last child to intrinsic size, fill middle
leading?: boolean
,
trailing?: boolean
.
spacing
: stack scale.
alignment
:
'leading' | 'center' | 'trailing' | 'baseline'
.
Tiles
Equal-size grid, wraps and stacks responsively
maxPerLine?: number
.
breakAt?: number
(px width below which tiles stack).
spacing
: stack scale +
'none'
.
alignment
:
'leading' | 'center' | 'trailing' | 'baseline'
. Direct children stretch — wrap a child in
View
to keep its intrinsic size.
Layout
Multi-section page scaffold with media-queried sizes
maxInlineSize?: number
(≤1 = %, >1 = px).
sizes?: Size[]
where
Size = 'auto' | 'fill' | number
.
media?: Media[]
where
Media = { viewportSize: 'small' | 'medium' | 'large'; maxInlineSize?: number; sizes?: Size[] }
.
inlineAlignment?: 'leading' | 'trailing'
.
blockAlignment?: 'center' | 'trailing'
. No
spacing
prop exists
on
LayoutProps
despite appearing in some doc code samples — using it is a TS error.
View
Generic container that does NOT stretch
inlinePadding
/
blockPadding
: View padding scale (
'extraTight' | 'tight' | 'base' | 'loose' | 'extraLoose'
). Note camelCase — NOT
xtight
/
xloose
. Use to opt out of
Tiles
/
Layout
stretching.
Separator
Visual divider
direction
:
'horizontal'
(default) /
'vertical'
.
width
:
'thin' | 'medium' | 'thick' | 'xthick'
.
组件用途关键属性 / 注意事项
BlockStack
垂直堆叠容器
spacing
:Stack尺度(无
none
)。
alignment
:`'leading' \
InlineStack
水平行容器
spacing
:Stack尺度。
alignment
:`'leading' \
Bookend
将首尾子元素固定为固有尺寸,中间子元素填充剩余空间
leading?: boolean
trailing?: boolean
spacing
:Stack尺度。
alignment
:`'leading' \
Tiles
等尺寸网格,可响应式换行堆叠
maxPerLine?: number
breakAt?: number
(px宽度,低于此值时卡片堆叠)。
spacing
:Stack尺度 +
'none'
alignment
:`'leading' \
Layout
多章节页面脚手架,支持媒体查询尺寸
maxInlineSize?: number
(≤1为百分比,>1为px)。
sizes?: Size[]
,其中 `Size = 'auto' \
View
通用容器,不会自动拉伸
inlinePadding
/
blockPadding
:View内边距尺度(`'extraTight' \
Separator
视觉分隔线
direction
'horizontal'
(默认)/
'vertical'
width
:`'thin' \

Typography

排版

ComponentPurposeKey Props / Gotchas
Heading
Section title
level?: 1 | 2 | 3
— visual override only; semantic level comes from
HeadingGroup
nesting.
role?: 'presentation'
strips semantics, keeps styling.
HeadingGroup
Increments heading level for nested childrenNo props. Wrap children that contain their own
Heading
to bump them down a level semantically.
Text
Inline styled text
size?: 'small' | 'medium' | 'large' | 'xlarge'
.
emphasized?: boolean
,
subdued?: boolean
.
id?: string
.
appearance?: 'critical' | 'warning' | 'success'
.
role
: string
'address'
or
'deletion'
(use
deletion
for strikethrough on original prices), or an object:
{ type: 'abbreviation'; for?: string }
,
{ type: 'directional-override'; direction: 'ltr' | 'rtl' }
(direction is required),
{ type: 'datetime'; machineReadable?: string }
. Inline only — wrap in
TextBlock
or a stack to break to a new line.
TextBlock
Block-level paragraph
size?: 'small' | 'medium' | 'large' | 'xlarge'
.
emphasized?: boolean
,
subdued?: boolean
.
id?: string
.
appearance?: 'critical' | 'warning' | 'success'
. No
role
prop.
TextContainer
Vertical spacing wrapper for text elements
spacing?: 'none' | 'tight' | 'loose'
(compact scale —
xtight
/
xloose
are NOT accepted here).
alignment?: 'leading' | 'center' | 'trailing'
.
组件用途关键属性 / 注意事项
Heading
章节标题`level?: 1 \
HeadingGroup
为嵌套子元素递增标题级别无属性。包裹包含自身
Heading
的子元素,使其语义级别降低一级。
Text
内联样式文本`size?: 'small' \
TextBlock
块级段落`size?: 'small' \
TextContainer
文本元素的垂直间距包装器`spacing?: 'none' \

Actions

操作组件

ComponentPurposeKey Props / Gotchas
Button
Primary action
onPress?(): void
(optional in the type — provide if not using
submit
or
to
).
submit?: boolean
(form submit).
to?: string
(renders as Link).
subdued?: boolean
(secondary look),
plain?: boolean
(link-styled).
loading?: boolean
+
loadingLabel?: string
.
disabled?: boolean
. No
variant
/
tone
props
— emphasis is via
subdued
/
plain
.
ButtonGroup
Inline-stacked buttons with auto-spacingNo props. Wraps two or more
Button
s.
Link
Navigation
to?: string
and/or
onPress?(): void
— provide at least one.
external?: boolean
opens in new tab.
id?: string
(target for accessibility-label associations). Not a button — use
Button
for actions.
组件用途关键属性 / 注意事项
Button
主要操作按钮
onPress?(): void
(类型中标记为可选——若未使用
submit
to
,则需提供)。
submit?: boolean
(表单提交)。
to?: string
(渲染为链接)。
subdued?: boolean
(次要样式)、
plain?: boolean
(链接样式)。
loading?: boolean
+
loadingLabel?: string
disabled?: boolean
variant
/
tone
属性
——通过
subdued
/
plain
设置强调级别。
ButtonGroup
内联堆叠按钮,自动设置间距无属性。包裹两个或多个
Button
Link
导航链接
to?: string
和/或
onPress?(): void
— 至少提供一个。
external?: boolean
在新标签页打开。
id?: string
(用于无障碍标签关联的目标)。不是按钮——操作请使用
Button

Forms

表单组件

ComponentPurposeKey Props / Gotchas
Form
Form wrapper with implicit-submit-on-Enter
onSubmit(): void
required.
disabled?: boolean
.
implicitSubmit?: boolean | string
(string = a11y label for screen-reader-only submit button). No
<form>
HTTP submission — handle in
onSubmit
.
FormLayout
Vertical-stacked field layoutNo props. Children stack on the block axis.
FormLayoutGroup
Inline-grouped fields within a
FormLayout
No props. Fields appear side-by-side with equal spacing. Lives in the same
.d.ts
file as
FormLayout
.
TextField
Single-line input
label: string
(required, doubles as placeholder when empty).
value?: string
,
onChange?(value: string): void
(fires on commit/blur).
onInput?(value: string): void
(fires every keystroke — drive controlled state from
onInput
, not
onChange
).
type?: 'text' | 'email' | 'number' | 'telephone'
.
name?: string
(form key).
id?: string
.
required?: boolean
(semantic only; does not auto-error).
error?: string
.
multiline?: boolean
.
autocomplete?: Autocomplete | boolean
.
tooltip?: { label: string; content: string }
.
onFocus?(): void
,
onBlur?(): void
.
Select
Dropdown
label: string
(required).
options: { value: string; label: string; disabled?: boolean }[]
.
value?: string
,
onChange?(value: string): void
.
placeholder?: string
.
id?: string
,
name?: string
.
required?: boolean
.
disabled?: boolean
(on the Select itself, not just options).
error?: string
.
autocomplete?: Autocomplete | boolean
.
Checkbox
Boolean toggle
checked?: boolean
(preferred) or
value?: boolean
checked
takes precedence when both are set.
onChange?(value: boolean): void
.
disabled?: boolean
,
error?: string
.
id?: string
,
name?: string
,
accessibilityLabel?: string
.
Radio
Single radio button
name: string
(required — same
name
groups options).
checked?: boolean
/
value?: boolean
.
onChange?(value: boolean): void
.
disabled?: boolean
.
id?: string
,
accessibilityLabel?: string
.
BuyerConsent
Subscription consent checkbox
policy: 'subscriptions'
.
checked: boolean
(required),
onChange(value: boolean): void
(required — unlike other form components).
error?: string
. Required when applying an
add_subscription
change with
applyChangeset(token, { buyerConsentToSubscriptions: true })
.
组件用途关键属性 / 注意事项
Form
表单包装器,支持按Enter键隐式提交
onSubmit(): void
为必填项。
disabled?: boolean
。`implicitSubmit?: boolean \
FormLayout
垂直堆叠的字段布局无属性。子元素在块轴方向堆叠。
FormLayoutGroup
FormLayout
内的内联分组字段
无属性。字段并排显示,间距均等。与
FormLayout
位于同一
.d.ts
文件中。
TextField
单行输入框
label: string
(必填,为空时同时作为占位符)。
value?: string
onChange?(value: string): void
(提交/失焦时触发)。
onInput?(value: string): void
(每次按键时触发——受控状态需从
onInput
获取,而非
onChange
)。`type?: 'text' \
Select
下拉选择器
label: string
(必填)。
options: { value: string; label: string; disabled?: boolean }[]
value?: string
onChange?(value: string): void
placeholder?: string
id?: string
name?: string
required?: boolean
disabled?: boolean
(作用于Select本身,而非仅选项)。
error?: string
。`autocomplete?: Autocomplete \
Checkbox
布尔切换按钮
checked?: boolean
(优先使用)或
value?: boolean
— 同时设置时
checked
优先级更高。
onChange?(value: boolean): void
disabled?: boolean
error?: string
id?: string
name?: string
accessibilityLabel?: string
Radio
单选按钮
name: string
(必填——相同
name
为一组选项)。
checked?: boolean
/
value?: boolean
onChange?(value: boolean): void
disabled?: boolean
id?: string
accessibilityLabel?: string
BuyerConsent
订阅同意复选框
policy: 'subscriptions'
checked: boolean
(必填)、
onChange(value: boolean): void
(必填——与其他表单组件不同)。
error?: string
。当使用
applyChangeset(token, { buyerConsentToSubscriptions: true })
应用
add_subscription
修改时,此组件为必填项。

Feedback & Status

反馈与状态组件

ComponentPurposeKey Props / Gotchas
Banner
Status / system message
title?: string
.
status?: 'info' | 'success' | 'warning' | 'critical'
(default
'info'
).
collapsible?: boolean
,
iconHidden?: boolean
. For status reporting — not for promotional copy.
CalloutBanner
Promotional offer header
title?: string
.
background?: 'secondary' | 'transparent'
(default
'secondary'
).
border?: 'none' | 'block'
(default
'block'
).
alignment?: 'leading' | 'center' | 'trailing'
(default
'center'
).
spacing?: 'none' | 'tight' | 'loose'
(compact scale —
xtight
/
xloose
are NOT accepted; default
'tight'
). For limited-time-offer framing — distinct from
Banner
.
Spinner
Loading indicator
size?: 'small' | 'large'
.
color?: 'inherit'
. Children = a11y fallback for reduced-motion users.
组件用途关键属性 / 注意事项
Banner
状态/系统消息
title?: string
。`status?: 'info' \
CalloutBanner
促销优惠头部
title?: string
。`background?: 'secondary' \
Spinner
加载指示器`size?: 'small' \

Media

媒体组件

ComponentPurposeKey Props / Gotchas
Image
Responsive image
source: string
(required).
description?: string
(alt; default
''
).
sources?: { source: string; viewportSize?: 'small' | 'medium' | 'large'; resolution?: 1 | 1.3 | 1.5 | 2 | 2.6 | 3 | 3.5 | 4 }[]
for responsive variants —
resolution
is a constrained numeric literal union, not any number.
aspectRatio?: number
— sets height from width to prevent layout shift.
fit?: 'cover' | 'contain'
(pair with
aspectRatio
to avoid stretch).
loading?: 'eager' | 'lazy'
.
bordered?: boolean
,
decorative?: boolean
.
组件用途关键属性 / 注意事项
Image
响应式图片
source: string
(必填)。
description?: string
(替代文本;默认
''
)。`sources?: { source: string; viewportSize?: 'small' \

Accessibility

无障碍组件

ComponentPurposeKey Props / Gotchas
HiddenForAccessibility
Hide children from a11y tree but show visuallyNo props. Use for purely decorative or duplicated content.
VisuallyHidden
Hide visually but keep available to screen readersNo props. Use for screen-reader-only labels.
组件用途关键属性 / 注意事项
HiddenForAccessibility
从无障碍树中隐藏子元素,但保持视觉可见无属性。用于纯装饰或重复内容。
VisuallyHidden
视觉上隐藏,但对屏幕阅读器可见无属性。用于仅屏幕阅读器可见的标签。
",