Skip to content

API Reference ​

Complete reference for all public exports from @middag-io/react.

Import path

All exports are available from the package root: import { ... } from '@middag-io/react'. Types are exported with the type keyword.

Components ​

ContractPage ​

Main page renderer. Resolves shell, layout, and blocks from registries based on the PageContract. This is the only component most consumers need.

tsx
<ContractPage contract={contract} overlay? help? inspector? />
PropTypeRequiredDefaultDescription
contractPageContractYesThe page contract describing shell, layout, and blocks to render.
overlaybooleanNofalseWhen true, renders the page in a full-screen overlay with a close button.
helpHelpDataNoContextual help content shown in the ContextAside panel.
inspectorInspectorDataNoInspector side-panel configuration (endpoint URL and width).

Registration Functions ​

Functions for populating the shell, layout, and block registries. Call registerDefaults() once at app startup to register all built-in components.

registerDefaults() ​

ts
function registerDefaults(): void

Registers all built-in shells, layouts, and block types. Idempotent — safe to call multiple times.

IIFE consumers

WordPress and other IIFE consumers should NOT call registerDefaults(). It pulls in lazy-loaded blocks (FlowEditor, FormBuilder, etc.) that include heavy dependencies (@xyflow/react, @dnd-kit). With inlineDynamicImports: true, these end up in the bundle even if unused. Use selective registration instead.

registerShell(key, component) ​

ts
function registerShell(key: Shell, component: ComponentType<ShellProps>): void

Register a custom shell component for a given shell key. Overrides the default if the key already exists.

ArgumentTypeDescription
key'product' | 'immersive' | stringShell identifier matching the contract shell field. Extensible via string & {}.
componentComponentType<ShellProps>React component that receives { children } and renders the shell chrome.

registerLayout(key, component) ​

ts
function registerLayout(key: string, component: ComponentType<LayoutProps>): void

Register a custom layout component for a given template key.

ArgumentTypeDescription
keystringLayout template key (e.g. 'stack', 'sidebar', 'dashboard').
componentComponentType<LayoutProps>React component receiving { layout, renderBlock }.

registerBlock(key, component) ​

ts
function registerBlock<TData>(key: string, component: ComponentType<BlockProps<TData>>): void

Register a custom block component for a given block type key.

ArgumentTypeDescription
keystringBlock type key (e.g. 'dense_table', 'metric_card').
componentComponentType<BlockProps<TData>>React component receiving { block } with typed data.

Selective registration ​

For IIFE consumers, import and register only the components you need:

tsx
import {
  registerShell, registerLayout, registerBlock,
  ProductShell, StackLayout, SidebarLayout, DashboardLayout,
  DenseTableBlock, MetricCardBlock, EmptyStateBlock,
} from '@middag-io/react';

registerShell('product', ProductShell);
registerLayout('stack', StackLayout);
registerLayout('sidebar', SidebarLayout);
registerLayout('dashboard', DashboardLayout);
registerBlock('dense_table', DenseTableBlock);
registerBlock('metric_card', MetricCardBlock);
registerBlock('empty_state', EmptyStateBlock);

Standard blocks, shells, and layouts are exported from the barrel. Heavy blocks (condition_tree, sentence_builder, flow_editor, form_builder) and form_panel are not exported from the barrel to avoid bundling their heavy dependencies. Import them from @middag-io/react/blocks/* if needed.

Package Export Paths ​

Import pathContent
@middag-io/reactBarrel: types, ContractPage, registries, shells, layouts, the standard Community blocks, providers, MIDDAGThemeProvider, HostSlot
@middag-io/react/style.cssCSS bundle (tokens + theme bridge + Tailwind utilities)
@middag-io/react/theme.cssSource CSS for consumers with their own Tailwind setup
@middag-io/react/themes/*.cssPer-theme CSS (classic, enterprise, soft, midnight)
@middag-io/react/reui/*ReUI primitives (pre-built ESM + .d.ts; extensionless specifiers, e.g. reui/button)
@middag-io/react/blocks/*Block components (source .tsx)
@middag-io/react/schemas/*Public JSON Schemas (wire contracts + discovery — the 11 files from docs/public/schemas/), e.g. schemas/page-contract.json
@middag-io/react/mockMock SPA extensible (GitHub Packages only)

Shell Components ​

Built-in shells available for selective registration:

ExportShell keyDescription
ProductShellproductFull shell with offcanvas sidebar, page header, breadcrumbs, floating controls, host header detection, optional admin tab bar.
ImmersiveShellimmersiveFull-screen, no sidebar. Slim top bar with close button. Designed for wizards, flow editors, onboarding.

The ProductShell provides:

  • Offcanvas collapsible sidebar with navigation
  • SidebarFooter with collapse button + AppearanceToggle
  • Floating expand + dark mode controls when sidebar is hidden
  • Host header height detection (Moodle navbar, WP admin bar)
  • Optional admin tab bar (rendered when admin_tabs shared prop is present)
  • Zero padding on the <main> content area (layouts control all spacing)

Lazy Block Loading ​

LazyBlock ​

Wrapper that auto-fetches block data via Inertia partial reload when the block mounts.

tsx
import { LazyBlock, isLazyBlock } from '@middag-io/react';
ExportTypeDescription
LazyBlockReact.FC<LazyBlockProps>Renders a loading skeleton, triggers router.reload, then renders the block.
isLazyBlock(block: BlockDescriptor) => booleanReturns true if block.meta.lazyProp is a string.

Lazy loading works automatically with ContractPage and TabbedPanelBlock. Set meta.lazyProp on a block descriptor and send a corresponding null Inertia prop from the backend. See Blocks Guide for details.

Registries ​

Six registries are Map instances that store the mapping from contract keys to React components. Populated by registerDefaults() or individual register* calls.

RegistryTypeBuilt-in entries
shellRegistryMap<Shell, ComponentType<ShellProps>>product, immersive
layoutRegistryMap<string, ComponentType<LayoutProps>>stack, sidebar, dashboard, wizard
blockRegistryMap<string, ComponentType<BlockProps>>All built-in block types (see Block Reference)

Field Registry ​

Maps form field component type strings to React field input components. Used internally by FormPanelBlock to resolve the correct input for each FormFieldNode.component value.

tsx
import { registerFieldComponent, resolveFieldComponent } from '@middag-io/react';
import type { FieldComponentProps } from '@middag-io/react';
ExportTypeDescription
registerFieldComponent(type: string, component: ComponentType<FieldComponentProps>) => voidRegister a custom field input component.
resolveFieldComponent(type: string) => ComponentType<FieldComponentProps> | undefinedLook up a field component by type key.

Icon Registry ​

Maps icon names (kebab-case) and navigation entity types to SVG icon elements. Used by navigation, blocks, actions, and anywhere icons are resolved by name.

tsx
import { registerIcon, resolveIcon, registerEntityIcon, resolveEntityIcon } from '@middag-io/react';
ExportTypeDescription
registerIcon(name: string, icon: IconSvgElement) => voidRegister an icon by kebab-case name (e.g. "graduation-cap").
resolveIcon(name: string) => IconSvgElement | undefinedLook up an icon by name.
registerEntityIcon(entityType: string, icon: IconSvgElement) => voidRegister an icon for a navigation entity type.
resolveEntityIcon(entityType: string) => IconSvgElement | undefinedLook up an entity type icon.

Built-in: 66 named icons + 11 entity type icons registered by registerDefaultIcons().

Cell Registry ​

Maps DataTable column variant strings to custom cell renderer components. Extends DenseTable with new column visualizations.

tsx
import { registerCellRenderer, resolveCellRenderer } from '@middag-io/react';
import type { CellRendererProps } from '@middag-io/react';
ExportTypeDescription
registerCellRenderer(type: string, component: ComponentType<CellRendererProps>) => voidRegister a custom cell renderer.
resolveCellRenderer(type: string) => ComponentType<CellRendererProps> | undefinedLook up a cell renderer by variant key.

Built-in variants: text, status, badge, boolean, timestamp, link, rich_status, html, link_group, annotated.

Providers ​

I18nProvider ​

tsx
<I18nProvider asyncResolver?={resolver} overrides?={strings}>{children}</I18nProvider>

Translation provider. Reads pre-loaded strings from Inertia shared props (theme.strings). Optionally accepts an async resolver for keys not pre-loaded by the server, and/or a static overrides map.

Lookup chain: server strings > overrides > async strings > LIB_UI_DEFAULTS > key itself.

PropTypeRequiredDescription
childrenReactNodeYesChild components that can access translation functions.
asyncResolverAsyncStringResolverNoOptional async function to resolve translation keys not in the pre-loaded strings.
overridesRecord<string, string>NoClient-side string overrides merged into the lookup chain. Useful for hosts that inject translations directly (e.g. ptBR locale).

See Providers Guide and i18n Guide for details.

useTranslation() ​

ts
function useTranslation(): { t, tAsync }
ReturnTypeDescription
t(key: string) => stringSynchronous lookup. Returns the key itself if not yet loaded.
tAsync(key: string, component?: string) => Promise<string>Async lookup using the injected resolver. Falls back to returning the key.

Theme Functions ​

Appearance management with host detection and manual override. Three-tier resolution: manual > host (Moodle/WP) > OS.

FunctionSignatureDescription
getStoredAppearance() => AppearanceRead stored preference from localStorage. Returns 'system' if none.
setAppearance(pref: Appearance) => voidPersist preference, resolve effective theme, apply to DOM.
cycleAppearance() => AppearanceCycle: system > light > dark > system. Persists and applies.
getEffectiveTheme(pref: Appearance) => 'light' | 'dark'Resolve 'system' to concrete theme via host detection + OS.
applyTheme(theme: EffectiveTheme) => voidApply resolved theme to DOM (data-theme on .middag-root, class on html).
toggleDir() => 'ltr' | 'rtl'Toggle document direction. Persists in localStorage.
initDir() => voidRestore persisted direction on page load.
onSystemThemeChange(cb?: () => void) => () => voidListen for OS theme changes. Only reacts when preference is 'system'. Returns cleanup function.

See Theme Guide for usage examples.

Hooks ​

useIsDark() ​

ts
function useIsDark(): boolean

Returns true when the effective theme is 'dark'. Reacts to theme changes via the data-theme attribute on .middag-root elements.

tsx
import { useIsDark } from '@middag-io/react';

function Logo() {
    const isDark = useIsDark();
    return <img src={isDark ? '/logo-white.svg' : '/logo-dark.svg'} alt="Logo" />;
}

Types: Page Contract ​

Core types for the contract-driven page system. The backend sends PageContract via Inertia props.

ts
interface PageContract {
    version: '1';
    shell: 'product' | 'immersive' | (string & {});
    page: PageMeta;
    layout: LayoutDescriptor;
    resources?: PageResources;
}

interface PageMeta {
    key: string;
    title: string;
    subtitle?: string;
    icon?: string;
    badge?: PageBadge;
    favoritable?: boolean;
    breadcrumbs?: Breadcrumb[];
    actions?: PageAction[];
    filterTabs?: PageFilterTab[];
    activeFilterTab?: string;
}

interface BlockDescriptor<TData = Record<string, unknown>> {
    type: string;       // block type key (e.g. 'dense_table')
    key: string;        // unique key within the page
    data: TData;        // typed data payload (empty {} for lazy blocks)
    variant?: string;
    title?: string;
    subtitle?: string;
    actions?: PageAction[];
    meta?: Record<string, unknown>;
    // Reserved meta keys:
    //   fullBleed: boolean -- skip horizontal padding (edge-to-edge)
    //   lazyProp: string   -- Inertia prop key for lazy-loaded data
    //   loading: boolean   -- show loading skeleton
}

interface LayoutDescriptor {
    template: 'stack' | 'sidebar' | 'dashboard' | 'wizard' | (string & {});
    regions: Record<string, BlockDescriptor[]>;
    meta?: Record<string, unknown>;
}

interface HelpData {
    title: string;
    description: string;
    sections?: HelpSection[];
    tips?: HelpTip[];
    shortcuts?: HelpShortcut[];
    learnMore?: string;
}

interface ContractPageProps {
    contract: PageContract;
    overlay?: boolean;
    help?: HelpData;
    inspector?: InspectorData;
}

Types: SharedProps ​

Typed Inertia shared props sent on every request.

ts
interface SharedProps {
    navigation: NavigationTreePayload;
    auth: SharedPropsAuth;
    theme: SharedPropsTheme;
    flash?: SharedPropsFlash;
    locale: string;       // e.g. 'pt-BR', 'en'
    version: string;      // e.g. '5.0.0'
    scope?: Record<string, unknown>;
    [key: string]: unknown;
}

interface SharedPropsAuth {
    id: number;
    name: string;
    email: string;
    avatarUrl?: string;
    capabilities: string[];
}

interface SharedPropsTheme {
    strings?: Record<string, string>;
    appearance?: 'system' | 'light' | 'dark';
    brandColor?: string;
    inherit?: boolean;
}

Types: Navigation ​

Navigation contract types. The unified tree model is the single supported shape (pre-1.0: the legacy sections-based NavigationPayload was removed).

ts
interface NavigationTreePayload {
    tree: NavigationNode[];
    activeKey: string;
    drilldownStack?: string[];
    footer: NavigationNode[];
}

interface NavigationNode {
    key: string;
    label: string;
    icon?: string;
    entityType?: NavigationEntityType;
    href?: string;
    badge?: string | number;
    badgeVariant?: 'count' | 'alert';
    active?: boolean;
    drilldown?: boolean;
    collapsible?: boolean;
    defaultOpen?: boolean;
    statusColor?: string;
    children?: NavigationNode[];
}

See Navigation Guide for usage details.

Types: Registry ​

Props interfaces for registered components.

ts
// Shell component receives children to render inside the chrome
type ShellProps = { children: ReactNode };

// Layout component receives the layout descriptor and a render function
type LayoutProps = {
    layout: LayoutDescriptor;
    renderBlock: (block: BlockDescriptor) => ReactElement | null;
};

// Block component receives the full block descriptor with typed data
type BlockProps<TData = Record<string, unknown>> = {
    block: BlockDescriptor<TData>;
};

Types: Block Data ​

Data contracts for the registered block types. Each block component receives a BlockDescriptor<TData> where TData is one of these interfaces.

Type KeyData InterfaceDescription
dense_tableDenseTableBlockDataFull-featured table with sort, filter, pagination, bulk/row actions, density toggle.
metric_cardMetricCardBlockDataKPI card with value, delta, icon, optional link.
status_stripStatusStripBlockDataHorizontal health indicators with score dots and key/value pairs.
detail_panelDetailPanelBlockDataSections of label/value fields (text, status, timestamp, link, code, email).
activity_timelineActivityTimelineBlockDataGrouped timeline entries with actor, action, icon, color, timestamp.
card_gridCardGridBlockDataCard-based grid. Variants: default, store, connector.
markdown_panelMarkdownPanelBlockDataSanitized markdown rendering with syntax highlight and copy button.
link_listLinkListBlockDataVertical list of links with icon, label, description.
tabbed_panelTabbedPanelBlockDataTab container that nests other blocks. Each tab has key, label, and blocks array.
empty_stateEmptyStateBlockDataEmpty/error/first-use placeholder. Variants: first-use, no-results, error, permission.
form_panelFormPanelBlockDataSchema-driven form with sections, groups, conditional fields, dirty tracking.
action_gridActionGridBlockDataGrid of action cards with icon, title, description, execute button.
condition_treeConditionTreeBlockDataAND/OR tree for condition rules with nesting.
sentence_builderSentenceBuilderBlockDataNatural language rule builder for audience segments.
flow_editorFlowEditorBlockDataCanvas with draggable nodes and edges for workflow editing.
form_builderFormBuilderBlockDataDrag-and-drop form field builder.
workflow_progressWorkflowProgressBlockDataHorizontal stepper showing linear workflow states (past/current/future).
chart_panelChartPanelBlockDataMulti-series chart (bar, line, area, pie) via Recharts. Lazy-loaded.
kanban_boardKanbanBoardBlockDataDrag-and-drop kanban board with columns and cards. Lazy-loaded.

See Block Catalog for data shape examples.

Types: Theme ​

ts
type Appearance = 'system' | 'light' | 'dark';
type EffectiveTheme = 'light' | 'dark';
type AsyncStringResolver = (key: string, component?: string) => Promise<string>;

Other Page Types ​

ts
type Shell = 'product' | 'immersive' | (string & {});

type LayoutTemplate = 'stack' | 'sidebar' | 'dashboard'
    | 'wizard' | (string & {});

interface PageFilterTab {
    key: string; label: string; badge?: number | string;
}

interface Breadcrumb {
    label: string; href?: string; external?: boolean;
}

interface PageAction {
    id: string; label: string; intent: ActionIntent;
    href?: string; method?: ActionMethod; icon?: string;
    confirmation?: ActionConfirmation; disabled?: boolean;
}

interface InspectorData { endpoint: string; width: number; }

interface HelpSection { title: string; body: string; }

interface HelpTip { icon?: string; text: string; }

interface HelpShortcut { keys: string; action: string; }

MIDDAG © 2015-2026