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/prismFor 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 --> CorePick 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.
| Subpath | What it exports |
@omnitron-dev/prism | Named imports across the public API; unused modules are discarded |
@omnitron-dev/prism/theme | createPrismTheme(), 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/components | Named imports from the component catalog |
@omnitron-dev/prism/components/card | Card, CardSection, StatCard |
@omnitron-dev/prism/components/table | Table, useTable |
@omnitron-dev/prism/components/map | MapLibre 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/forms | Schema-aware form helpers |
@omnitron-dev/prism/hooks | 48 React hooks |
@omnitron-dev/prism/state | Zustand-based store factory |
@omnitron-dev/prism/accessibility | Vision simulation provider and controls |
@omnitron-dev/prism/netron | Pre-wired Netron auth/UI bindings |
@omnitron-dev/prism/http | HTTP fetcher helpers |
@omnitron-dev/prism/cli | CLI 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 componentsThe 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>+SchemaProviderproduces consistent UX with zero per-field boilerplate.Surface form errors with
<FormAlert>inline; reserve toasts (<Snackbar>) for transient background events.createPrismStore/createPersistedStoreover raw Zustand for any state that persists — the versioned-settings helpers handle migrations.
See also
Components catalog — 50+ widgets with props + examples
Blocks —
<AuthBlock>,<DashboardBlock>,<DataGridBlock>Layouts — three app shells
Theme — palette, typography, dark mode
Forms — schema-aware form patterns
Hooks catalog — React hooks
netron-browser — the RPC client
netron-react — React hooks for RPC
Frontend overview — the three-package picture
Further guides
State and persistence — stores, hydration and migrations.
HTTP client — REST requests, cancellation and interceptors.
Component CLI — copy and maintain local component source.
API index — every declared public export.