# Webflow Code Component:convert Component

> Convert an existing React component into a Webflow Code Component. Analyzes TypeScript props, maps to Webflow prop types, generates the .webflow.tsx definition file, and identifies required modifications.

- **Type:** Skill
- **Install:** `agentstack add skill-webflow-webflow-skills-convert-component`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [webflow](https://agentstack.voostack.com/s/webflow)
- **Installs:** 0
- **Category:** [Agent Skills](https://agentstack.voostack.com/c/agent-skills)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [webflow](https://github.com/webflow)
- **Source:** https://github.com/webflow/webflow-skills/tree/main/plugins/webflow-skills/skills/convert-component

## Install

```sh
agentstack add skill-webflow-webflow-skills-convert-component
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# Convert Component

Convert an existing React component into a Webflow Code Component by analyzing its structure and generating the appropriate `.webflow.tsx` definition file.

## When to Use This Skill

**Use when:**
- User has an existing React component they want to use in Webflow
- User asks to "convert", "adapt", or "make this work with Webflow"
- User provides a React component file and wants a Webflow definition
- User is migrating components from another React project

**Do NOT use when:**
- Creating a component from scratch (use component-scaffold)
- User just wants to understand code components (answer directly)
- Component is already a Webflow code component (use component-audit)

## Instructions

### Phase 1: Analyze Existing Component

1. **Read the React component file**: Get the full source code

2. **Extract component information**:
   - Component name (function/const name)
   - Props interface or type definition
   - Each prop's TypeScript type
   - Default values if defined
   - Whether component uses `children`

3. **Identify incompatible patterns**:

   | Pattern | Issue | Resolution |
   |---------|-------|------------|
   | React Context usage | Context doesn't work across Webflow components | Refactor to props or use nano stores |
   | `window`/`document` in render | SSR will fail | Wrap in useEffect or set `ssr: false` |
   | `localStorage`/`sessionStorage` in render | SSR will fail | Wrap in useEffect or set `ssr: false` |
   | Complex object props | Can't map to Webflow prop types | Break into individual props |
   | Function props (callbacks) | Not supported in Webflow | Remove or internalize logic |
   | `useContext` hook | Won't work across components | Use alternative state patterns |
   | External CSS imports | May not work in Shadow DOM | Import in .webflow.tsx instead |
   | CSS class references to global styles | Won't work in Shadow DOM | Use component-scoped styles |
   | styled-components | Needs Shadow DOM decorator | Set up globals.ts with decorator |
   | Emotion (@emotion/styled) | Needs Shadow DOM decorator | Set up globals.ts with decorator |

4. **Detect styling approach** and note required setup:

   **If using styled-components:**
   ```bash
   npm i @webflow/styled-components-utils styled-components
   ```

   Create/update `globals.ts`:
   ```typescript
   import { styledComponentsShadowDomDecorator } from "@webflow/styled-components-utils";
   export const decorators = [styledComponentsShadowDomDecorator];
   ```

   **If using Emotion:**
   ```bash
   npm i @webflow/emotion-utils @emotion/cache @emotion/react
   ```

   Create/update `globals.ts`:
   ```typescript
   import { emotionShadowDomDecorator } from "@webflow/emotion-utils";
   export const decorators = [emotionShadowDomDecorator];
   ```

   **For both CSS-in-JS approaches**, update `webflow.json`:

   styled-components:
   ```json
   {
     "library": {
       "globals": "./src/globals.ts",
       "renderer": {
         "server": "@webflow/styled-components-utils/server"
       }
     }
   }
   ```

   Emotion:
   ```json
   {
     "library": {
       "globals": "./src/globals.ts",
       "renderer": {
         "server": "@webflow/emotion-utils/server"
       }
     }
   }
   ```

5. **Flag any dependencies** that might cause issues:
   - Large libraries (bundle size concern)
   - Browser-only libraries
   - Libraries that manipulate DOM directly

### Phase 2: Map Props to Webflow Types

6. **Apply TypeScript → Webflow prop type mapping**:

   | TypeScript Type | Webflow Prop | Notes |
   |-----------------|--------------|-------|
   | `string` | `props.Text()` | Default for short text |
   | `string` (long/HTML content) | `props.RichText()` | If prop name suggests content/body/description |
   | `React.ReactNode` / `children` | `props.Slot()` | For nested content |
   | `number` | `props.Number()` | Numeric values |
   | `boolean` | `props.Boolean()` | Toggles |
   | `"option1" \| "option2"` | `props.Variant()` | String literal unions (requires `options` array) |
   | `enum` | `props.Variant()` | Convert enum values to `options` array (required) |
   | `{ href: string; ... }` | `props.Link()` | Returns `{ href, target?, preload? }` object — may need wrapper if component expects separate `href`/`target` props |
   | Image-related types | `props.Image()` | Image src, url, etc. |
   | `string` (canvas-editable text) | `props.TextNode()` | For text editable directly on canvas; has `multiline` param |
   | `boolean` (show/hide) | `props.Visibility()` | Semantic show/hide toggle |
   | `string` (for HTML id) | `props.Id()` | If prop is named "id" or used for accessibility |
   | Complex objects | **SPLIT** | Break into multiple simple props |
   | Functions/callbacks | **REMOVE** | Not supported |
   | Arrays | **SPECIAL** | May need component redesign |

7. **Handle special cases**:

   **Complex object props** - Break them down:
   ```typescript
   // 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" })
   }
   ```

   **Union types with more than simple strings**:
   ```typescript
   // 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
   ```

   **Optional props** - Provide defaultValue for prop types that support it. Note: Link, Image, Slot, and Id do not accept defaultValue.
   ```typescript
   // 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: Check Project Setup

8. **Verify Webflow setup exists**:
   - Check for `webflow.json` in project root
   - Check for required dependencies (@webflow/webflow-cli, @webflow/data-types, @webflow/react)
   - If using styled-components/Emotion, check for decorator packages
   - If missing, offer to set up or direct to local-dev-setup skill

9. **Determine file locations**:
   - Identify where the original component lives
   - Determine where `.webflow.tsx` should be created (same directory)
   - Check for existing styles that need to be imported

### Phase 4: Generate Definition File

10. **Create the `.webflow.tsx` file**:

```typescript
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
  }
});
```

11. **Provide the complete file** with all props mapped

### Phase 5: Document Required Changes

12. **List modifications needed** to the original component:

```markdown
## 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 |
```

13. **Provide modified component code** if changes are needed:
    - Show the updated TypeScript interface
    - Show any useEffect wrappers needed for SSR
    - Show style import changes
    - Note CSS-in-JS setup requirements if applicable

## Examples

### Example 1: Simple Component Conversion

**User prompt:**
```
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 = ({
  quote,
  authorName,
  authorTitle = "",
  rating = 5
}) => {
  return (
    
      {"★".repeat(rating)}
      {quote}
      
        {authorName}
        {authorTitle && {authorTitle}}
      
    
  );
};
```

**Response:**

```markdown
## 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
  }
});
```

### Required Changes to TestimonialCard.tsx

1. **Convert to CSS Modules** - Rename `TestimonialCard.css` to `TestimonialCard.module.css` and update imports:

```typescript
import styles from "./TestimonialCard.module.css";

// Default CSS Modules uses bracket notation:

// Dot notation (styles.testimonialCard) requires camelCase class names
// or a webpack override for css-loader.
```

### Setup Check
- [ ] Verify webflow.json exists
- [ ] Install dependencies if needed: `npm i --save-dev @webflow/webflow-cli @webflow/data-types @webflow/react`
```

---

### Example 2: Component with Incompatible Patterns

**User prompt:**
```
Make this work as a Webflow code component:

// Modal.tsx
import { createContext, useContext, useState } from 'react';

const ModalContext = createContext 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 (
    
      {trigger}
      {isOpen && (
        
           e.stopPropagation()}>
            {children}
          
        
      )}
    
  );
};
```

**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. Convert to CSS Modules
Rename styles and update class references.

### 4. Remove exported useModal hook
This won't work for external components. Keep ModalContext internal only.

---

## Generated Files

### Modal.webflow.tsx
```typescript
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
  }
});
```

### Updated Modal.tsx
```typescript
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 = ({ 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 (
    <>
      {trigger}
      {isOpen && (
        
           e.stopPropagation()}>
            {children}
          
        
      )}
    
  );
};
```

### Setup Required
```bash
npm i --save-dev @webflow/webflow-cli @webflow/data-types @webflow/react
```
```

---

### Example 3: Component with Complex Object Props

**User prompt:**
```
Convert to 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) => (
  
    
    {showBadge && {product.category}}
    {product.name}
    ${product.price}
  
);
```

**Response:**

```markdown
## Component Analysis: ProductCard

### Complex Props Detected:

The `product` prop is a complex object that must be flattened for Webflow:

| Original | Flattened Prop

…

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [webflow](https://github.com/webflow)
- **Source:** [webflow/webflow-skills](https://github.com/webflow/webflow-skills)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-webflow-webflow-skills-convert-component
- Seller: https://agentstack.voostack.com/s/webflow
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
