关于 webflow-code-component:convert-component
用于分析现有 React 组件并生成 Webflow Code Component 定义文件的技能。它将属性映射为 Webflow 类型,并指出可能需要调整的兼容性问题。
主要功能
- 01分析组件结构与属性
- 02将 TypeScript 属性映射为 Webflow 类型
- 03标记不兼容的 React 模式
- 04生成 .webflow.tsx 定义文件
- 05识别配置与依赖需求
适用场景
- 01为 Webflow 准备 React 组件
- 02转换属性以便在 Designer 中编辑
- 03检查 SSR 与样式兼容性
- 04规划不支持的回调或对象属性的调整
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ webflow/ webflow-skills --skill webflow-code-component:convert-component安装后在 Claude Code 中输入/webflow-code-component:convert-component确认已加载。
附带文件references/prop-types.md
详细介绍
转换组件
通过分析现有 React 组件的结构,并生成相应的 .webflow.tsx 定义文件,将其转换为 Webflow 代码组件。
适用场景
适用于:
- 用户有一个想在 Webflow 中使用的现有 React 组件
- 用户要求“转换”“适配”或“让它能在 Webflow 中使用”
- 用户提供了 React 组件文件,并想要 Webflow 定义文件
- 用户正在从其他 React 项目迁移组件
不适用于:
- 从头开始创建组件(使用 component-scaffold)
- 用户只想了解代码组件(直接回答)
- 组件已经是 Webflow 代码组件(使用 component-audit)
说明
第一阶段:分析现有组件
-
读取 React 组件文件:获取完整源代码
-
提取组件信息:
- 组件名称(函数名/const 名称)
- Props 接口或类型定义
- 每个 prop 的 TypeScript 类型
- 已定义的默认值
- 组件是否使用
children
-
识别不兼容的模式:
模式 问题 解决方法 使用 React Context Context 无法跨 Webflow 组件使用 重构为 props,或使用 nano stores 在渲染时使用 window/documentSSR 会失败 封装在 useEffect 中,或设置 ssr: false在渲染时使用 localStorage/sessionStorageSSR 会失败 封装在 useEffect 中,或设置 ssr: false复杂对象 prop 无法映射到 Webflow prop 类型 拆分为多个独立 prop 函数 prop(回调) Webflow 不支持 移除或将逻辑内置到组件中 useContext钩子无法跨组件使用 使用替代状态模式 外部 CSS 导入 在 Shadow DOM 中可能无法使用 改为在 .webflow.tsx 中导入 CSS 类引用全局样式 在 Shadow DOM 中无法使用 使用组件作用域样式 styled-components 需要 Shadow DOM 装饰器 在 globals.ts 中配置装饰器 Emotion (@emotion/styled) 需要 Shadow DOM 装饰器 在 globals.ts 中配置装饰器 -
检测样式方案,并记录所需的配置:
如果使用 styled-components:
npm i @webflow/styled-components-utils styled-components创建/更新
globals.ts:import { styledComponentsShadowDomDecorator } from "@webflow/styled-components-utils"; export const decorators = [styledComponentsShadowDomDecorator];如果使用 Emotion:
npm i @webflow/emotion-utils @emotion/cache @emotion/react创建/更新
globals.ts:import { emotionShadowDomDecorator } from "@webflow/emotion-utils"; export const decorators = [emotionShadowDomDecorator];对于这两种 CSS-in-JS 方案,更新
webflow.json:styled-components:
{ "library": { "globals": "./src/globals.ts", "renderer": { "server": "@webflow/styled-components-utils/server" } } }Emotion:
{ "library": { "globals": "./src/globals.ts", "renderer": { "server": "@webflow/emotion-utils/server" } } } -
标记可能导致问题的依赖项:
- 大型库(可能影响打包体积)
- 仅限浏览器使用的库
- 会直接操作 DOM 的库
Phase 2:将属性映射到 Webflow 类型
-
应用 TypeScript → Webflow 属性类型映射:
TypeScript 类型 Webflow 属性 说明 stringprops.Text()短文本的默认类型 string(长文本/HTML 内容)props.RichText()属性名称暗示内容/正文/描述时使用 React.ReactNode/childrenprops.Slot()用于嵌套内容 numberprops.Number()数值 booleanprops.Boolean()开关 "option1" | "option2"props.Variant()字符串字面量联合类型(需要 options数组)enumprops.Variant()将枚举值转换为 options数组(必需){ href: string; ... }props.Link()返回 { href, target?, preload? }对象——如果组件需要单独的href/target属性,可能需要包装器图片相关类型 props.Image()图片 src、url 等 string(可在画布上编辑的文本)props.TextNode()用于可直接在画布上编辑的文本;带有 multiline参数boolean(显示/隐藏)props.Visibility()语义化的显示/隐藏开关 string(用于 HTML id)props.Id()属性名为 “id” 或用于辅助功能时使用 复杂对象 拆分 拆分为多个简单属性 函数/回调 移除 不受支持 数组 特殊处理 可能需要重新设计组件 -
处理特殊情况:
复杂对象属性——将其拆分:
// Original interface Props { author: { name: string; avatar: string; bio: string; } } // Converted to flat props props: { authorName: props.Text({ name: "Author Name" }), authorAvatar: props.Image({ name: "Author Avatar" }), authorBio: props.RichText({ name: "Author Bio" }) }包含非简单字符串的联合类型:
// Original - complex union type Size = "sm" | "md" | "lg" | { width: number; height: number }; // Convert to Variant with only string options size: props.Variant({ name: "Size", options: ["sm", "md", "lg", "custom"], defaultValue: "md" }) // Note: Custom size would need additional Number props可选属性——为支持
defaultValue的属性类型提供defaultValue。注意:Link、Image、Slot 和 Id 不接受defaultValue。// Original interface Props { title?: string; } // Converted - provide default for types that support it title: props.Text({ name: "Title", defaultValue: "" // Empty string or sensible default })
Phase 3:检查项目设置
-
确认 Webflow 设置是否存在:
- 检查项目根目录中是否有
webflow.json - 检查是否包含所需依赖(@webflow/webflow-cli、@webflow/data-types、@webflow/react)
- 如果使用 styled-components/Emotion,检查是否包含装饰器包
- 如果缺少,请提供设置帮助,或引导至 local-dev-setup skill
- 检查项目根目录中是否有
-
确定文件位置:
- 找出原始组件所在位置
- 确定
.webflow.tsx的创建位置(同一目录) - 检查是否需要导入现有样式
Phase 4:生成定义文件
- 创建
.webflow.tsx文件:
import { declareComponent } from "@webflow/react";
import { props } from "@webflow/data-types";
import { ComponentName } from "./ComponentName";
// Import styles if they exist
import "./ComponentName.module.css"; // or .css
export default declareComponent(ComponentName, {
name: "ComponentName",
description: "[Generated from component purpose]",
group: "[Appropriate category]",
props: {
// Mapped props here
},
// decorators: [], // Optional — per-component decorators (e.g., for CSS-in-JS Shadow DOM support)
options: {
applyTagSelectors: true, // Default is false. Set to true to apply Webflow's tag selectors (e.g., h1, p styles) inside the component.
ssr: true // or false if browser APIs detected
}
});
- 提供包含所有属性映射的完整文件
Phase 5:记录所需更改
- 列出原始组件需要进行的修改:
## Required Changes to [ComponentName].tsx
### Must Fix (Component won't work without these):
- [ ] Issue 1: [Description and how to fix]
- [ ] Issue 2: [Description and how to fix]
### Recommended (Will improve Webflow integration):
- [ ] Recommendation 1
- [ ] Recommendation 2
### Props Mapping Summary:
| Original Prop | Webflow Prop | Notes |
|---------------|--------------|-------|
| title: string | props.Text() | Direct mapping |
| isOpen: boolean | props.Boolean() | Direct mapping |
| data: object | SPLIT → dataTitle, dataValue | Flattened |
| onClick: function | REMOVED | Not supported |
- 如果需要更改,提供修改后的组件代码:
- 展示更新后的 TypeScript 接口
- 展示 SSR 所需的任何 useEffect 包装器
- 展示样式导入的更改
- 适用时说明 CSS-in-JS 设置要求
示例
示例 1:简单组件转换
用户提示词:
Convert this React component to work with Webflow:
// TestimonialCard.tsx
interface TestimonialCardProps {
quote: string;
authorName: string;
authorTitle?: string;
rating?: number;
}
export const TestimonialCard: React.FC<TestimonialCardProps> = ({
quote,
authorName,
authorTitle = "",
rating = 5
}) => {
return (
<div className="testimonial-card">
<div className="stars">{"★".repeat(rating)}</div>
<blockquote>{quote}</blockquote>
<div className="author">
<strong>{authorName}</strong>
{authorTitle && <span>{authorTitle}</span>}
</div>
</div>
);
};
回复:
## Component Analysis: TestimonialCard
### Props Detected:
| Prop | TypeScript Type | Webflow Mapping |
|------|-----------------|-----------------|
| quote | string | props.Text() |
| authorName | string | props.Text() |
| authorTitle | string (optional) | props.Text() |
| rating | number (optional) | props.Number() |
### Issues Found:
- **CSS class "testimonial-card"** - Uses global class that won't work in Shadow DOM
- Fix: Convert to CSS Modules or import scoped styles
### No blocking issues - ready to convert!
---
## Generated Files
### TestimonialCard.webflow.tsx
```typescript
import { declareComponent } from "@webflow/react";
import { props } from "@webflow/data-types";
import { TestimonialCard } from "./TestimonialCard";
import "./TestimonialCard.module.css";
export default declareComponent(TestimonialCard, {
name: "TestimonialCard",
description: "Display customer testimonials with rating stars",
group: "Display",
props: {
quote: props.Text({
name: "Quote",
defaultValue: "This product changed my life!"
}),
authorName: props.Text({
name: "Author Name",
defaultValue: "Jane Doe"
}),
authorTitle: props.Text({
name: "Author Title",
defaultValue: "CEO, Company"
}),
rating: props.Number({
name: "Rating",
defaultValue: 5,
min: 1,
max: 5
})
},
options: {
applyTagSelectors: true,
ssr: true
}
});
TestimonialCard.tsx 所需更改
- 转换为 CSS Modules——将
TestimonialCard.css重命名为TestimonialCard.module.css,并更新导入:
import styles from "./TestimonialCard.module.css";
// Default CSS Modules uses bracket notation:
<div className={styles["testimonial-card"]}>
<div className={styles["stars"]}>
// Dot notation (styles.testimonialCard) requires camelCase class names
// or a webpack override for css-loader.
设置检查
- 确认 webflow.json 存在
- 如有需要,安装依赖:
npm i --save-dev @webflow/webflow-cli @webflow/data-types @webflow/react
---
### Example 2: Component with Incompatible Patterns
**User prompt:**
将其转换为可用作 Webflow 代码组件的形式:
// Modal.tsx import { createContext, useContext, useState } from 'react';
const ModalContext = createContext<{ isOpen: boolean; toggle: () => void } | null>(null);
export const useModal = () => { const context = useContext(ModalContext); if (!context) throw new Error('useModal must be used within ModalProvider'); return context; };
interface ModalProps { trigger: React.ReactNode; children: React.ReactNode; onOpen?: () => void; onClose?: () => void; }
export const Modal: React.FC = ({ trigger, children, onOpen, onClose }) => { const [isOpen, setIsOpen] = useState(false);
const toggle = () => { const newState = !isOpen; setIsOpen(newState); if (newState && onOpen) onOpen(); if (!newState && onClose) onClose(); };
useEffect(() => { if (isOpen) { document.body.style.overflow = 'hidden'; } else { document.body.style.overflow = ''; } }, [isOpen]);
return ( <ModalContext.Provider value={{ isOpen, toggle }}>
**Response:**
```markdown
## Component Analysis: Modal
### Issues Found:
| Issue | Severity | Resolution |
|-------|----------|------------|
| React Context (ModalContext) | Critical | Context won't work across Webflow components. Since this is internal to Modal, it's OK but exported useModal hook won't work. |
| document.body manipulation | Critical | SSR incompatible. Must set `ssr: false` or wrap in useEffect check. |
| onOpen/onClose callbacks | Warning | Function props not supported in Webflow. Must remove. |
| Global CSS classes | Warning | "modal-overlay", "modal-content" won't work in Shadow DOM. |
### Props Detected:
| Prop | TypeScript Type | Webflow Mapping | Status |
|------|-----------------|-----------------|--------|
| trigger | React.ReactNode | props.Slot() | OK |
| children | React.ReactNode | props.Slot() | OK |
| onOpen | function | REMOVE | Not supported |
| onClose | function | REMOVE | Not supported |
---
## Required Changes
### 1. Remove function props (onOpen, onClose)
These cannot be passed from Webflow Designer. Remove from interface and component.
### 2. Fix SSR issue with document.body
Either set `ssr: false` or add typeof check:
```typescript
useEffect(() => {
if (typeof document === 'undefined') return;
// ... rest of effect
}, [isOpen]);
3. 转换为 CSS Modules
重命名样式并更新类引用。
4. 移除导出的 useModal hook
它无法用于外部组件。仅将 ModalContext 保留为内部内容。
生成的文件
Modal.webflow.tsx
import { declareComponent } from "@webflow/react";
import { props } from "@webflow/data-types";
import { Modal } from "./Modal";
import "./Modal.module.css";
export default declareComponent(Modal, {
name: "Modal",
description: "Overlay modal dialog with trigger element",
group: "Interactive",
props: {
trigger: props.Slot({
name: "Trigger"
}),
children: props.Slot({
name: "Content"
})
},
options: {
applyTagSelectors: true,
ssr: false // Required due to document.body usage
}
});
更新后的 Modal.tsx
import { useState, useEffect } from 'react';
import styles from "./Modal.module.css";
interface ModalProps {
trigger: React.ReactNode;
children: React.ReactNode;
// Removed: onOpen, onClose - not supported in Webflow
}
export const Modal: React.FC<ModalProps> = ({ trigger, children }) => {
const [isOpen, setIsOpen] = useState(false);
const toggle = () => setIsOpen(!isOpen);
useEffect(() => {
if (typeof document === 'undefined') return;
if (isOpen) {
document.body.style.overflow = 'hidden';
} else {
document.body.style.overflow = '';
}
return () => {
document.body.style.overflow = '';
};
}, [isOpen]);
return (
<>
<div onClick={toggle}>{trigger}</div>
{isOpen && (
<div className={styles["overlay"]} onClick={toggle}>
<div className={styles["content"]} onClick={e => e.stopPropagation()}>
{children}
</div>
</div>
)}
</>
);
};
所需设置
npm i --save-dev @webflow/webflow-cli @webflow/data-types @webflow/react
---
### Example 3: Component with Complex Object Props
**User prompt:**
转换为 Webflow:
interface Product { id: string; name: string; price: number; image: string; category: 'electronics' | 'clothing' | 'home'; }
interface ProductCardProps { product: Product; showBadge?: boolean; }
export const ProductCard = ({ product, showBadge = true }: ProductCardProps) => (
{product.name}
${product.price}
Response:
## Component Analysis: ProductCard
### Complex Props Detected:
The `product` prop is a complex object that must be flattened for Webflow:
| Original | Flattened Props |
|----------|-----------------|
| product.id | (Not needed in UI - remove) |
| product.name | productName: props.Text() |
| product.price | productPrice: props.Number() |
| product.image | productImage: props.Image() |
| product.category | productCategory: props.Variant() |
| showBadge | showBadge: props.Boolean() |
---
## Generated Files
### ProductCard.webflow.tsx
```typescript
import { declareComponent } from "@webflow/react";
import { props } from "@webflow/data-types";
import { ProductCard } from "./ProductCard";
import "./ProductCard.module.css";
export default declareComponent(ProductCard, {
name: "ProductCard",
description: "Product display card with image, name, price, and category",
group: "Display",
props: {
productName: props.Text({
name: "Product Name",
defaultValue: "Product Title"
}),
productPrice: props.Number({
name: "Price",
defaultValue: 29.99
}),
productImage: props.Image({
name: "Product Image"
}),
productCategory: props.Variant({
name: "Category",
options: ["electronics", "clothing", "home"],
defaultValue: "electronics"
}),
showBadge: props.Boolean({
name: "Show Category Badge",
defaultValue: true
})
},
options: {
applyTagSelectors: true,
ssr: true
}
});
更新后的 ProductCard.tsx
import styles from "./ProductCard.module.css";
interface ProductCardProps {
productName: string;
productPrice: number;
productImage?: { src: string; alt?: string };
productCategory: 'electronics' | 'clothing' | 'home';
showBadge?: boolean;
}
export const ProductCard = ({
productName,
productPrice,
productImage,
productCategory,
showBadge = true
}: ProductCardProps) => (
<div className={styles["card"]}>
{productImage && (
<img src={productImage.src} alt={productImage.alt || productName} />
)}
{showBadge && <span className={styles["badge"]}>{productCategory}</span>}
<h3>{productName}</h3>
<p>${productPrice.toFixed(2)}</p>
</div>
);
迁移说明:
product对象已拆分为各个独立属性- 现在可在 Webflow Designer 中直接编辑每个属性
product.id已移除,因为它不会显示在 UI 中- Image 属性现在使用 Webflow 的 Image 类型(提供 { src, alt })
---
### Example 4: Component with styled-components
**User prompt:**
将此 styled-components React 组件转换为 Webflow:
// Button.tsx import styled from 'styled-components';
const StyledButton = styled.button<{ variant: 'primary' | 'secondary' }>` padding: 12px 24px; border-radius: 8px; font-weight: 600; background: ${props => props.variant === 'primary' ? '#3b82f6' : '#e5e7eb'}; color: ${props => props.variant === 'primary' ? 'white' : '#1f2937'};
&:hover { opacity: 0.9; } `;
interface ButtonProps { label: string; variant?: 'primary' | 'secondary'; disabled?: boolean; }
export const Button = ({ label, variant = 'primary', disabled = false }: ButtonProps) => ( {label} );
**Response:**
```markdown
## Component Analysis: Button
### Styling Approach Detected: styled-components
styled-components requires a Shadow DOM decorator to work in Webflow.
### Props Detected:
| Prop | TypeScript Type | Webflow Mapping |
|------|-----------------|-----------------|
| label | string | props.Text() |
| variant | 'primary' \| 'secondary' | props.Variant() |
| disabled | boolean | props.Boolean() |
### No blocking issues - ready to convert!
---
## Required Setup for styled-components
**1. Install dependencies:**
```bash
npm i @webflow/styled-components-utils styled-components
npm i -D @types/styled-components
2. 创建/更新 src/globals.ts:
import { styledComponentsShadowDomDecorator } from "@webflow/styled-components-utils";
export const decorators = [styledComponentsShadowDomDecorator];
3. 更新 webflow.json:
{
"library": {
"name": "My Library",
"components": ["./src/**/*.webflow.@(js|jsx|mjs|ts|tsx)"],
"globals": "./src/globals.ts"
}
}
生成的文件
Button.webflow.tsx
import { declareComponent } from "@webflow/react";
import { props } from "@webflow/data-types";
import { Button } from "./Button";
// No CSS import needed - styled-components handles styles
export default declareComponent(Button, {
name: "Button",
description: "Styled button with primary and secondary variants",
group: "Interactive",
props: {
label: props.Text({
name: "Label",
defaultValue: "Click me"
}),
variant: props.Variant({
name: "Variant",
options: ["primary", "secondary"],
defaultValue: "primary"
}),
disabled: props.Boolean({
name: "Disabled",
defaultValue: false,
trueLabel: "Disabled",
falseLabel: "Enabled"
})
},
options: {
applyTagSelectors: true,
ssr: true
}
});
Button.tsx 无需更改
组件可以保持原样。globals.ts 中的 styled-components 装饰器会自动处理 Shadow DOM 样式注入。
设置清单
- 安装 @webflow/styled-components-utils
- 创建包含装饰器的 globals.ts
- 更新 webflow.json 以引用 globals
- 使用
npx webflow library share部署
## Guidelines
### When to Recommend Component Redesign
Some components fundamentally don't fit the Webflow model:
1. **Heavy Context usage**: If component relies on app-wide context, suggest redesign
2. **Complex state machines**: May need simplification
3. **Tightly coupled components**: Each needs to be independent in Webflow
4. **Components that render portals**: Consider if portal is necessary
### Default Value Strategy
Always provide sensible defaults:
- Text props: Representative example text
- Numbers: Common/typical value
- Booleans: Most common use case
- Variants: Most popular option
- Images: Can be undefined (optional)
- Slots: No default needed
### Props Naming for Webflow
Make prop names designer-friendly:
- Use descriptive names: `buttonText` not `txt`
- Avoid abbreviations: `imageSource` not `imgSrc`
- Group related props with prefixes: `authorName`, `authorAvatar`, `authorBio`
### SSR Decision
Set `ssr: false` if component:
- Accesses `window`, `document`, `navigator`
- Uses `localStorage` or `sessionStorage`
- Manipulates DOM directly
- Uses libraries that require browser APIs
- Renders canvas, WebGL, or maps