Documentation/frontend/Frontend overview
In this section

Frontend overview

The server-side Netron lives inside Titan itself (@omnitron-dev/titan/netron, including all four transports). This section covers the browser side.

Browser-side packages:

PackageRoleFrameworkSize
@omnitron-dev/netron-browserRPC transport (HTTP + WebSocket), middleware, auth, multi-backend poolFramework-agnostic — vanilla JS, Vue, Svelte, Solid, Angular, Lit, React, Web Workers~25 kB gz
@omnitron-dev/netron-reactReact hooks, query/mutation cache, auth guards, devtoolsReact-only — optional layer on top of netron-browser~15 kB gz
@omnitron-dev/prismDesign system: MUI v9 components, blocks, layouts, forms, hooksReact-only (MUI v9 is React) — optionalDepends on selected components and peers; unused modules are discarded

netron-browser works everywhere. Add netron-react + prism only for React apps. For Vue / Svelte / Solid / Angular / Lit, use netron-browser directly and wrap calls in your framework's reactivity primitives.

How they compose

flowchart LR
  Comp["React component<br/>built with Prism"]
  Hook["netron-react hook<br/>(useQuery / useService)"]
  Client["netron-browser<br/>NetronClient"]
  Wire["HTTP / WebSocket"]
  Server["Titan @Service / @Public"]

  Comp --> Hook --> Client --> Wire --> Server

A typical screen:

import { DataGridBlock }   from '@omnitron-dev/prism/blocks';
import { useService }      from '@omnitron-dev/netron-react';

interface UserService {
  list(filter: UserFilter): Promise<User[]>;
}

function UsersPage() {
  const users = useService<UserService>('users');
  return (
    <DataGridBlock
      title="Users"
      columns={[
        { field: 'email',  header: 'Email' },
        { field: 'role',   header: 'Role'  },
      ]}
      query={({ page, sort, filter }) =>
        users.list.useQuery([{ page, sort, filter }])
      }
    />
  );
}

Three packages, one screen, no codegen.

End-to-end types

There is no schema file, no codegen step, no manual sync. The TypeScript compiler is the source of truth:

// Shared package — the contract:
export interface UsersService {
  findById(id: string): Promise<User | null>;
}

// Browser — type flows through:
const users = useService<UsersService>('users');
const { data } = users.findById.useQuery([id]);
//      ^? User | null | undefined

A method signature change on the server fails the build on every caller in the same tsc pass.

Wiring an app from scratch

import { NetronReactClient, NetronProvider }   from '@omnitron-dev/netron-react';
import { AuthProvider }                        from '@omnitron-dev/netron-react/auth';
import { PrismProvider, ProviderStack }        from '@omnitron-dev/prism/core';
import { RouterProvider }                      from 'react-router-dom';

const client = new NetronReactClient({
  url:       import.meta.env.VITE_API_URL,
  transport: 'http',
});

// `AuthProvider` takes a `config` (refreshEndpoint?, logoutEndpoint?,
// storage?: 'local' | 'session' | 'memory', autoRefresh?, refreshThreshold?)
// and an `onLogin` handler that performs the actual sign-in and returns
// an AuthResult.
function App() {
  return (
    <ProviderStack
      providers={[
        [NetronProvider, { client }],
        [AuthProvider,   {
          config:  { storage: 'local', autoRefresh: true, refreshEndpoint: '/auth/refresh' },
          onLogin: (credentials) => signIn(client, credentials),
        }],
        [PrismProvider,  { defaultSettings: { mode: 'dark', primaryColor: '#7c4dff' } }],
        [RouterProvider, { router }],
      ]}
    >
      <Outlet />
    </ProviderStack>
  );
}

Four providers, one app. Anything Titan-side becomes discoverable through useService. The auth API is login / logout (from useAuth()) with <AuthGuard> / <GuestGuard> / <RoleGuard> / <PermissionGuard> route guards — see netron-react / Auth for the full surface.

Multi-backend out of the box

Talking to several Netron servers? Swap NetronProvider for MultiBackendProvider:

import { createMultiBackendClient }
  from '@omnitron-dev/netron-browser';
import { MultiBackendProvider, useBackendService }
  from '@omnitron-dev/netron-react';

const client = createMultiBackendClient({
  baseUrl: 'https://api.example.com',
  backends: {
    auth:      { path: '/auth',      transport: 'http' },
    media:     { path: '/media',     transport: 'http' },
    analytics: { path: '/analytics', transport: 'http' },
  },
  routing: {
    patterns: [
      { pattern: 'users.',   backend: 'auth' },
      { pattern: 'objects.', backend: 'media' },
      { pattern: 'reports.', backend: 'analytics' },
    ],
  },
});

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

// In components:
const users = useBackendService<UserService>('auth', 'users');

→ netron-react / Multi-backend.

What lives where

You want…Reach for
Theme, colors, typography@omnitron-dev/prism/theme
Dashboard / auth / data-grid shell@omnitron-dev/prism/blocks
Sidebar + topbar layout@omnitron-dev/prism/layouts
Card / Table / Drawer / Form field@omnitron-dev/prism or @omnitron-dev/prism/components
Schema-aware forms@omnitron-dev/prism/forms
Persisted UI state (sidebar open, dark mode)@omnitron-dev/prism/state
Plain React hooks (useArray, useFocusTrap, …)@omnitron-dev/prism/hooks
Type-safe RPC call inside a componentuseService from @omnitron-dev/netron-react
Live data over WebSocketuseSubscription from @omnitron-dev/netron-react
Sign-in flow + route guards@omnitron-dev/netron-react/auth + <AuthGuard>
Multi-backend setupMultiBackendProvider from @omnitron-dev/netron-react
Raw RPC client (no React)createClient from @omnitron-dev/netron-browser
Cross-tab auth syncAuthenticationClient from @omnitron-dev/netron-browser
Cache, retry, circuit breaker@omnitron-dev/netron-browser/middleware

Bundle strategy

StageBundle goal
First paint (auth screen)Load the auth screen and its providers
Authenticated shellAdd the dashboard layout and shared navigation after authentication
Per-route pageLazy-load with React.lazy() and measure the emitted route chunks
Charts / editor / mapsLoad engines and styles with the routes that use them

Named imports from the root and @omnitron-dev/prism/components discard unused modules. Public subpaths such as components/card, components/chart, components/map, components/editor and components/emoji-picker are also available for explicit composition and lazy loading. Only declared exports are public; there is no wildcard mapping from every source directory.

Import each component's documented CSS entry when required. CSS remains in the production output. The package archive check verifies that a simple Card consumer does not retain optional engines and that selected calendar, map and stylesheet dependencies remain. It does not set an application-wide size promise: measure initial requests, route chunks and shared peer dependencies in the actual production build.

Production reference

The Omnitron Console (apps/omnitron/webapp/) is built entirely from these three packages. ~20 pages, multi-backend, full RBAC, real-time subscriptions, devtools — see Console for the route map.

Read on