wix-cli-site-component by wix/skills
npx skills add https://github.com/wix/skills --skill wix-cli-site-component为 Wix CLI 应用程序创建具备编辑器清单的生产级 React 站点组件。站点组件是与 Wix 编辑器集成的 React 组件,允许站点所有者通过可视化界面自定义内容、样式和行为。
站点组件由 四个必需文件 组成:
manifest.json)定义 React 组件与 Wix 生态系统之间的契约:
component.tsx)生产就绪的 React 函数式组件:
wix.elementsRemovalState 处理元素移除状态style.css)广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
具备响应式设计的现代 CSS:
box-sizing: border-boxtransition: all,禁止使用媒体查询(prefers-reduced-motion 除外)--display: [value] CSS 变量,然后在根元素上使用 display: var(--display)types.ts)严格类型定义:
在实现站点组件之前,您必须阅读 MANIFEST_GUIDELINES.md。 它包含完整的清单结构、所有数据类型、元素配置和必需的模式。
清单使用以下关键部分定义编辑器契约:
{
"installation": {
"staticContainer": "HOMEPAGE",
"initialSize": {
"width": { "sizingType": "pixels", "pixels": 400 },
"height": { "sizingType": "pixels", "pixels": 300 }
}
}
}
"HOMEPAGE"sizingType 选项定义初始尺寸:
"content" - 基于内容自动调整大小"stretched" - 填充可用空间"pixels" - 固定像素尺寸(需要 pixels 属性){
"selector": ".component-name",
"displayName": "Component Name",
"archetype": "Container",
"layout": {
"resizeDirection": "horizontalAndVertical",
"contentResizeDirection": "horizontal"
},
"cssProperties": {
"backgroundColor": {
"displayName": "Background Color",
"defaultValue": "#ffffff"
}
},
"data": {
"columns": {
"dataType": "number",
"displayName": "Number of Columns",
"number": { "minimum": 1, "maximum": 4 }
}
},
"elements": {
"title": {
"elementType": "inlineElement",
"inlineElement": {
"selector": ".component-name__title",
"displayName": "Title",
"data": {
"titleText": {
"dataType": "text",
"displayName": "Title Text"
}
},
"behaviors": {
"selectable": true,
"removable": true
}
}
}
}
}
| 类型 | 运行时值 | 使用场景 |
|---|---|---|
text | string | 名称、标题、描述 |
textEnum | string | 预定义选项 |
number | number | 数量、尺寸 |
booleanValue | boolean | 开关、标志 |
a11y | Object | 无障碍属性 |
link | { href, target, rel } | 导航链接 |
image | { uri, url, alt, width, height } | 图像 |
video | Video 对象 | 媒体内容 |
vectorArt | 经过清理的 SVG 对象 | 图标、图形 |
localDate | string (YYYY-MM-DD) | 日期值 |
localTime | string (hh:mm) | 时间值 |
webUrl | string | 外部 URL |
richText | string (HTML) | 格式化内容 |
arrayItems | Array | 集合、列表 |
direction | string | HTML dir 属性 |
menuItems | 菜单项数组 | 导航菜单 |
用于样式自定义的常见 CSS 属性:
display, gap, padding, margin, width, heightfont, fontSize, fontWeight, textAlign, colorbackgroundColor, backgroundImageborder, borderRadius, boxShadowalignItems, justifyContent, flexDirection完整的 CSS 属性参考: 查看 CSS_GUIDELINES.md 以获取所有 CSS 属性、变量模式和样式最佳实践。
完整参考: 查看 REACT_PATTERNS.md 以获取详细的组件架构、所有编码模式和实现示例。
interface ComponentProps {
// 标准 props(始终存在)
className: string;
id: string;
wix?: Wix;
// 组件级数据(来自 editorElement.data)
columns?: number;
layout?: string;
// 元素 props(来自 elements 定义)
elementProps?: {
title?: {
titleText?: string;
wix?: Wix;
};
button?: {
buttonText?: string;
buttonLink?: Link;
wix?: Wix;
};
};
}
将每个不同的 UI 元素提取为命名的子组件:
// 标题子组件
interface TitleProps {
titleText?: string;
className: string;
}
const Title: FC<TitleProps> = ({ titleText = "Default Title", className }) => (
<h2 className={className}>{titleText}</h2>
);
// 主组件
const ProductCard: FC<ProductCardProps> = ({
className,
id,
elementProps,
wix
}) => {
const removalState = wix?.elementsRemovalState || {};
return (
<div className={`product-card ${className}`} id={id}>
{!removalState['title'] && (
<Title
className="product-card__title"
{...elementProps?.title}
/>
)}
{!removalState['button'] && (
<Button
className="product-card__button"
{...elementProps?.button}
/>
)}
</div>
);
};
所有元素必须根据移除状态进行条件渲染:
const removalState = wix?.elementsRemovalState || {};
return (
<div className={`component ${className}`} id={id}>
{!removalState['elementKey'] && <Element />}
</div>
);
组件存在于不同视口内用户可调整大小的容器(300-1200px)中:
width: 100%; height: 100%clamp() 实现流体缩放.component {
--display: block;
--background-color: #ffffff;
--text-color: #333333;
display: var(--display);
background-color: var(--background-color);
color: var(--text-color);
pointer-events: auto;
}
关键:CSS 选择器必须与清单选择器和 React 的 className 完全匹配:
className="product-card__title".product-card__title { ... }"selector": ".product-card__title"完整参考: 查看 DESIGN_SYSTEM.md 以获取视觉设计原则、创意指南和美学最佳实践。
| 关系 | 值 | 使用场景 |
|---|---|---|
| 紧密(图标 + 标签) | 0.25-0.5rem (4-8px) | 聚集相关项目 |
| 同一类别 | 1-1.5rem (16-24px) | 卡片区域、表单字段 |
| 不同部分 | 2-3rem (32-48px) | 主要内容块 |
| 强调/戏剧性 | 4rem+ (64px+) | 英雄内容、奢华感 |
超越显而易见的解决方案:
每个不同的视觉部分都需要一个单独的清单元素:
editorElement.data - 仅用于组件范围的配置:
elements[key].data - 该特定元素的内容:
当组件需要默认图像时,请使用以下格式:
// 在组件中导入
import { heroImage } from './assets/defaultImages';
// 使用
<img src={heroImage} alt="Hero" />
资源规范格式:
<imageUrlName>
{ "description": "Modern cityscape at sunset", "width": 1920, "height": 1088 }
</imageUrlName>
规则:
'./assets/defaultImages' 作为命名导出导入src/extensions/site/components/
└── {component-name}/
├── manifest.json # 组件清单
├── component.tsx # React 组件
├── style.css # CSS 样式
├── types.ts # TypeScript 类型
└── assets/ # 可选资源
└── defaultImages.ts
完整工作示例: 查看 EXAMPLE.md 以获取包含所有模式的完整生产就绪站点组件,包括清单、React 组件、CSS 和类型。
请求: "创建一个包含图像、标题、价格和购买按钮的产品卡片组件"
输出:
请求: "构建一个包含背景图像、标题、副标题和 CTA 按钮的英雄区域"
输出:
请求: "创建一个具有可配置项目数量的功能组件"
输出:
扩展注册是强制性的,并且需要两个步骤。
每个站点组件在其文件夹中都需要一个 extensions.ts 文件:
import { extensions } from "@wix/astro/builders";
import manifest from "./manifest.json";
export const sitecomponentMyComponent = extensions.siteComponent({
...manifest,
id: "{{GENERATE_UUID}}",
description: "My Component",
type: "platform.MyComponent",
resources: {
client: {
component: "./extensions/site/components/my-component/component.tsx",
componentUrl: "./extensions/site/components/my-component/component.tsx",
},
},
});
关键:类型命名约定
type 字段使用格式 platform.{PascalCaseFolderName}:
my-component → type: "platform.MyComponent"product-card → type: "platform.ProductCard"hero-section → type: "platform.HeroSection"文件夹名称被转换为 PascalCase(连字符被移除,每个单词首字母大写)。
关键:UUID 生成
id 必须是一个唯一的、静态的 UUID v4 字符串。为每个扩展生成一个新的 UUID - 不要使用 randomUUID() 或从示例中复制 UUID。
关键: 创建组件特定的扩展文件后,您必须阅读 wix-cli-extension-registration 并按照"应用注册"部分更新 src/extensions.ts。
如果不完成步骤 2,站点组件将无法在 Wix 编辑器中使用。
完整参考: 查看 TYPESCRIPT_QUALITY.md 以获取全面的类型安全指南和代码质量标准。
any 类型@ts-ignore 或 @ts-expect-error 注释@typescript-eslint/no-unused-vars)img 的 src 不是 https://...(允许:本地导入、wixstatic.com、变量)window/document,在 useEffect/处理函数中保护浏览器 APIdangerouslySetInnerHTML 或内联 <style> 标签 - 对于动态值,使用 CSS 变量或内联 style propwindow.fetch (no-restricted-properties)exhaustive-deps:在 useEffect/useCallback 内部使用的来自组件作用域的所有值都必须在依赖数组中const/let(没有 var),没有未知的 JSX 属性| 错误 | 失败原因 | 修复方法 |
|---|---|---|
| CSS 选择器与清单不匹配 | 编辑器无法将样式应用于元素 | 确保清单 selector、React className 和 CSS 选择器完全相同 |
将内容文本放在 editorElement.data 中 | 内容属于特定元素,而非根元素 | 将文本/图像/链接数据移动到 elements[key].data 中 |
直接在根元素上使用 display: flex | 破坏编辑器覆盖机制 | 使用 --display: flex CSS 变量,然后在根元素上使用 display: var(--display) |
可选元素上缺少 removable: true | 站点所有者无法隐藏元素 | 为可选元素添加 behaviors: { removable: true } |
在模块作用域中使用 window/document | 构建期间 SSR 失败 | 在 useEffect 或事件处理函数内部保护浏览器 API |
从 @wix/design-system 导入 | 在站点组件中不可用 | 仅使用纯 HTML/CSS 或自定义组件 |
@wix/design-system 或 @wix/wix-ui-icons-common每周安装数
188
仓库
GitHub 星标数
3
首次出现
2026年1月26日
安全审计
安装于
opencode166
cursor92
codex87
gemini-cli85
github-copilot82
kimi-cli77
Creates production-quality React site components with editor manifests for Wix CLI applications. Site components are React components that integrate with the Wix Editor, allowing site owners to customize content, styling, and behavior through a visual interface.
Site components consist of four required files :
manifest.json)Defines the contract between the React component and Wix ecosystem:
component.tsx)Production-ready React functional component:
wix.elementsRemovalStatestyle.css)Modern CSS with responsive design:
box-sizing: border-box all elementstransition: all, NO media queries (except prefers-reduced-motion)--display: [value] CSS variable, then use display: var(--display) on roottypes.ts)Strict type definitions:
You MUST readMANIFEST_GUIDELINES.md before implementing a site component. It contains the complete manifest structure, all data types, element configurations, and required patterns.
The manifest defines the editor contract using these key sections:
{
"installation": {
"staticContainer": "HOMEPAGE",
"initialSize": {
"width": { "sizingType": "pixels", "pixels": 400 },
"height": { "sizingType": "pixels", "pixels": 300 }
}
}
}
"HOMEPAGE" for automatic installation on Harmony editorsizingType options:
"content" - Auto-size based on content"stretched" - Fill available space"pixels" - Fixed pixel dimension (requires pixels property){
"selector": ".component-name",
"displayName": "Component Name",
"archetype": "Container",
"layout": {
"resizeDirection": "horizontalAndVertical",
"contentResizeDirection": "horizontal"
},
"cssProperties": {
"backgroundColor": {
"displayName": "Background Color",
"defaultValue": "#ffffff"
}
},
"data": {
"columns": {
"dataType": "number",
"displayName": "Number of Columns",
"number": { "minimum": 1, "maximum": 4 }
}
},
"elements": {
"title": {
"elementType": "inlineElement",
"inlineElement": {
"selector": ".component-name__title",
"displayName": "Title",
"data": {
"titleText": {
"dataType": "text",
"displayName": "Title Text"
}
},
"behaviors": {
"selectable": true,
"removable": true
}
}
}
}
}
| Type | Runtime Value | Use Case |
|---|---|---|
text | string | Names, titles, descriptions |
textEnum | string | Predefined options |
number | number | Quantities, dimensions |
booleanValue | boolean | Toggles, flags |
a11y | Object | Accessibility attributes |
Common CSS properties for styling customization:
display, gap, padding, margin, width, heightfont, fontSize, fontWeight, textAlign, colorComplete CSS properties reference: See CSS_GUIDELINES.md for all CSS properties, variable patterns, and styling best practices.
Complete reference: See REACT_PATTERNS.md for detailed component architecture, all coding patterns, and implementation examples.
interface ComponentProps {
// Standard props (always present)
className: string;
id: string;
wix?: Wix;
// Component-level data (from editorElement.data)
columns?: number;
layout?: string;
// Element props (from elements definitions)
elementProps?: {
title?: {
titleText?: string;
wix?: Wix;
};
button?: {
buttonText?: string;
buttonLink?: Link;
wix?: Wix;
};
};
}
Extract every distinct UI element into named sub-components:
// Title sub-component
interface TitleProps {
titleText?: string;
className: string;
}
const Title: FC<TitleProps> = ({ titleText = "Default Title", className }) => (
<h2 className={className}>{titleText}</h2>
);
// Main component
const ProductCard: FC<ProductCardProps> = ({
className,
id,
elementProps,
wix
}) => {
const removalState = wix?.elementsRemovalState || {};
return (
<div className={`product-card ${className}`} id={id}>
{!removalState['title'] && (
<Title
className="product-card__title"
{...elementProps?.title}
/>
)}
{!removalState['button'] && (
<Button
className="product-card__button"
{...elementProps?.button}
/>
)}
</div>
);
};
All elements must be conditionally rendered based on removal state:
const removalState = wix?.elementsRemovalState || {};
return (
<div className={`component ${className}`} id={id}>
{!removalState['elementKey'] && <Element />}
</div>
);
Components live in user-resizable containers (300-1200px) within varying viewports:
width: 100%; height: 100%clamp() for fluid scaling.component {
--display: block;
--background-color: #ffffff;
--text-color: #333333;
display: var(--display);
background-color: var(--background-color);
color: var(--text-color);
pointer-events: auto;
}
CRITICAL : CSS selectors must match manifest selectors and React classNames exactly:
className="product-card__title".product-card__title { ... }"selector": ".product-card__title"Complete reference: See DESIGN_SYSTEM.md for visual design principles, creative guidelines, and aesthetic best practices.
| Relationship | Value | Use Case |
|---|---|---|
| Tight (icon + label) | 0.25-0.5rem (4-8px) | Clustering related items |
| Same category | 1-1.5rem (16-24px) | Card sections, form fields |
| Different sections | 2-3rem (32-48px) | Major content blocks |
| Emphasis/Drama | 4rem+ (64px+) | Hero content, luxury feel |
Push beyond obvious solutions:
Each distinct visual part requires a separate manifest element:
editorElement.data - Component-wide configuration only:
elements[key].data - Content for that specific element:
When components need default images, use this format:
// Import in component
import { heroImage } from './assets/defaultImages';
// Usage
<img src={heroImage} alt="Hero" />
Asset specification format:
<imageUrlName>
{ "description": "Modern cityscape at sunset", "width": 1920, "height": 1088 }
</imageUrlName>
Rules:
'./assets/defaultImages'src/extensions/site/components/
└── {component-name}/
├── manifest.json # Component manifest
├── component.tsx # React component
├── style.css # CSS styles
├── types.ts # TypeScript types
└── assets/ # Optional assets
└── defaultImages.ts
Complete working example: See EXAMPLE.md for a full production-ready site component with all patterns, including manifest, React component, CSS, and types.
Request: "Create a product card component with image, title, price, and buy button"
Output:
Request: "Build a hero section with background image, headline, subtitle, and CTA button"
Output:
Request: "Create a features component with configurable number of items"
Output:
Extension registration is MANDATORY and has TWO required steps.
Each site component requires an extensions.ts file in its folder:
import { extensions } from "@wix/astro/builders";
import manifest from "./manifest.json";
export const sitecomponentMyComponent = extensions.siteComponent({
...manifest,
id: "{{GENERATE_UUID}}",
description: "My Component",
type: "platform.MyComponent",
resources: {
client: {
component: "./extensions/site/components/my-component/component.tsx",
componentUrl: "./extensions/site/components/my-component/component.tsx",
},
},
});
CRITICAL: Type Naming Convention
The type field uses the format platform.{PascalCaseFolderName}:
my-component → type: "platform.MyComponent"product-card → type: "platform.ProductCard"hero-section → type: "platform.HeroSection"The folder name is converted to PascalCase (hyphens removed, each word capitalized).
CRITICAL: UUID Generation
The id must be a unique, static UUID v4 string. Generate a fresh UUID for each extension - do NOT use randomUUID() or copy UUIDs from examples.
CRITICAL: After creating the component-specific extension file, you MUST read wix-cli-extension-registration and follow the "App Registration" section to update src/extensions.ts.
Without completing Step 2, the site component will not be available in the Wix Editor.
Complete reference: See TYPESCRIPT_QUALITY.md for comprehensive type safety guidelines and code quality standards.
any types@ts-ignore or @ts-expect-error comments@typescript-eslint/no-unused-vars)img src not https://... (allowed: local imports, wixstatic.com, variables)window/document at module scope/constructor, guard browser APIs in useEffect/handlersdangerouslySetInnerHTML or inline <style> tags - use CSS variables or inline prop for dynamic values| Mistake | Why It Fails | Fix |
|---|---|---|
| CSS selector doesn't match manifest | Editor can't apply styles to the element | Ensure manifest selector, React className, and CSS selector are identical |
Putting content text in editorElement.data | Content belongs to specific elements, not root | Move text/image/link data into elements[key].data |
Using display: flex directly on root | Breaks editor override mechanism | Use --display: flex CSS variable, then |
@wix/design-system or @wix/wix-ui-icons-commonWeekly Installs
188
Repository
GitHub Stars
3
First Seen
Jan 26, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
opencode166
cursor92
codex87
gemini-cli85
github-copilot82
kimi-cli77
React 组合模式指南:Vercel 组件架构最佳实践,提升代码可维护性
113,700 周安装
link | { href, target, rel } | Navigation links |
image | { uri, url, alt, width, height } | Images |
video | Video object | Media content |
vectorArt | Sanitized SVG object | Icons, graphics |
localDate | string (YYYY-MM-DD) | Date values |
localTime | string (hh:mm) | Time values |
webUrl | string | External URLs |
richText | string (HTML) | Formatted content |
arrayItems | Array | Collections, lists |
direction | string | HTML dir attribute |
menuItems | Array of menu items | Navigation menus |
backgroundColor, backgroundImageborder, borderRadius, boxShadowalignItems, justifyContent, flexDirectionstylewindow.fetch (no-restricted-properties)exhaustive-deps: ALL values from component scope used inside useEffect/useCallback MUST be in dependency arrayconst/let (no var), no unknown JSX propertiesdisplay: var(--display)Missing removable: true on optional elements | Site owner can't hide the element | Add behaviors: { removable: true } to optional elements |
Using window/document at module scope | SSR fails during build | Guard browser APIs inside useEffect or event handlers |
Importing from @wix/design-system | Not available in site components | Use plain HTML/CSS or custom components only |