react-to-web-component

repository·main·Indexed 21 days ago

https://github.com/bitovi/react-to-web-component

A utility for converting React components into native Web Components (Custom Elements), allowing them to be used in any HTML environment or framework such as Vue, Svelte, Angular, and Ember. It includes @r2wc/core for the conversion engine and @r2wc/react-to-web-component for end-users. The library supports property synchronization, custom attribute mapping via R2WCOptions, and Shadow DOM configuration. The latest version requires React 18, while version 1 supports React 16 and 17.

Tokens
3.8K
Snippets
13
Records
18
Agent score
75%

What's inside react-to-web-component

  1. What is @r2wc/core?

    main

    @r2wc/core is the core engine of the React to Web Component system. It converts React components into native Custom Elements (Web Components).

    Key characteristics:

    • The resulting custom element acts as a wrapper for the underlying React component.
    • It allows React components to be used in any HTML-based environment (Vue, Svelte, Angular, Ember, etc.) without requiring the consumer to mount them through React.
    • Note: This is an internal library. End-users should generally use renderers designed for this system, such as @r2wc/react-to-web-component, rather than using @r2wc/core directly.
  2. How r2wc works under the hood

    main

    The r2wc function creates a CustomElementConstructor equipped with custom getters, setters, and lifecycle methods.

    Key Behaviors:

    • Re-rendering: When a defined property is set on the custom element, the setter triggers a re-render of the underlying React component.
    • Property Mapping: Enumerable properties and values on the custom element instance are passed directly as props to the React component.
    • Optimization: The custom setter creates an enumerable getter/setter on the instance to store the value, preventing repeated hits to the proxy.
    • Lazy Rendering: The React component is not rendered until the custom element is actually inserted into the DOM.
  3. How @r2wc/core works

    main

    The library works by creating a CustomElementConstructor that manages the lifecycle and property synchronization between the Web Component and the React component.

    Property Synchronization: When a property is set on the custom element, the internal setter:

    1. Re-renders the React component inside the custom element with the new props.
    2. Creates an enumerable getter/setter on the instance to cache the value and avoid unnecessary proxy hits in the future.

    Data Flow:

    • Props: Enumerable properties and values set on the custom element are passed directly to the React component as props.
    • Rendering: The React component is not rendered until the custom element is actually inserted into the DOM.
  4. Use Web Components declaratively

    main

    You can use Web Components declaratively by adding their custom tag names to the HTML markup (e.g., via innerHTML). Once the element exists in the DOM, you can access it and update its properties to trigger updates in the component.

    document.body.innerHTML = "<web-greeting></web-greeting>"
    
    document.body.firstChild.name = "I do declare"
    
    document.body.firstChild.innerHTML //-> "<h1>Hello, I do declare</h1>"
  5. Pass attributes to a React-based Web Component

    main

    When using a Web Component generated by reactToWebComponent, you can pass data via HTML attributes. To ensure compatibility with React props, convert camelCase prop names to kebab-case in your HTML.

    For example, if your React component has a colorMode prop, use the color-mode attribute in the HTML tag.

    <!-- Using kebab-case for attributes to map to camelCase React props -->
    <web-greeting
      name="Sven"
      description="How do you do?"
      color-mode="dark"
      button-variant="contained"
    ></web-greeting>
  6. Use Web Components programmatically

    main

    You can instantiate and manipulate Web Components using standard DOM APIs. Create the element using document.createElement, set its properties directly, and then append it to the document. Changes to properties will reflect in the component's internal rendering.

    const webGreeting = document.createElement("web-greeting")
    webGreeting.name = "Justin"
    
    document.body.append(webGreeting)
    
    webGreeting.innerHTML //-> "<h1>Hello, Justin</h1>"
  7. Install @r2wc/react-to-web-component

    main

    Install the package via npm to begin converting React components into custom elements.

    Note: The latest version of this package requires React 18. If you are using React 16 or 17, you must use version 1.

    npm install @r2wc/react-to-web-component
  8. Convert a React component to a Web Component

    main

    To convert a React component, import r2wc and pass your component to it. The function returns a constructor that you can then register using the standard customElements.define API.

    By default, r2wc only passes properties to the underlying React component. It does not automatically map HTML attributes to React props unless they are explicitly defined in the configuration.

    import r2wc from "@r2wc/react-to-web-component"
    
    const Greeting = () => {
      return <h1>Hello, World!</h1>
    }
    
    const WebGreeting = r2wc(Greeting)
    
    customElements.define("web-greeting", WebGreeting)
  9. Use reactToWebComponent with React components using third-party libraries

    main

    The reactToWebComponent function can wrap React components that rely on external libraries (such as Material UI's ThemeProvider or Button) to expose them as Web Components. When defining the component via reactToWebComponent, you must specify the expected types for the props in the configuration object.

    Note that when passing values via HTML attributes, you should use kebab-case (e.g., color-mode instead of colorMode) to map to the React component's props.

    import { Button } from "@mui/material"
    import { ThemeProvider, createTheme } from "@mui/material/styles"
    
    interface GreetingProps {
      name: string
      description: string
      colorMode?: "light" | "dark" | undefined
      buttonVariant?: "contained" | "text" | "outlined" | undefined
    }
    
    export const Greeting = ({
      name,
      description,
      colorMode = "light",
      buttonVariant = "text",
    }: GreetingProps) => {
      const themeMode = createTheme({
        palette: {
          mode: colorMode,
        },
      })
    
      return (
        <ThemeProvider theme={themeMode}>
          <main>
            <h1>Hello, {name}</h1>
            <p>{description}</p>
            <Button variant={buttonVariant}>This is the button</Button>
          </main>
        </ThemeProvider>
      )
    }
    
    const WebGreeting = reactToWebComponent(Greeting, {
      props: {
        name: "string",
        description: "string",
        colorMode: "string",
        buttonVariant: "string",
      },
    })
    
    customElements.define("web-greeting", WebGreeting)
  10. Configure attributes for React props

    main

    To allow your custom element to respond to HTML attributes (e.g., <web-greeting name="Justin"></web-greeting>), you must provide a configuration object as the second argument to r2wc. This object must include a props mapping that defines the expected types for your component's props.

    const Greeting = ({ name }) => {
      return <h1>Hello, {name}!</h1>
    }
    
    const WebGreeting = r2wc(Greeting, {
      props: {
        name: "string",
      },
    })
    
    customElements.define("web-greeting", WebGreeting)
  11. Configure r2wc options

    main

    The R2WCOptions object allows you to customize how the resulting Web Component behaves.

    Options:

    • shadow: Specifies the Shadow DOM mode. Use 'open' or 'closed'. If omitted, the component renders into the light DOM.
    • props: Defines which React props should be exposed as attributes or properties. It accepts:
      • An array of strings: ['propOne', 'propTwo']. All will be treated as strings.
      • A mapping object: { propOne: 'json', propTwo: 'number' }. This allows you to specify the transformation type for each prop.
    • events: Defines which React props should be treated as custom events. It accepts:
      • An array of strings: ['onCustomEvent']. These will be dispatched as CustomEvents when the React prop is called.
      • A mapping object: { onCustomEvent: { detail: 'some-value' } } to provide EventInit configuration.
    const options = {
      shadow: 'open',
      props: {
        count: 'number',
        user: 'json',
        isAdmin: 'boolean'
      },
      events: {
        onSelect: { bubbles: true }
      }
    };
  12. Convert a React component to a Web Component with reactToWebComponent()

    main

    Use reactToWebComponent(ReactComponent, React, ReactDOM, options) to create a class inheriting from HTMLElement. This class can be passed to customElements.define or extended.

    Parameters:

    • ReactComponent: The React component to convert.
    • React: The React library.
    • ReactDOM: The ReactDOM library.
    • options: Configuration object (see Typed Props for details).

    Options:

    • shadow: `