ce-la-react

0.3.2 · active · verified Sun Apr 19

ce-la-react is a utility library designed to simplify the integration of vanilla custom elements with React components, providing a wrapper that addresses common issues with prop passing and event handling. The current stable version is 0.3.2. It maintains a fairly active release cadence with frequent patch releases, indicating ongoing development. A key differentiator from similar libraries like `@lit/react` is its stronger emphasis on server-side rendering (SSR) by favoring attributes over properties for primitive values. It also offers configurable conversion functions for properties to attributes and supports custom element templates via a static `getTemplateHTML` method, offering a robust solution for developers building component libraries with Web Components that need to be consumed by React applications.

Common errors

Warnings

Install

Imports

Quickstart

This code demonstrates how to create a React component wrapper for a custom element using `createComponent`. It includes a minimal, self-contained custom element definition and shows how to pass props from React to the custom element and listen for custom events with type-safety using `EventName`.

import * as React from 'react';
import { createComponent } from 'ce-la-react';
import type { EventName } from 'ce-la-react';

// Define a simple custom element for demonstration purposes
class MyToggleElement extends HTMLElement {
  static observedAttributes = ['active'];
  private _active: boolean = false;

  constructor() {
    super();
    this.attachShadow({ mode: 'open' });
    this.shadowRoot!.innerHTML = `<style>
      :host { display: inline-block; padding: 8px; border: 1px solid #ccc; border-radius: 4px; }
      button { padding: 8px 12px; cursor: pointer; background: #007bff; color: white; border: none; border-radius: 3px; }
      button.active { background: #28a745; }
    </style>
    <button>${this._active ? 'Active' : 'Inactive'}</button>`;

    this.shadowRoot!.querySelector('button')!.onclick = () => {
      this.active = !this.active;
    };
  }

  set active(value: boolean) {
    if (this._active === value) return;
    this._active = value;
    if (value) {
      this.setAttribute('active', '');
    } else {
      this.removeAttribute('active');
    }
    this.updateButtonText();
    this.dispatchEvent(new CustomEvent('toggle', { detail: { active: value }, bubbles: true, composed: true }));
  }

  get active(): boolean {
    return this._active;
  }

  attributeChangedCallback(name: string, oldValue: string, newValue: string) {
    if (name === 'active') {
      this.active = newValue !== null;
    }
  }

  private updateButtonText() {
    const button = this.shadowRoot!.querySelector('button');
    if (button) {
      button.textContent = this._active ? 'Active' : 'Inactive';
      button.classList.toggle('active', this._active);
    }
  }
}

customElements.define('my-toggle-element', MyToggleElement);

interface ToggleEventDetail { active: boolean; }

// Create the React component wrapper
export const MyToggleComponent = createComponent({
  tagName: 'my-toggle-element',
  elementClass: MyToggleElement,
  react: React,
  events: {
    onToggle: 'toggle' as EventName<CustomEvent<ToggleEventDetail>>
  },
});

// Example React usage
export function App() {
  const [isActive, setIsActive] = React.useState(false);

  return (
    <div>
      <h1>ce-la-react Custom Element Demo</h1>
      <p>React State: {isActive ? 'ON' : 'OFF'}</p>
      <MyToggleComponent
        active={isActive}
        onToggle={(e: CustomEvent<ToggleEventDetail>) => {
          console.log('Custom event received:', e.detail.active);
          setIsActive(e.detail.active);
        }}
      />
      <p>Click the custom element button to toggle its state and see React update.</p>
    </div>
  );
}

// To run this in a real app, you would typically render `App`:
// import { createRoot } from 'react-dom/client';
// const container = document.getElementById('root');
// if (container) {
//   const root = createRoot(container);
//   root.render(<App />);
// }

view raw JSON →