@editora/ui-react

React wrappers, providers, and hooks for @editora/ui-core Web Components.

Overview

@editora/ui-react provides React-native ergonomics on top of Web Components from @editora/ui-core. It includes typed wrappers, hooks for common workflows, and providers for dialogs and theme state.

Installation

npm install @editora/ui-react @editora/ui-core

Peer dependencies: react, react-dom.

Quick Start

import { Button, Input, ThemeProvider } from '@editora/ui-react';

export function App() {
  return (
    <ThemeProvider>
      <div style={{ display: 'grid', gap: 12, maxWidth: 360 }}>
        <Input name="title" label="Title" placeholder="Untitled" clearable />
        <Button variant="primary">Save</Button>
      </div>
    </ThemeProvider>
  );
}

Import Rule

Prefer package-root imports from @editora/ui-react. If you deep-import wrappers directly, ensure @editora/ui-core is imported first so custom elements are registered.

Providers and Hooks

API Type Use Case
ThemeProvider, useTheme Provider + hook Token-based theming and persisted theme state.
DialogProvider, useDialog Provider + hook Promise-based confirm/prompt dialog workflows.
AlertDialogProvider, useAlertDialog Provider + hook Destructive/alert flow confirmations with accessibility defaults.
useForm Hook Validation, submit, dirty-state, reset, and value extraction from wrapped forms.

Common Patterns

1. Form + useForm

import { Form, Field, Input, Button, useForm } from '@editora/ui-react';

export function ProfileForm() {
  const { ref, submit, validate, getValues, reset, isDirty } = useForm();

  return (
    <Form
      ref={ref}
      autosave
      guardUnsaved
      onSubmit={(values) => console.log('submit', values)}
      onInvalid={(errors) => console.log('invalid', errors)}
    >
      <Field label="Full name" required>
        <Input name="fullName" required />
      </Field>

      <Field label="Email" required>
        <Input name="email" type="email" required />
      </Field>

      <Button onClick={() => submit()} disabled={!isDirty()}>Submit</Button>
      <Button variant="secondary" onClick={() => reset()}>Reset</Button>
      <Button variant="secondary" onClick={() => console.log(getValues())}>Values</Button>
    </Form>
  );
}

2. Dialog providers

import { DialogProvider, useDialog, AlertDialogProvider, useAlertDialog, Button } from '@editora/ui-react';

function Actions() {
  const dialog = useDialog();
  const alerts = useAlertDialog();

  return (
    <div style={{ display: 'flex', gap: 8 }}>
      <Button onClick={async () => {
        const res = await dialog.confirm({ title: 'Archive project?' });
        console.log(res);
      }}>Confirm</Button>

      <Button variant="secondary" onClick={async () => {
        const res = await alerts.prompt({ title: 'Rename' });
        console.log(res);
      }}>Prompt</Button>
    </div>
  );
}

export function DialogExample() {
  return (
    <DialogProvider>
      <AlertDialogProvider>
        <Actions />
      </AlertDialogProvider>
    </DialogProvider>
  );
}

3. Data table workflows

import { DataTable, Pagination } from '@editora/ui-react';
import { useState } from 'react';

export function UsersTable() {
  const [page, setPage] = useState(1);

  return (
    <div style={{ display: 'grid', gap: 10 }}>
      <DataTable
        sortable
        selectable
        page={page}
        pageSize={10}
        paginationId="users-pager"
        onPageChange={(d) => setPage(d.page)}
        onSortChange={(d) => console.log('sort', d)}
        onRowSelect={(d) => console.log('rows', d.indices)}
      >
        {/* table markup */}
      </DataTable>

      <Pagination id="users-pager" page={String(page)} />
    </div>
  );
}

Component Groups

  • Forms: Form, Field, Input, Textarea, Select, Combobox, Switch, Checkbox, DatePicker, ColorPicker
  • Data: DataTable, Pagination, Table, Calendar, Chart, Timeline, Progress
  • Overlay: Dialog, AlertDialog, Popover, Tooltip, Dropdown, Drawer
  • Layout: Layout, Sidebar, Tabs, Grid, Flex, Container, Section

Theming

import { ThemeProvider } from '@editora/ui-react';

<ThemeProvider
  tokens={{
    colors: { primary: '#0f766e', text: '#0f172a', background: '#ffffff' },
    radius: '10px'
  }}
  storageKey="my-app.theme"
>
  {/** app */}
</ThemeProvider>

SSR and StrictMode Notes

  • Providers are SSR-safe and create host nodes on client mount.
  • Promise dialog providers are StrictMode-safe and clean up on unmount.
  • Avoid calling custom element imperative methods until component mount is complete.

Production Guidance

  • Keep root imports from @editora/ui-react to avoid registration drift.
  • Batch expensive state updates around large table operations.
  • Prefer declarative props over direct DOM mutation of wrapped components.

Related Docs