mcpField
W
webflow-code-component:convert-component

webflow-code-component:convert-component

github.com/webflow开发1331.0k 次安装今天更新
代码仓库

关于 webflow-code-component:convert-component

用于分析现有 React 组件并生成 Webflow Code Component 定义文件的技能。它将属性映射为 Webflow 类型,并指出可能需要调整的兼容性问题。

主要功能

  1. 01分析组件结构与属性
  2. 02将 TypeScript 属性映射为 Webflow 类型
  3. 03标记不兼容的 React 模式
  4. 04生成 .webflow.tsx 定义文件
  5. 05识别配置与依赖需求

适用场景

  1. 01为 Webflow 准备 React 组件
  2. 02转换属性以便在 Designer 中编辑
  3. 03检查 SSR 与样式兼容性
  4. 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)

说明

第一阶段:分析现有组件
  1. 读取 React 组件文件:获取完整源代码

  2. 提取组件信息:

    • 组件名称(函数名/const 名称)
    • Props 接口或类型定义
    • 每个 prop 的 TypeScript 类型
    • 已定义的默认值
    • 组件是否使用 children
  3. 识别不兼容的模式:

    模式问题解决方法
    使用 React ContextContext 无法跨 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 中配置装饰器
  4. 检测样式方案,并记录所需的配置:

    如果使用 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"
        }
      }
    }
    
  5. 标记可能导致问题的依赖项:

    • 大型库(可能影响打包体积)
    • 仅限浏览器使用的库
    • 会直接操作 DOM 的库
Phase 2:将属性映射到 Webflow 类型
  1. 应用 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” 或用于辅助功能时使用
    复杂对象拆分拆分为多个简单属性
    函数/回调移除不受支持
    数组特殊处理可能需要重新设计组件
  2. 处理特殊情况:

    复杂对象属性——将其拆分:

    // 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:检查项目设置
  1. 确认 Webflow 设置是否存在:

    • 检查项目根目录中是否有 webflow.json
    • 检查是否包含所需依赖(@webflow/webflow-cli、@webflow/data-types、@webflow/react)
    • 如果使用 styled-components/Emotion,检查是否包含装饰器包
    • 如果缺少,请提供设置帮助,或引导至 local-dev-setup skill
  2. 确定文件位置:

    • 找出原始组件所在位置
    • 确定 .webflow.tsx 的创建位置(同一目录)
    • 检查是否需要导入现有样式
Phase 4:生成定义文件
  1. 创建 .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
  }
});
  1. 提供包含所有属性映射的完整文件
Phase 5:记录所需更改
  1. 列出原始组件需要进行的修改:
## 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 |
  1. 如果需要更改,提供修改后的组件代码:
    • 展示更新后的 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 所需更改
  1. 转换为 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 }}>

{trigger}
{isOpen && (
<div className="modal-content" onClick={e => e.stopPropagation()}> {children}
)} </ModalContext.Provider> ); };


**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} {showBadge && {product.category}}
{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

评论

支持 Markdown