vtex-io-react-apps
Original:🇺🇸 English
Not Translated
Apply when building React components under react/ or configuring store blocks in store/ for VTEX IO apps. Covers interfaces.json, contentSchemas.json for Site Editor, VTEX Styleguide for admin apps, and css-handles for storefront styling. Use for creating custom storefront components, admin panels, pixel apps, or any frontend development within the VTEX IO react builder ecosystem.
4installs
Sourcevtex/skills
Added on
NPX Install
npx skill4agent add vtex/skills vtex-io-react-appsSKILL.md Content
Frontend React Components & Hooks
When this skill applies
Use this skill when building VTEX IO frontend apps using the builder — creating React components that integrate with Store Framework as theme blocks, configuring , setting up for Site Editor, and applying styling patterns.
reactinterfaces.jsoncontentSchemas.json- Creating custom storefront components (product displays, forms, banners)
- Building admin panel interfaces with VTEX Styleguide
- Registering components as Store Framework blocks
- Exposing component props in Site Editor via
contentSchemas.json - Applying for safe storefront styling
css-handles
Do not use this skill for:
- Backend service implementation (use instead)
vtex-io-service-apps - GraphQL schema and resolver development (use instead)
vtex-io-graphql-api - Manifest and builder configuration (use instead)
vtex-io-app-structure
Decision rules
- Every visible storefront element is a block. Blocks are declared in theme JSON and map to React components via interfaces.
- (in
interfaces.json) maps block names to React component files:/storeis the file name in"component"(without extension),/reactlists child blocks,"allowed"controls how children work ("composition"or"children")."blocks" - Each exported component MUST have a root-level file in that re-exports it. The builder resolves
/reactto"component": "ProductReviews".react/ProductReviews.tsx - For storefront components, use for styling (not inline styles, not global CSS).
vtex.css-handles - For admin components, use — the official VTEX Admin component library. No third-party UI libraries.
vtex.styleguide - Use in
contentSchemas.jsonto make component props editable in Site Editor (JSON Schema format)./store - Use and the
react-intlbuilder for i18n — never hardcode user-facing strings.messages - Fetch data via GraphQL queries (from
useQuery), never via direct API calls from the browser.react-apollo
Architecture:
text
Store Theme (JSON blocks)
└── declares "product-reviews" block with props
│
▼
interfaces.json → maps "product-reviews" to "ProductReviews" component
│
▼
react/ProductReviews.tsx → React component renders
│
├── useCssHandles() → CSS classes for styling
├── useQuery() → GraphQL data fetching
└── useProduct() / useOrderForm() → Store Framework context hooksHard constraints
Constraint: Declare Interfaces for All Storefront Blocks
Every React component that should be usable as a Store Framework block MUST have a corresponding entry in . Without the interface declaration, the block cannot be referenced in theme JSON files.
store/interfaces.jsonWhy this matters
The store builder resolves block names to React components through . If a component has no interface, it is invisible to Store Framework and will not render on the storefront.
interfaces.jsonDetection
If a React component in is intended for storefront use but has no matching entry in , warn the developer. The component will compile but never render.
/reactstore/interfaces.jsonCorrect
json
{
"product-reviews": {
"component": "ProductReviews",
"composition": "children",
"allowed": ["product-review-item"]
},
"product-review-item": {
"component": "ReviewItem"
}
}tsx
// react/ProductReviews.tsx
import ProductReviews from './components/ProductReviews'
export default ProductReviewsWrong
tsx
// react/ProductReviews.tsx exists but NO store/interfaces.json entry
// The component compiles fine but cannot be used in any theme.
// Adding <product-reviews /> in a theme JSON will produce:
// "Block 'product-reviews' not found"
import ProductReviews from './components/ProductReviews'
export default ProductReviewsConstraint: Use VTEX Styleguide for Admin UIs
Admin panel components (apps using the builder) MUST use VTEX Styleguide () for UI elements. You MUST NOT use third-party UI libraries like Material UI, Chakra UI, or Ant Design in admin apps.
adminvtex.styleguideWhy this matters
VTEX Admin has a consistent design language enforced by Styleguide. Third-party UI libraries produce inconsistent visuals, may conflict with the Admin's global CSS, and add unnecessary bundle size. Apps submitted to the VTEX App Store with non-Styleguide admin UIs will fail review.
Detection
If you see imports from , , , , or in an admin app, warn the developer to use instead.
@material-ui@chakra-ui/react@chakra-uiantd@ant-designvtex.styleguideCorrect
tsx
// react/admin/ReviewModeration.tsx
import React, { useState } from 'react'
import {
Layout,
PageHeader,
Table,
Button,
Tag,
Modal,
Input,
} from 'vtex.styleguide'
interface Review {
id: string
author: string
rating: number
text: string
status: 'pending' | 'approved' | 'rejected'
}
function ReviewModeration() {
const [reviews, setReviews] = useState<Review[]>([])
const [modalOpen, setModalOpen] = useState(false)
const tableSchema = {
properties: {
author: { title: 'Author', width: 200 },
rating: { title: 'Rating', width: 100 },
text: { title: 'Review Text' },
status: {
title: 'Status',
width: 150,
cellRenderer: ({ cellData }: { cellData: string }) => (
<Tag type={cellData === 'approved' ? 'success' : 'error'}>
{cellData}
</Tag>
),
},
},
}
return (
<Layout fullWidth pageHeader={<PageHeader title="Review Moderation" />}>
<Table
items={reviews}
schema={tableSchema}
density="medium"
/>
</Layout>
)
}
export default ReviewModerationWrong
tsx
// react/admin/ReviewModeration.tsx
import React from 'react'
import { DataGrid } from '@material-ui/data-grid'
import { Button } from '@material-ui/core'
// Material UI components will look inconsistent in the VTEX Admin,
// conflict with global styles, and inflate bundle size.
// This app will fail VTEX App Store review.
function ReviewModeration() {
return (
<div>
<DataGrid rows={[]} columns={[]} />
<Button variant="contained" color="primary">Approve</Button>
</div>
)
}Constraint: Export Components from react/ Root Level
Every Store Framework block component MUST have a root-level export file in the directory that matches the value in . The actual implementation can live in subdirectories, but the root file must exist.
/reactcomponentinterfaces.jsonWhy this matters
The react builder resolves components by looking for files at the root of . If declares , the builder looks for . Without this root export file, the component will not be found and the block will fail to render.
/reactinterfaces.json"component": "ProductReviews"react/ProductReviews.tsxDetection
If references a component name that does not have a matching file at the root of , STOP and create the export file.
interfaces.json/reactCorrect
tsx
// react/ProductReviews.tsx — root-level export file
import ProductReviews from './components/ProductReviews/index'
export default ProductReviewstsx
// react/components/ProductReviews/index.tsx — actual implementation
import React from 'react'
import { useCssHandles } from 'vtex.css-handles'
const CSS_HANDLES = ['container', 'title', 'list'] as const
interface Props {
title: string
maxReviews: number
}
function ProductReviews({ title, maxReviews }: Props) {
const handles = useCssHandles(CSS_HANDLES)
return (
<div className={handles.container}>
<h2 className={handles.title}>{title}</h2>
{/* ... */}
</div>
)
}
export default ProductReviewsWrong
text
react/components/ProductReviews/index.tsx exists but
react/ProductReviews.tsx does NOT exist.
The builder cannot find the component.
Error: "Could not find component ProductReviews"Preferred pattern
Create the React component inside a subdirectory:
tsx
// react/components/ProductReviews/index.tsx
import React, { useMemo } from 'react'
import { useQuery } from 'react-apollo'
import { useProduct } from 'vtex.product-context'
import { useCssHandles } from 'vtex.css-handles'
import GET_REVIEWS from '../../graphql/getReviews.graphql'
import ReviewItem from './ReviewItem'
const CSS_HANDLES = [
'reviewsContainer',
'reviewsTitle',
'reviewsList',
'averageRating',
'emptyState',
] as const
interface Props {
title?: string
showAverage?: boolean
maxReviews?: number
}
function ProductReviews({
title = 'Customer Reviews',
showAverage = true,
maxReviews = 10,
}: Props) {
const handles = useCssHandles(CSS_HANDLES)
const productContext = useProduct()
const productId = productContext?.product?.productId
const { data, loading, error } = useQuery(GET_REVIEWS, {
variables: { productId, limit: maxReviews },
skip: !productId,
})
const averageRating = useMemo(() => {
if (!data?.reviews?.length) return 0
const sum = data.reviews.reduce(
(acc: number, review: { rating: number }) => acc + review.rating,
0
)
return (sum / data.reviews.length).toFixed(1)
}, [data])
if (loading) return <div className={handles.reviewsContainer}>Loading...</div>
if (error) return null
return (
<div className={handles.reviewsContainer}>
<h2 className={handles.reviewsTitle}>{title}</h2>
{showAverage && data?.reviews?.length > 0 && (
<div className={handles.averageRating}>
Average: {averageRating} / 5
</div>
)}
{data?.reviews?.length === 0 ? (
<p className={handles.emptyState}>No reviews yet.</p>
) : (
<ul className={handles.reviewsList}>
{data.reviews.map((review: { id: string; author: string; rating: number; text: string }) => (
<ReviewItem key={review.id} review={review} />
))}
</ul>
)}
</div>
)
}
export default ProductReviewsRoot export file:
tsx
// react/ProductReviews.tsx
import ProductReviews from './components/ProductReviews'
export default ProductReviewsBlock interface:
json
{
"product-reviews": {
"component": "ProductReviews",
"composition": "children",
"allowed": ["product-review-form"],
"render": "client"
}
}Site Editor schema:
json
{
"definitions": {
"ProductReviews": {
"type": "object",
"properties": {
"title": {
"type": "string",
"title": "Section Title",
"description": "Title displayed above the reviews list",
"default": "Customer Reviews"
},
"showAverage": {
"type": "boolean",
"title": "Show average rating",
"default": true
},
"maxReviews": {
"type": "number",
"title": "Maximum reviews",
"default": 10,
"enum": [5, 10, 20, 50]
}
}
}
}
}Using the component in a Store Framework theme:
json
{
"store.product": {
"children": [
"product-images",
"product-name",
"product-price",
"buy-button",
"product-reviews"
]
},
"product-reviews": {
"props": {
"title": "What Our Customers Say",
"showAverage": true,
"maxReviews": 20
}
}
}Common failure modes
- Importing third-party UI libraries for admin apps: Using ,
@material-ui/core, or@chakra-ui/reactconflicts with VTEX Admin's global CSS, produces inconsistent visuals, and will fail App Store review. Useantdinstead.vtex.styleguide - Directly calling APIs from React components: Using or
fetch()exposes authentication tokens to the client and bypasses CORS restrictions. Use GraphQL queries that resolve server-side viaaxiosfromuseQuery.react-apollo - Hardcoded strings without i18n: Components with hardcoded strings only work in one language. Use the builder and
messagesfor internationalization.react-intl - Missing root-level export file: If references
interfaces.jsonbut"component": "ProductReviews"doesn't exist, the block silently fails to render.react/ProductReviews.tsx
Review checklist
- Does every storefront block have a matching entry in ?
store/interfaces.json - Does every component have a root-level export file in
interfaces.json?/react - Are admin apps using (no third-party UI libraries)?
vtex.styleguide - Are storefront components using for styling?
css-handles - Is data fetched via GraphQL (), not direct API calls?
useQuery - Are user-facing strings using and the
react-intlbuilder?messages - Is defined for Site Editor-editable props?
contentSchemas.json
Reference
- Developing Custom Storefront Components — Guide for building Store Framework components
- Interfaces — How interfaces map blocks to React components
- React Builder — React builder configuration and directory structure
- Making a Custom Component Available in Site Editor — contentSchemas.json and Site Editor integration
- Store Framework — Overview of the block-based storefront system
- Using Components — How to use native and custom components in themes
- VTEX Styleguide — Official component library for VTEX Admin UIs