React components
The full @flowpanel/react export surface, grouped by what it's for.
@flowpanel/react is the component library the admin renders with. Most of it
is consumed indirectly — resources, widgets and forms compile down to these
components — but every export is public and safe to use directly when you
build a custom slot, page, or "bare"-shell chrome around FlowPanel.
import { AutoForm, DataTable, useAdminTable } from "@flowpanel/kit/react";@flowpanel/kit/react is the specifier to reach for: @flowpanel/kit is what
flowpanel init installs, and it re-exports this whole surface. The
standalone @flowpanel/react package publishes the same exports and works
identically — use it only when you depend on it directly rather than through
the kit.
Two neighbouring subpaths are worth knowing about, because their exports are not in the table below:
@flowpanel/kit/next/client— the client components the Next.js runtime mounts around this library:DrawerHost,CommandHost,DashboardActionsBar,DataTableWithDrawerRows,CreateDrawer,DetailTabsClient,SavedViewsDropdown,ResourceListSearch,ResourceListFilters,ResourceListDeletedToggle,DashboardDateRange,WidgetErrorBoundary. Reach for it when you assemble a"bare"-shell layout by hand.@flowpanel/kit/client— the standaloneuseAdminMutationhook, which pulls in no component library at all. See Client hooks.
Shell
Chrome, navigation and the pieces a custom layout composes around admin content.
| Export | Purpose |
|---|---|
AdminShell | Pure visual chrome — sidebar or tab-strip variant — around admin content. |
AdminNav | Renders NavGroup[] (Dashboards, Pages, Resources, Queues) as the sidebar/tab nav. |
AdminTabs | Tab-strip variant of the nav, used when AdminShellVariant is "tabs". |
AccountMenu | User menu (avatar, name, sign-out) shown in the shell header. |
Brand | Renders a ShellBrand (logo/name) in the shell header. |
Breadcrumbs | Breadcrumb trail from a BreadcrumbItem[]. |
CommandPalette | The ⌘K command palette, built on cmdk. |
Drawer, DrawerHeader, DrawerContent, DrawerFooter | Side-variant drawer built on Radix Dialog, and its section slots. |
DetailShell | Chrome around a resource detail page. |
FlowpanelGlobals | Mounts cross-cutting globals (toast host, theme script, shortcuts) once per admin. |
PageHeader, DefaultPageHeader | Page title/description/actions header and its default renderer. |
ShortcutsCheatsheet, DEFAULT_SHORTCUTS | The ?-triggered keyboard shortcuts overlay and the shortcut list it ships with. |
ThemeScript | Inline script that applies the stored theme class before hydration, avoiding a flash. |
ComponentsProvider, useComponents | Provider/reader for the FlowpanelComponentSlots override map. |
useComponent | Resolves a single slot — useComponent("Pagination", DefaultPagination) returns the host override when there is one, otherwise the default you passed. |
useComponentOverrides | The raw override map from the nearest ComponentsProvider, without defaults filled in. |
LabelsProvider, useLabels | Provider/reader for label overrides (pluralization, field labels). |
ApiBaseProvider, useApiBase | Provider/reader for the route handlers' mount point. FlowpanelGlobals fills it from paths.api, so every client fetch — drawer, actions, inline edit, import, reference search, SSE — follows the admin wherever it is mounted. Standalone components fall back to /api/flowpanel. |
Prop
Type
Prop
Type
Prop
Type
Data table & filters
DataTable and the controls that surround it — column menus, filter inputs,
cell renderers.
| Export | Purpose |
|---|---|
DataTable | The resource list table: sorting, pagination, selection, inline edit, realtime refresh. |
BulkBar | Floating action bar shown when DataTable rows are selected. |
ColumnPinMenu | Per-column menu for pinning left/right. |
ColumnVisibilityMenu | Toolbar menu for showing/hiding columns. |
DensityToggle | Toggles DataTableDensity (row spacing) in the toolbar. |
FilterBar | Renders a FilterBarSpec[] as the filter row above the table. |
BooleanFilter, DateRangeFilter, MultiSelectFilter, NumericRangeFilter, SelectFilter, TagFilter, TextFilter | One filter control per ColumnMeta.type, each driven by FilterBar. DateRangeFilter opens a two-month range calendar with presets. |
DateRangePicker | Standalone preset dropdown (Today, Last 7 days, MTD, …) for dashboards. |
Pagination, DefaultPagination | Page controls under the table and their default renderer. |
ArrayCell, JsonCell, ReferenceCell | Read-only cell renderers for array, JSON, and reference columns. |
InlineEditCell | Editable cell used when a column declares editable: true. |
JsonEditor | Structured editor for a JSON column, used by InlineEditCell and forms. |
ReferencePicker | Search-and-select control for picking a referenced row. |
MobileCardList | Card layout DataTable switches to under the mobile breakpoint. |
KV, KVRow | Key/value list layout, used by card and drawer field views. |
renderFormatCell | Renders a declarative ColumnFormat value — the function DataTable cells call internally. |
Prop
Type
Forms
AutoForm compiles a resource's declared fields into inputs; Form and
Field are the lower-level primitives it's built from, for hand-written
forms.
| Export | Purpose |
|---|---|
AutoForm | Renders a resource's create/update fields from ResolvedField[] or ColumnMeta[]. |
Form | Conform + Zod form wrapper — POSTs to a route and surfaces field errors. |
useFormContext | Reads the current Form's conform metadata; throws outside a Form. |
FormField | One labeled input, switched on its type prop (see below). |
FormError | Renders the form-level error from useFormContext. |
FormSection | Labeled fieldset grouping for AutoForm/Form fields. |
FormSubmit | Submit button that reflects the form's pending state. |
AsyncSelect | Debounced async-search select, used by reference fields. |
ResolvedField (type) | The server-resolved field shape AutoForm consumes — see field-types.ts. |
Prop
Type
Prop
Type
Prop
Type
The component is declared internally as Field with a FieldProps interface,
but the package renames both on export: the value is exported as
FormField and the type as FormFieldProps. Field and FieldProps are not
importable names — the prefix keeps them from colliding with the many other
Field components a consuming app is likely to have. The interface is
unchanged by the rename; the property table above is generated from it.
FormField's type prop accepts:
"text" | "email" | "password" | "url" | "number" | "date" | "datetime" |
"datetime-local" | "time" | "color" | "search" | "textarea" | "markdown" |
"json" | "tags" | "select" | "multiselect" | "radio" | "reference" |
"boolean" | "switch" | "checkbox" | "hidden"That union is internal — it has a name in the source (FieldControlType) but
is not exported, so type it inline or read it off
ComponentProps<typeof FormField>["type"]. It is a superset of the
config-level FieldType: datetime-local and search exist only here, which
is why a ResolvedField (typed FieldType) always assigns cleanly into it and
not the other way round.
Feedback
Empty, error, loading and toast states.
| Export | Purpose |
|---|---|
EmptyState, DefaultEmptyState | "Nothing here" placeholder and its default renderer. |
ErrorState | Inline error placeholder with an optional retry action. |
ErrorCard | Error display for a caught Error, with an optional retry callback. |
HealthBanner | Toned banner (e.g. degraded queue, stale data) shown above content. |
ConfirmDialog, DefaultConfirmDialog | Confirmation modal for destructive actions and its default renderer. |
SkeletonCard, SkeletonTable, DefaultSkeletonTable | Loading placeholders for a card and a table, and the table's default renderer. |
ResourceListSkeleton, ResourceDetailSkeleton, DashboardSkeleton | Page-level loading skeletons for the three page kinds. |
Toast, ToastProvider, useToast | Toast host provider and the hook to fire toasts from anywhere under it. |
Widgets
Dashboard widget renderers — the components a widget() builder's config
resolves into.
| Export | Purpose |
|---|---|
MetricCard, DefaultMetricCard | Renders a metric() widget and its default look. |
StatGroupCard | Renders a statGroup() widget. |
TableWidget | Renders a table() widget, reusing DataTable. |
CustomWidget | Wraps a custom() widget's component with the shared widget frame. |
Layout primitives
Shared by widgets and by hand-built pages.
| Export | Purpose |
|---|---|
Card, CardHeader, CardContent, CardDescription | Generic card shell and its section slots. |
MetricGrid | Responsive grid for laying out MetricCards. |
Section, SectionLabel, Divider, spanClass | Section wrapper with a label/description, a divider rule, and the Span → CSS class map dashboards use for widget width. |
Atoms
Small, single-purpose display components.
| Export | Purpose |
|---|---|
Avatar, DefaultAvatar | User/entity avatar and its default renderer. |
Badge, DefaultBadge | Toned inline badge and its default renderer. |
StatusBadge, DefaultStatusBadge | Badge preset for status-like values, and its default renderer. |
StatusDot | Small toned dot, e.g. for realtime/queue status. |
LiveIndicator | Realtime connection status indicator (see LiveStatus). |
LocalTime | Absolute timestamp rendered in the viewer's timezone. |
TimeAgo | Relative timestamp that ticks ("3m ago"), re-rendering on an interval. |
Sparkline | Minimal inline trend line for a numeric series. |
Mono | Monospace inline text, for IDs and code-like values. |
FlowpanelIcon | Renders a public IconName as the same decorative Lucide SVG used by nav and actions. |
Prop
Type
Hooks
Client hooks for building custom admin UI. All are "use client".
useLiveChannel(channel, onMessage, options?) — subscribes to one SSE
channel served by stream(). Connection pooling: hook instances that share
the same endpoint + channel reuse one EventSource, refcounted, rather
than opening one per instance. The returned LiveStatus runs idle →
connecting → live, drops to reconnecting while a retry is pending, and
reaches offline after six consecutive failed attempts (~30s); it returns to
idle when the subscription is torn down. Retries continue while offline.
const status = useLiveChannel("resource.orders", (payload) => {
console.log("order event", payload);
});useAdminTable() — reads and writes the list URL state (?page=,
?perPage=, ?q=, ?sort=field:dir, ?f_<field>=value) that DataTable and
FilterBar render from. pageSize is number | null: null means no
?perPage= is present and the resource's configured pageSize applies.
const { page, pageSize, sort, setPage, setSort } = useAdminTable();Every setter navigates with router.replace, not push: filters change on
each keystroke, and pushing would turn Back into a walk through the filter
history instead of a way out of the list.
useUrlState() — general-purpose typed reader/writer for the current
route's search params. useAdminDrawer is built on it; useAdminTable is
not — it reads next/navigation directly.
useTheme(options?) — reads the resolved light/dark/auto theme and
returns a stable toggle/setTheme that persists to localStorage.
const { theme, toggle } = useTheme();useToast() — fires a toast from anywhere under ToastProvider /
FlowpanelGlobals.
const toast = useToast();
toast.success("Saved");useOptimisticAction(serverValue, applyPatch) — thin wrapper over
React.useOptimistic + useTransition for an optimistic value with a pending
flag. It is offered for your own components; nothing shipped uses it —
InlineEditCell keeps its draft in plain useState.
useAdminCommand() — controls the ⌘K command palette's open state and
binds the ⌘K/Ctrl+K shortcut.
useAdminDrawer() — reads/writes ?drawer=<resource>:<id> and ?tab=,
the URL state a drawer's open/closed state and active tab live in.
useDashboardParam(key, schema, fallback) — type-safe reader/writer for
one dashboard URL search param, validated against a Zod schema.
useMediaQuery(query) — subscribes to a CSS media query via
useSyncExternalStore, with a server snapshot of false so SSR and first
paint agree. DataTable uses useMediaQuery("(max-width: 639px)") for its
card/table breakpoint; use the same query to match it in custom UI.
const isMobile = useMediaQuery("(max-width: 639px)");Realtime
Realtime status flows through a shared bus so many components can subscribe
without opening one EventSource each.
RealtimeProvider — mounts one shared EventSource for every descendant
useRealtimeRefresh call. It routes incoming envelopes ({ channel, payload }) to the subscribers of that channel, coalesces the resulting
router.refresh(), and reconnects with backoff on error. Mount it once near
the root of a "bare"-shell layout; Flowpanel/AdminShell mount it for you
otherwise.
useRealtimeRefresh(channels, options?) — subscribes a component to one
or more channels and triggers a debounced router.refresh() on any event.
Under a RealtimeProvider ancestor it joins the shared bus; standalone (no
provider), debounceMs and endpoint apply directly and it opens one pooled
EventSource carrying its whole channel set, shared with every other
standalone subscriber of the same set.
useRealtimeRefresh("resource.orders");RealtimeRefresh — the same thing as a component, for a server tree that
has no client component to hang the hook on: <RealtimeRefresh channels={…} debounceMs={…} /> renders nothing and subscribes its channels. This is what
the dashboard widget renderer emits for a widget's realtime option.
<RealtimeRefresh channels={["resource.orders", "scraper:tick"]} />DevToolsPanel — floating "fp" panel showing realtime status, the
active-channel list and event count. Development-only: it renders null when
NODE_ENV === "production". Mount it once, in development builds:
{process.env.NODE_ENV !== "production" && <DevToolsPanel />}Prop
Type
Lower-level bus access: useRealtimeBus (the raw RealtimeBus, or null
outside a provider), useRealtimeStatus, useRealtimeStats
({ channels, eventCount }).
Theme utilities
Non-React helpers behind useTheme and ThemeScript, for code that needs the
theme outside a component.
| Export | Purpose |
|---|---|
resolveTheme, readStoredTheme, writeStoredTheme, THEME_STORAGE_KEY | Resolve the effective theme from storage + system preference, and read/write the stored choice. |
applyThemeClass, toggleTheme | Apply a theme class to <html>, and flip the stored choice. |
buildThemeInitScript | Builds the inline script string ThemeScript renders. |
cn (class name merge), formatNumber, humanize, resolveFieldLabel, and
triggerDownload are general-purpose utilities exported alongside these for
the same reason: components elsewhere in the package, and custom slots, both
need them.
Name collisions with @flowpanel/kit
A handful of identifiers exist in both the config surface (@flowpanel/kit,
re-exporting @flowpanel/core) and here. They are different declarations
with the same name, so importing both into one module needs an alias.
| Name | In @flowpanel/kit | In @flowpanel/kit/react |
|---|---|---|
TableWidget | The table() builder's return type ({ kind: "table"; options }). | The component that renders one. |
CustomWidget | The custom() builder's return type. | The component that wraps one. |
Tone | The config-level tone vocabulary used by format: "badge", widget tone, … | The same vocabulary, re-declared next to formatNumber. |
NumericFormat | The widget/metric format union. | The same union, re-declared next to formatNumber. |
DrawerWidth | DrawerConfig["width"], the config option. | DrawerProps["width"], the component prop — an identical union, declared separately. |
import type { TableWidget } from "@flowpanel/kit";
import { TableWidget as TableWidgetRenderer } from "@flowpanel/kit/react";FormActionResult is a fourth collision, and the harmless kind: it is
declared twice with identical members — once here (what Form reads back
from the route it POSTs to) and once in @flowpanel/kit/next (what
makeActions' form routes answer with). Both are
{ ok: boolean; createdKey?: string; error?: string; fieldErrors?: Record<string, string> }, so
either import satisfies the other. They are still two declarations: if one
ever gains a member, they stop being interchangeable.
UI primitives
Dialog, Popover, Select, DropdownMenu, Tabs*,
plus Button, Checkbox, Input, Label, Skeleton, and Switch, are
shadcn-derived primitives re-exported so custom slots and pages can be built
in the same visual language as the rest of the admin. Each follows its
upstream shadcn/Radix API as-is — FlowPanel does not wrap or change their
props — so shadcn's own docs apply directly. buttonVariants and
DefaultButton are FlowPanel additions alongside Button.