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, 12 standard 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 (source .tsx, requires TS bundler)
@middag-io/react/blocks/*Block components (source .tsx)
@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 19 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: NavigationPayload | 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. Two shapes are supported: legacy sections-based and the newer unified tree model.

ts
interface NavigationPayload {
    sections: NavigationSection[];
    activeKey: string;
}

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 all 19 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;
    requiresConfirmation?: boolean; 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