Browse documentation

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 standalone useAdminMutation hook, which pulls in no component library at all. See Client hooks.

Shell

Chrome, navigation and the pieces a custom layout composes around admin content.

ExportPurpose
AdminShellPure visual chrome — sidebar or tab-strip variant — around admin content.
AdminNavRenders NavGroup[] (Dashboards, Pages, Resources, Queues) as the sidebar/tab nav.
AdminTabsTab-strip variant of the nav, used when AdminShellVariant is "tabs".
AccountMenuUser menu (avatar, name, sign-out) shown in the shell header.
BrandRenders a ShellBrand (logo/name) in the shell header.
BreadcrumbsBreadcrumb trail from a BreadcrumbItem[].
CommandPaletteThe ⌘K command palette, built on cmdk.
Drawer, DrawerHeader, DrawerContent, DrawerFooterSide-variant drawer built on Radix Dialog, and its section slots.
DetailShellChrome around a resource detail page.
FlowpanelGlobalsMounts cross-cutting globals (toast host, theme script, shortcuts) once per admin.
PageHeader, DefaultPageHeaderPage title/description/actions header and its default renderer.
ShortcutsCheatsheet, DEFAULT_SHORTCUTSThe ?-triggered keyboard shortcuts overlay and the shortcut list it ships with.
ThemeScriptInline script that applies the stored theme class before hydration, avoiding a flash.
ComponentsProvider, useComponentsProvider/reader for the FlowpanelComponentSlots override map.
useComponentResolves a single slot — useComponent("Pagination", DefaultPagination) returns the host override when there is one, otherwise the default you passed.
useComponentOverridesThe raw override map from the nearest ComponentsProvider, without defaults filled in.
LabelsProvider, useLabelsProvider/reader for label overrides (pluralization, field labels).
ApiBaseProvider, useApiBaseProvider/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.

ExportPurpose
DataTableThe resource list table: sorting, pagination, selection, inline edit, realtime refresh.
BulkBarFloating action bar shown when DataTable rows are selected.
ColumnPinMenuPer-column menu for pinning left/right.
ColumnVisibilityMenuToolbar menu for showing/hiding columns.
DensityToggleToggles DataTableDensity (row spacing) in the toolbar.
FilterBarRenders a FilterBarSpec[] as the filter row above the table.
BooleanFilter, DateRangeFilter, MultiSelectFilter, NumericRangeFilter, SelectFilter, TagFilter, TextFilterOne filter control per ColumnMeta.type, each driven by FilterBar. DateRangeFilter opens a two-month range calendar with presets.
DateRangePickerStandalone preset dropdown (Today, Last 7 days, MTD, …) for dashboards.
Pagination, DefaultPaginationPage controls under the table and their default renderer.
ArrayCell, JsonCell, ReferenceCellRead-only cell renderers for array, JSON, and reference columns.
InlineEditCellEditable cell used when a column declares editable: true.
JsonEditorStructured editor for a JSON column, used by InlineEditCell and forms.
ReferencePickerSearch-and-select control for picking a referenced row.
MobileCardListCard layout DataTable switches to under the mobile breakpoint.
KV, KVRowKey/value list layout, used by card and drawer field views.
renderFormatCellRenders 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.

ExportPurpose
AutoFormRenders a resource's create/update fields from ResolvedField[] or ColumnMeta[].
FormConform + Zod form wrapper — POSTs to a route and surfaces field errors.
useFormContextReads the current Form's conform metadata; throws outside a Form.
FormFieldOne labeled input, switched on its type prop (see below).
FormErrorRenders the form-level error from useFormContext.
FormSectionLabeled fieldset grouping for AutoForm/Form fields.
FormSubmitSubmit button that reflects the form's pending state.
AsyncSelectDebounced 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.

ExportPurpose
EmptyState, DefaultEmptyState"Nothing here" placeholder and its default renderer.
ErrorStateInline error placeholder with an optional retry action.
ErrorCardError display for a caught Error, with an optional retry callback.
HealthBannerToned banner (e.g. degraded queue, stale data) shown above content.
ConfirmDialog, DefaultConfirmDialogConfirmation modal for destructive actions and its default renderer.
SkeletonCard, SkeletonTable, DefaultSkeletonTableLoading placeholders for a card and a table, and the table's default renderer.
ResourceListSkeleton, ResourceDetailSkeleton, DashboardSkeletonPage-level loading skeletons for the three page kinds.
Toast, ToastProvider, useToastToast 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.

ExportPurpose
MetricCard, DefaultMetricCardRenders a metric() widget and its default look.
StatGroupCardRenders a statGroup() widget.
TableWidgetRenders a table() widget, reusing DataTable.
CustomWidgetWraps a custom() widget's component with the shared widget frame.

Layout primitives

Shared by widgets and by hand-built pages.

ExportPurpose
Card, CardHeader, CardContent, CardDescriptionGeneric card shell and its section slots.
MetricGridResponsive grid for laying out MetricCards.
Section, SectionLabel, Divider, spanClassSection 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.

ExportPurpose
Avatar, DefaultAvatarUser/entity avatar and its default renderer.
Badge, DefaultBadgeToned inline badge and its default renderer.
StatusBadge, DefaultStatusBadgeBadge preset for status-like values, and its default renderer.
StatusDotSmall toned dot, e.g. for realtime/queue status.
LiveIndicatorRealtime connection status indicator (see LiveStatus).
LocalTimeAbsolute timestamp rendered in the viewer's timezone.
TimeAgoRelative timestamp that ticks ("3m ago"), re-rendering on an interval.
SparklineMinimal inline trend line for a numeric series.
MonoMonospace inline text, for IDs and code-like values.
FlowpanelIconRenders 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 idleconnectinglive, 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.

ExportPurpose
resolveTheme, readStoredTheme, writeStoredTheme, THEME_STORAGE_KEYResolve the effective theme from storage + system preference, and read/write the stored choice.
applyThemeClass, toggleThemeApply a theme class to <html>, and flip the stored choice.
buildThemeInitScriptBuilds 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.

NameIn @flowpanel/kitIn @flowpanel/kit/react
TableWidgetThe table() builder's return type ({ kind: "table"; options }).The component that renders one.
CustomWidgetThe custom() builder's return type.The component that wraps one.
ToneThe config-level tone vocabulary used by format: "badge", widget tone, …The same vocabulary, re-declared next to formatNumber.
NumericFormatThe widget/metric format union.The same union, re-declared next to formatNumber.
DrawerWidthDrawerConfig["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.