Documentation/frontend/Overview
In this section

Prism

@omnitron-dev/prism is the design system. Pre-composed, theme-aware components and entire UI blocks for building production React frontends — built on MUI v9 + React 19, integrated with react-hook-form + zod, ready for Vite / Next / Remix out of the box.

pnpm add @omnitron-dev/prism

For React Server Components, put providers and interactive Prism components at the client boundary. The published UI entry points declare "use client". Import @omnitron-dev/prism/http directly for server-side HTTP calls.

Three layers of API

flowchart LR
  Blocks[Blocks<br/>full-page composites]
  Layouts[Layouts<br/>shell + slots]
  Components[Components<br/>composable widgets]
  Core[Core primitives<br/>theme, providers, types]

  Blocks --> Layouts
  Blocks --> Components
  Layouts --> Components
  Components --> Core
  Layouts --> Core

Pick the level that matches your need:

  • Blocks — copy a <DashboardBlock>, fill the slots, ship a screen.

  • Layouts — own the routing; use <DashboardLayout> for shell.

  • Components — assemble from <Card> / <Table> / <Drawer>.

  • Theme — palette, typography, shadows, density, dark mode.

  • Forms — schema-aware forms with <Field> + SchemaProvider.

  • Hooks catalog — all 48 React hooks.

  • Maps — self-hosted MapLibre map, markers, point picker and coverage layer.

Subpath exports

Public subpaths are explicit package exports with matching JavaScript and TypeScript declarations. For example, ./components/card, ./components/table, ./components/tabs, ./components/chart and ./components/map are available. A source directory by itself does not create a public import path; there are no wildcard component exports.

Named imports from the root or ./components discard unused component modules. Importing Card does not retain the editor, maps or chart engines. Choose a public subpath when it makes a route's lazy-loading boundary clearer. Editors, maps, research documents and emoji pickers should load with the routes that use them.

Import documented styles.css entries explicitly for components that need CSS; these styles remain side-effectful and survive production optimization. Calendar locale registration stays with its component. CI builds consumers from the real package archive and verifies both unused dependency removal and preservation of calendar, map and stylesheet dependencies. Run pnpm check:packages after the workspace build to repeat this check. Measure final application chunks separately: the size depends on selected components, peers and route boundaries.

SubpathWhat it exports
@omnitron-dev/prismNamed imports across the public API; unused modules are discarded
@omnitron-dev/prism/themecreatePrismTheme(), palette, typography, shadows, presets
@omnitron-dev/prism/core<PrismProvider>, <ProviderStack>, context primitives
@omnitron-dev/prism/layouts<DashboardLayout>, <AuthCenteredLayout> / <AuthSplitLayout> / <AuthSimpleLayout>, <LayoutProvider>
@omnitron-dev/prism/blocks<AuthBlock>, <DashboardBlock>, <DataGridBlock>
@omnitron-dev/prism/componentsNamed imports from the component catalog
@omnitron-dev/prism/components/cardCard, CardSection, StatCard
@omnitron-dev/prism/components/tableTable, useTable
@omnitron-dev/prism/components/mapMapLibre map, markers, layers and place search
@omnitron-dev/prism/components/editor<Editor> alone, as its own chunk
@omnitron-dev/prism/components/emoji-picker<EmojiPicker> alone — the dataset is ~80 kB gz
@omnitron-dev/prism/formsSchema-aware form helpers
@omnitron-dev/prism/hooks48 React hooks
@omnitron-dev/prism/stateZustand-based store factory
@omnitron-dev/prism/accessibilityVision simulation provider and controls
@omnitron-dev/prism/netronPre-wired Netron auth/UI bindings
@omnitron-dev/prism/httpHTTP fetcher helpers
@omnitron-dev/prism/cliCLI helpers (used by prism bin)

Tree-shaking works on every subpath — import the smallest scope your bundler needs.

Minimum wiring

import { PrismProvider } from '@omnitron-dev/prism/core';

function App() {
  return (
    <PrismProvider
      defaultSettings={{ mode: 'dark', preset: 'midnight', primaryColor: '#7c4dff' }}
    >
      <Outlet />
    </PrismProvider>
  );
}

<PrismProvider> builds the theme itself — you do not call createPrismTheme() and hand it over, and there is no theme prop to hand it to. It reads the settings store (persisted, so a user's own choices survive a reload), falls back to defaultSettings for anything unset, resolves mode: 'system' against the OS preference, and calls createPrismTheme() with the result. The Omnitron console supplies mode, preset and direction through this settings interface.

defaultSettings accepts mode, preset, direction, primaryColor, contrast, fontSize, fontFamily and navLayout. For MUI-level escapes use themeOverrides, which is merged into the generated theme; config carries non-theme configuration. createPrismTheme() is exported for building a theme outside the provider (a standalone MUI tree, a snapshot test), not for feeding one back in.

Beyond the theme, the provider sets up MUI's ThemeProvider, CssBaseline, the snackbar host, the icon registry, and the localization context (dateAdapterLocale, dateLocaleText).

For more sophisticated apps, use <ProviderStack> to layer multiple providers cleanly:

<ProviderStack
  providers={[
    [QueryClientProvider, { client: queryClient }],
    [AuthProvider,         { client: authClient }],
    [PrismProvider,        { defaultSettings: { mode: 'dark', preset: 'midnight' } }],
    [RouterProvider,       { router }],
  ]}
>
  <Outlet />
</ProviderStack>

State management — createPrismStore / createPersistedStore

Prism ships Zustand-based store factories (Immer + DevTools + optional localStorage persistence baked in). Use createPrismStore for general state and createPersistedStore for persisted state with version-aware migration:

import { createPrismStore } from '@omnitron-dev/prism/state';

interface UIState {
  sidebarOpen: boolean;
  toggleSidebar: () => void;
}

// Immer middleware is enabled — mutate `state` directly in `set`.
export const useUIStore = createPrismStore<UIState>((set) => ({
  sidebarOpen:   true,
  toggleSidebar: () => set((s) => { s.sidebarOpen = !s.sidebarOpen; }),
}), {
  name:    'ui-store',
  persist: { partialize: (s) => ({ sidebarOpen: s.sidebarOpen }) },
});

For persisted preferences with migrations, pass a version plus a migrate callback through the persist options:

import { createPersistedStore } from '@omnitron-dev/prism/state';

export const useSettings = createPersistedStore<SettingsState>(
  (set) => ({ /* … */ }),
  'settings',                       // store name (positional)
  {
    version: 2,
    migrate: (persisted, version) => {
      // version-aware migration
      return persisted as SettingsState;
    },
  },
);

When you bump version, persisted state from older versions runs through migrate before being adopted. (See also the versioned-settings helpers in @omnitron-dev/prism/state.)

Accessibility

@omnitron-dev/prism/accessibility exports vision simulation through VisionModeProvider, useVisionMode, VISION_MODE_OPTIONS and getVisionModeStyles. See Accessibility for the actual surface and an application-level verification workflow.

Netron integration — @omnitron-dev/prism/netron

Pre-wired auth + UI bindings for apps that talk to a Titan backend via Netron:

import { NetronProvider, createNetronClient }
  from '@omnitron-dev/prism/netron';

const client = createNetronClient({ transport: 'http', url: '/api' });

function App() {
  return (
    <NetronProvider client={client}>
      <Outlet />
    </NetronProvider>
  );
}

For multi-backend setups:

import { createMultiBackendClient, MultiBackendProvider }
  from '@omnitron-dev/prism/netron';

const client = createMultiBackendClient({
  baseUrl: '',
  backends: {
    main:    { path: '/api/main' },
    storage: { path: '/api/storage' },
    realtime:{ path: '/api/realtime' },
  },
  defaultBackend: 'main',
});

<MultiBackendProvider client={client} autoConnect>
  <Outlet />
</MultiBackendProvider>

The bindings re-export @omnitron-dev/netron-react hooks — see netron/react for the full hook reference.

CLI — prism binary

prism init                 # scaffold Prism config in current project
prism add card   # generate boilerplate using registered template
prism list --components      # show available components

The CLI uses templates from templates/ (shipped with the package) plus the schema in registry.json for available component metadata.

Best practices

  • Pick the smallest layer. A <DashboardBlock> is faster than composing it from 12 components, but constrains you to its prop API. Drop to <DashboardLayout>

    • components when you need flexibility.

  • One <PrismProvider> per app. Multiple providers create duplicate snackbar hosts and confused theme contexts.

  • Use declared entry points. Consult the API index; arbitrary per-component paths are not published.

  • Use schema-driven forms. <Field> + SchemaProvider produces consistent UX with zero per-field boilerplate.

  • Surface form errors with <FormAlert> inline; reserve toasts (<Snackbar>) for transient background events.

  • createPrismStore / createPersistedStore over raw Zustand for any state that persists — the versioned-settings helpers handle migrations.

See also

Further guides