🏰 @statelyjs/ui

Base UI components, layout, and plugin infrastructure for Stately applications

npm License

This package provides the foundational UI layer for Stately: React components, theming, layout primitives, and the plugin runtime system. It has no knowledge of Stately's entity system or backend - those concerns live in @statelyjs/stately.

Installation

pnpm add @statelyjs/ui

Most users should install @statelyjs/stately instead, which includes this package with the core plugin pre-configured. Use @statelyjs/ui directly when:

  • Accessing the base components, layouts, or views
  • Building a custom plugin
  • Want fine-grained control over plugin composition
  • Stately's underlying types and logic without core's entity system (would require schema also)

What's Included

Components (@statelyjs/ui/components)

Styled, accessible React components built on Radix UI primitives:

  • Base components: Button, Input, Select, Dialog, Dropdown, Tabs, etc.
  • Form components: Field wrappers, validation states, form actions
  • Layout components: Sidebar, Header, Navigation
  • Utility components: Spinner, Badge, Tooltip, etc.

Layout (@statelyjs/ui/layout)

App shell with responsive sidebar navigation:

import { Layout } from '@statelyjs/ui/layout';

function App() {
  return (
    <Layout>
      <YourContent />
    </Layout>
  );
}

Theme (@statelyjs/ui)

Dark/light/system mode support with system preference detection:

import { ThemeProvider, ThemeToggle, useTheme } from '@statelyjs/ui';

function App() {
  return (
    <ThemeProvider defaultTheme="system">
      <ThemeToggle />
      <YourContent />
    </ThemeProvider>
  );
}

Plugin Runtime

Infrastructure for building and composing UI plugins:

import { createStatelyUi, createUseStatelyUi, StatelyUiProvider } from '@statelyjs/ui';

// Create a runtime with your schema
const runtime = createStatelyUi({ schema, client, options });

// Add plugins
const runtimeWithPlugins = runtime
  .withPlugin(myPlugin())
  .withPlugin(anotherPlugin());

Registry System

Component and transformer registries for dynamic field rendering:

import { registry } from '@statelyjs/ui';

// Register a custom field component
registry.components.register('myNodeType', 'edit', MyEditComponent);
registry.components.register('myNodeType', 'view', MyViewComponent);

Exports

Main (@statelyjs/ui)

import {
  // Runtime
  createStatelyUi,
  createStatelyUiProvider,
  createUseStatelyUi,
  StatelyUiProvider,
  
  // Theme
  ThemeProvider,
  ThemeToggle,
  useTheme,
  
  // Layout
  Layout,
  
  // Form and node type routers
  BaseForm,
  
  // Registry
  registry,
  
  // Utilities
  cn,
  toTitleCase,
  toKebabCase,
  devLog,
} from '@statelyjs/ui';

Components (@statelyjs/ui/components)

import { Editor, Note, CopyButton, GlowingSave } from '@statelyjs/ui/components';

Base Components (@statelyjs/ui/components/base)

All shadcn/ui style components:

import {
  Button,
  Input,
  Select,
  Dialog,
  DropdownMenu,
  Tabs,
  Card,
  Badge,
  // ... and many more
} from '@statelyjs/ui/components/base';

Form (@statelyjs/ui/form)

import { BaseForm, FieldEdit, FieldView, FormActions } from '@statelyjs/ui/form';

Hooks (@statelyjs/ui/hooks)

import { useCopyToClipboard, useIsMobile, useLocalStorage } from '@statelyjs/ui/hooks';

Layout (@statelyjs/ui/layout)

import { Layout, Header, PageHeader, Navigation } from '@statelyjs/ui/layout';

Dialogs (@statelyjs/ui/dialogs)

import { ConfirmationDialog, DeleteDialog } from '@statelyjs/ui/dialogs';

Utilities

import { cn } from '@statelyjs/ui/lib/utils';
import { devLog, devLogger } from '@statelyjs/ui/lib/logging';

Styling

Stately requires Tailwind CSS v4. Configure your app's CSS as follows:

/* Import Tailwind */
@import "tailwindcss";

/* Import Stately theme tokens and base styles */
@import "@statelyjs/ui/styles.css";

/* Scan your app source */
@source ".";

/* 
 * Scan all Stately packages for utility classes.
 * node_modules is excluded by default, so explicit @source is required.
 * This single directive covers stately, ui, and any @statelyjs plugins.
 */
@source "./node_modules/@statelyjs";

The @source directive tells Tailwind to scan Stately packages for utility class usage, ensuring all component styles are included in your build.

Writing a Plugin

Plugins extend the UI runtime with custom functionality:

import type { DefineUiPlugin, UiPluginFactory } from '@statelyjs/ui';

// Define plugin shape
export type MyPlugin = DefineUiPlugin<
  'my-plugin',           // Plugin name
  MyPluginOptions,       // Options type
  MyPluginRoutes,        // Routes type
  MyPluginUtils          // Utils type
>;

// Create plugin factory
export function myPlugin(options: MyPluginOptions): UiPluginFactory<MyPlugin> {
  return (runtime) => ({
    name: 'my-plugin',
    options,
    routes: { /* navigation items */ },
    utils: { /* helper functions */ },
    api: {
      operations: createOperations(runtime.client, options),
    },
  });
}

See @statelyjs/files and @statelyjs/arrow for complete plugin examples.

Peer Dependencies

  • react ^18.0.0 || ^19.0.0
  • react-dom ^18.0.0 || ^19.0.0
  • lucide-react ^0.562.0
  • sonner ^2.0.7
  • openapi-fetch ^0.15

Optional

  • @uiw/react-codemirror ^4.25.3 - Required for the Editor component

License

Apache-2.0

Modules