webflow-code-component:convert-component

Par webflow · webflow-skills

Convertit un composant React existant en Webflow Code Component. Analyse les props TypeScript, les mappe vers les types de props Webflow, génère le fichier de définition `.webflow.tsx`, et identifie les modifications requises.

npx skills add https://github.com/webflow/webflow-skills --skill webflow-code-component:convert-component

Convertir un composant

Convertir un composant React existant en composant Webflow en analysant sa structure et en générant le fichier de définition .webflow.tsx approprié.

Quand utiliser cette compétence

À utiliser quand :

  • L'utilisateur a un composant React existant qu'il veut utiliser dans Webflow
  • L'utilisateur demande de « convertir », « adapter » ou « faire fonctionner avec Webflow »
  • L'utilisateur fournit un fichier de composant React et veut une définition Webflow
  • L'utilisateur migre des composants depuis un autre projet React

À NE PAS utiliser quand :

  • Créer un composant à partir de zéro (utiliser component-scaffold)
  • L'utilisateur veut juste comprendre les composants de code (répondre directement)
  • Le composant est déjà un composant Webflow (utiliser component-audit)

Instructions

Phase 1 : Analyser le composant existant

  1. Lire le fichier du composant React : Obtenir le code source complet

  2. Extraire les informations du composant :

    • Nom du composant (nom de fonction/const)
    • Interface ou définition de type des props
    • Type TypeScript de chaque prop
    • Valeurs par défaut si définies
    • Si le composant utilise children
  3. Identifier les motifs incompatibles :

    Motif Problème Résolution
    Utilisation de React Context Context ne fonctionne pas entre les composants Webflow Refactoriser en props ou utiliser nano stores
    window/document dans le rendu SSR échouera Envelopper dans useEffect ou définir ssr: false
    localStorage/sessionStorage dans le rendu SSR échouera Envelopper dans useEffect ou définir ssr: false
    Props d'objet complexe Impossible de mapper aux types de prop Webflow Diviser en props individuelles
    Props de fonction (callbacks) Non supporté dans Webflow Supprimer ou internaliser la logique
    Hook useContext Ne fonctionne pas entre les composants Utiliser des motifs d'état alternatifs
    Imports CSS externes Peut ne pas fonctionner en Shadow DOM Importer dans .webflow.tsx à la place
    Références de classe CSS aux styles globaux Ne fonctionnera pas en Shadow DOM Utiliser des styles limités au composant
    styled-components Nécessite un décorateur Shadow DOM Configurer globals.ts avec décorateur
    Emotion (@emotion/styled) Nécessite un décorateur Shadow DOM Configurer globals.ts avec décorateur
  4. Détecter l'approche de style et noter la configuration requise :

    Si utilisation de styled-components :

    npm i @webflow/styled-components-utils styled-components

    Créer/mettre à jour globals.ts :

    import { styledComponentsShadowDomDecorator } from "@webflow/styled-components-utils";
    export const decorators = [styledComponentsShadowDomDecorator];

    Si utilisation d'Emotion :

    npm i @webflow/emotion-utils @emotion/cache @emotion/react

    Créer/mettre à jour globals.ts :

    import { emotionShadowDomDecorator } from "@webflow/emotion-utils";
    export const decorators = [emotionShadowDomDecorator];

    Pour les deux approches CSS-in-JS, mettre à jour 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. Signaler les dépendances susceptibles de causer des problèmes :

    • Grandes bibliothèques (préoccupation de taille du bundle)
    • Bibliothèques uniquement pour le navigateur
    • Bibliothèques qui manipulent le DOM directement

Phase 2 : Mapper les props aux types Webflow

  1. Appliquer le mappage TypeScript → type de prop Webflow :

    Type TypeScript Prop Webflow Notes
    string props.Text() Défaut pour texte court
    string (contenu long/HTML) props.RichText() Si le nom de la prop suggère contenu/body/description
    React.ReactNode / children props.Slot() Pour contenu imbriqué
    number props.Number() Valeurs numériques
    boolean props.Boolean() Bascules
    "option1" \| "option2" props.Variant() Unions littérales de chaînes (nécessite tableau options)
    enum props.Variant() Convertir valeurs enum en tableau options (requis)
    { href: string; ... } props.Link() Retourne objet { href, target?, preload? } — peut nécessiter enveloppe si le composant s'attend à des props href/target distincts
    Types liés aux images props.Image() Src d'image, url, etc.
    string (texte éditable sur canvas) props.TextNode() Pour texte éditable directement sur le canvas ; a paramètre multiline
    boolean (afficher/masquer) props.Visibility() Basculement sémantique afficher/masquer
    string (pour id HTML) props.Id() Si prop nommée « id » ou utilisée pour l'accessibilité
    Objets complexes DIVISER Diviser en plusieurs props simples
    Fonctions/callbacks SUPPRIMER Non supporté
    Tableaux SPÉCIAL Peut nécessiter refonte du composant
  2. Gérer les cas particuliers :

    Props d'objet complexe - Les diviser :

    // Original
    interface Props {
      author: {
        name: string;
        avatar: string;
        bio: string;
      }
    }
    
    // Converti en props plates
    props: {
      authorName: props.Text({ name: "Author Name" }),
      authorAvatar: props.Image({ name: "Author Avatar" }),
      authorBio: props.RichText({ name: "Author Bio" })
    }

    Types union avec plus que de simples chaînes :

    // Original - union complexe
    type Size = "sm" | "md" | "lg" | { width: number; height: number };
    
    // Convertir en Variant avec uniquement options chaîne
    size: props.Variant({
      name: "Size",
      options: ["sm", "md", "lg", "custom"],
      defaultValue: "md"
    })
    // Remarque : custom size nécessiterait des props Number supplémentaires

    Props optionnelles - Fournir defaultValue pour les types de prop qui le supportent. Remarque : Link, Image, Slot et Id n'acceptent pas defaultValue.

    // Original
    interface Props {
      title?: string;
    }
    
    // Converti - fournir défaut pour types qui le supportent
    title: props.Text({
      name: "Title",
      defaultValue: ""  // Chaîne vide ou défaut sensé
    })

Phase 3 : Vérifier la configuration du projet

  1. Vérifier que la configuration Webflow existe :

    • Chercher webflow.json à la racine du projet
    • Chercher dépendances requises (@webflow/webflow-cli, @webflow/data-types, @webflow/react)
    • Si utilisation de styled-components/Emotion, chercher packages décorateur
    • Si manquant, proposer de configurer ou diriger vers compétence local-dev-setup
  2. Déterminer les emplacements des fichiers :

    • Identifier où vit le composant original
    • Déterminer où .webflow.tsx doit être créé (même répertoire)
    • Chercher styles existants à importer

Phase 4 : Générer le fichier de définition

  1. Créer le fichier .webflow.tsx :
import { declareComponent } from "@webflow/react";
import { props } from "@webflow/data-types";
import { ComponentName } from "./ComponentName";
// Importer styles s'ils existent
import "./ComponentName.module.css"; // ou .css

export default declareComponent(ComponentName, {
  name: "ComponentName",
  description: "[Généré à partir de la finalité du composant]",
  group: "[Catégorie appropriée]",
  props: {
    // Props mappées ici
  },
  // decorators: [], // Optionnel — décorateurs par composant (ex. pour support Shadow DOM CSS-in-JS)
  options: {
    applyTagSelectors: true, // Défaut est false. Définir à true pour appliquer les sélecteurs de balise Webflow (ex. styles h1, p) dans le composant.
    ssr: true // ou false si APIs du navigateur détectées
  }
});
  1. Fournir le fichier complet avec tous les props mappés

Phase 5 : Documenter les modifications requises

  1. Lister les modifications nécessaires au composant original :
## Modifications requises pour [ComponentName].tsx

### Doit corriger (Le composant ne fonctionnera pas sans) :
- [ ] Problème 1 : [Description et comment corriger]
- [ ] Problème 2 : [Description et comment corriger]

### Recommandé (Améliorera l'intégration Webflow) :
- [ ] Recommandation 1
- [ ] Recommandation 2

### Résumé du mappage des props :
| Prop original | Prop Webflow | Notes |
|---------------|--------------|-------|
| title: string | props.Text() | Mappage direct |
| isOpen: boolean | props.Boolean() | Mappage direct |
| data: object | DIVISER → dataTitle, dataValue | Aplati |
| onClick: function | SUPPRIMÉ | Non supporté |
  1. Fournir code de composant modifié si modifications nécessaires :
    • Afficher l'interface TypeScript mise à jour
    • Afficher tout enveloppe useEffect nécessaire pour SSR
    • Afficher modifications d'import de style
    • Noter exigences de configuration CSS-in-JS si applicable

Exemples

Exemple 1 : Conversion de composant simple

Demande utilisateur :

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

Réponse :

## Analyse du composant : TestimonialCard

### Props détectés :
| Prop | Type TypeScript | Mappage Webflow |
|------|-----------------|-----------------|
| quote | string | props.Text() |
| authorName | string | props.Text() |
| authorTitle | string (optionnel) | props.Text() |
| rating | number (optionnel) | props.Number() |

### Problèmes détectés :
- **Classe CSS « testimonial-card »** - Utilise classe globale qui ne fonctionnera pas en Shadow DOM
  - Correction : Convertir en CSS Modules ou importer styles limités

### Aucun problème bloquant - prêt à convertir !

---

## Fichiers générés

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

Modifications requises pour TestimonialCard.tsx

  1. Convertir en CSS Modules - Renommer TestimonialCard.css en TestimonialCard.module.css et mettre à jour imports :
import styles from "./TestimonialCard.module.css";

// CSS Modules par défaut utilise notation crochet :
<div className={styles["testimonial-card"]}>
<div className={styles["stars"]}>
// Notation pointée (styles.testimonialCard) nécessite noms de classe camelCase
// ou override webpack pour css-loader.

Vérification de la configuration

  • [ ] Vérifier que webflow.json existe
  • [ ] Installer dépendances si nécessaire : npm i --save-dev @webflow/webflow-cli @webflow/data-types @webflow/react

Exemple 2 : Composant avec motifs incompatibles

Demande utilisateur :

Make this work as a Webflow code component:

// 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<ModalProps> = ({ 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 }}>
      <div onClick={toggle}>{trigger}</div>
      {isOpen && (
        <div className="modal-overlay" onClick={toggle}>
          <div className="modal-content" onClick={e => e.stopPropagation()}>
            {children}
          </div>
        </div>
      )}
    </ModalContext.Provider>
  );
};

Réponse :

## Analyse du composant : Modal

### Problèmes détectés :

| Problème | Sévérité | Résolution |
|----------|----------|-----------|
| React Context (ModalContext) | Critique | Context ne fonctionnera pas entre les composants Webflow. Puisque c'est interne à Modal, c'est OK mais hook useModal exporté ne fonctionnera pas. |
| Manipulation de document.body | Critique | Incompatible SSR. Doit définir `ssr: false` ou envelopper dans vérification useEffect. |
| Callbacks onOpen/onClose | Avertissement | Props de fonction non supportées dans Webflow. Doit supprimer. |
| Classes CSS globales | Avertissement | « modal-overlay », « modal-content » ne fonctionneront pas en Shadow DOM. |

### Props détectés :
| Prop | Type TypeScript | Mappage Webflow | Statut |
|------|-----------------|-----------------|--------|
| trigger | React.ReactNode | props.Slot() | OK |
| children | React.ReactNode | props.Slot() | OK |
| onOpen | function | SUPPRIMER | Non supporté |
| onClose | function | SUPPRIMER | Non supporté |

---

## Modifications requises

### 1. Supprimer les props de fonction (onOpen, onClose)
Celles-ci ne peuvent pas être passées depuis Webflow Designer. Supprimer de l'interface et du composant.

### 2. Corriger le problème SSR avec document.body
Soit définir `ssr: false` soit ajouter vérification typeof :

```typescript
useEffect(() => {
  if (typeof document === 'undefined') return;
  // ... reste de l'effet
}, [isOpen]);

3. Convertir en CSS Modules

Renommer styles et mettre à jour références de classe.

4. Supprimer hook useModal exporté

Cela ne fonctionnera pas pour les composants externes. Garder ModalContext interne seulement.


Fichiers générés

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 mis à jour

import { useState, useEffect } from 'react';
import styles from "./Modal.module.css";

interface ModalProps {
  trigger: React.ReactNode;
  children: React.ReactNode;
  // Supprimé : onOpen, onClose - non supporté dans 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>
      )}
    </>
  );
};

Configuration requise

npm i --save-dev @webflow/webflow-cli @webflow/data-types @webflow/react

---

### Exemple 3 : Composant avec props d'objet complexe

**Demande utilisateur :**

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) => ( <div> <img src={product.image} alt={product.name} /> {showBadge && <span>{product.category}</span>} <h3>{product.name}</h3> <p>${product.price}</p> </div> );


**Réponse :**

```markdown
## Analyse du composant : ProductCard

### Props complexes détectés :

Le prop `product` est un objet complexe qui doit être aplati pour Webflow :

| Original | Props aplatis |
|----------|---------------|
| product.id | (Non nécessaire en UI - supprimer) |
| product.name | productName: props.Text() |
| product.price | productPrice: props.Number() |
| product.image | productImage: props.Image() |
| product.category | productCategory: props.Variant() |
| showBadge | showBadge: props.Boolean() |

---

## Fichiers générés

### 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 mis à jour

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>
);

Notes de migration :

  • L'objet product a été aplati en props individuels
  • Chaque prop est maintenant directement éditable dans Webflow Designer
  • product.id a été supprimé car non affiché dans l'UI
  • Prop Image utilise maintenant le type Image Webflow (fournit { src, alt })

Exemple 4 : Composant avec styled-components

Demande utilisateur :

Convert this styled-components React component to 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) => (
  <StyledButton variant={variant} disabled={disabled}>
    {label}
  </StyledButton>
);

Réponse :

## Analyse du composant : Button

### Approche de style détectée : styled-components

styled-components nécessite un décorateur Shadow DOM pour fonctionner dans Webflow.

### Props détectés :
| Prop | Type TypeScript | Mappage Webflow |
|------|-----------------|-----------------|
| label | string | props.Text() |
| variant | 'primary' \| 'secondary' | props.Variant() |
| disabled | boolean | props.Boolean() |

### Aucun problème bloquant - prêt à convertir !

---

## Configuration requise pour styled-components

**1. Installer dépendances :**
```bash
npm i @webflow/styled-components-utils styled-components
npm i -D @types/styled-components

2. Créer/mettre à jour src/globals.ts :

import { styledComponentsShadowDomDecorator } from "@webflow/styled-components-utils";
export const decorators = [styledComponentsShadowDomDecorator];

3. Mettre à jour webflow.json :

{
  "library": {
    "name": "My Library",
    "components": ["./src/**/*.webflow.@(js|jsx|mjs|ts|tsx)"],
    "globals": "./src/globals.ts"
  }
}

Fichiers générés

Button.webflow.tsx

import { declareComponent } from "@webflow/react";
import { props } from "@webflow/data-types";
import { Button } from "./Button";
// Aucun import CSS nécessaire - styled-components gère les 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
  }
});

Aucune modification nécessaire pour Button.tsx

Le composant peut rester tel quel. Le décorateur styled-components dans globals.ts gèrera l'injection de style Shadow DOM automatiquement.

Checklist de configuration

  • [ ] Installer @webflow/styled-components-utils
  • [ ] Créer globals.ts avec décorateur
  • [ ] Mettre à jour webflow.json pour référencer globals
  • [ ] Déployer avec npx webflow library share

Directives

Quand recommander refonte de composant

Certains composants ne correspondent fondamentalement pas au modèle Webflow :

  1. Utilisation importante de Context : Si le composant repose sur context au niveau application, suggérer refonte
  2. Machines d'état complexes : Peut nécessiter simplification
  3. Composants étroitement couplés : Chacun doit être indépendant dans Webflow
  4. Composants qui rendent des portals : Considérer si portal est nécessaire

Stratégie de valeur par défaut

Toujours fournir défauts sensés :

  • Props texte : Texte d'exemple représentatif
  • Nombres : Valeur commune/typique
  • Booléens : Cas d'utilisation le plus courant
  • Variantes : Option la plus populaire
  • Images : Peut être indéfini (optionnel)
  • Slots : Pas de défaut nécessaire

Nommage des props pour Webflow

Rendre noms de props designer-friendly :

  • Utiliser noms descriptifs : buttonText plutôt que txt
  • Éviter abréviations : imageSource plutôt que imgSrc
  • Grouper props associés avec préfixes : authorName, authorAvatar, authorBio

Décision SSR

Définir ssr: false si le composant :

  • Accède à window, document, navigator
  • Utilise localStorage ou sessionStorage
  • Manipule le DOM directement
  • Utilise bibliothèques nécessitant APIs du navigateur
  • Rend canvas, WebGL ou cartes

Skills similaires