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.
<ContractPage contract={contract} overlay? help? inspector? />| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
contract | PageContract | Yes | The page contract describing shell, layout, and blocks to render. | |
overlay | boolean | No | false | When true, renders the page in a full-screen overlay with a close button. |
help | HelpData | No | Contextual help content shown in the ContextAside panel. | |
inspector | InspectorData | No | Inspector 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()
function registerDefaults(): voidRegisters all built-in shells (product, admin), layouts (stack, split, dashboard, master-detail, wizard, canvas), and all 16 block types. Idempotent -- safe to call multiple times.
registerShell(key, component)
function registerShell(key: Shell, component: ComponentType<ShellProps>): voidRegister a custom shell component for a given shell key. Overrides the default if the key already exists.
| Argument | Type | Description |
|---|---|---|
key | 'product' | 'admin' | 'course' | 'immersive' | Shell identifier matching the contract shell field. |
component | ComponentType<ShellProps> | React component that receives { children } and renders the shell chrome. |
registerLayout(key, component)
function registerLayout(key: string, component: ComponentType<LayoutProps>): voidRegister a custom layout component for a given template key.
| Argument | Type | Description |
|---|---|---|
key | string | Layout template key (e.g. 'stack', 'split', 'dashboard'). |
component | ComponentType<LayoutProps> | React component receiving { layout, renderBlock }. |
registerBlock(key, component)
function registerBlock<TData>(key: string, component: ComponentType<BlockProps<TData>>): voidRegister a custom block component for a given block type key.
| Argument | Type | Description |
|---|---|---|
key | string | Block type key (e.g. 'dense_table', 'metric_card'). |
component | ComponentType<BlockProps<TData>> | React component receiving { block } with typed data. |
Registries
The three registries are Map instances that store the mapping from contract keys to React components. Populated by registerDefaults() or individual register* calls.
| Registry | Type | Built-in entries |
|---|---|---|
shellRegistry | Map<Shell, ComponentType<ShellProps>> | product, admin |
layoutRegistry | Map<string, ComponentType<LayoutProps>> | stack, split, dashboard, master-detail, wizard, canvas |
blockRegistry | Map<string, ComponentType<BlockProps>> | 16 block types |
Providers
I18nProvider
<I18nProvider asyncResolver?={resolver}>{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.
| Prop | Type | Required | Description |
|---|---|---|---|
children | ReactNode | Yes | Child components that can access translation functions. |
asyncResolver | AsyncStringResolver | No | Optional async function to resolve translation keys not in the pre-loaded strings. |
See Providers Guide and i18n Guide for details.
useTranslation()
function useTranslation(): { t, tAsync }| Return | Type | Description |
|---|---|---|
t | (key: string) => string | Synchronous 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.
| Function | Signature | Description |
|---|---|---|
getStoredAppearance | () => Appearance | Read stored preference from localStorage. Returns 'system' if none. |
setAppearance | (pref: Appearance) => void | Persist preference, resolve effective theme, apply to DOM. |
cycleAppearance | () => Appearance | Cycle: system > light > dark > system. Persists and applies. |
getEffectiveTheme | (pref: Appearance) => 'light' | 'dark' | Resolve 'system' to concrete theme via host detection + OS. |
applyTheme | (theme: EffectiveTheme) => void | Apply resolved theme to DOM (data-theme on .middag-root, class on html). |
toggleDir | () => 'ltr' | 'rtl' | Toggle document direction. Persists in localStorage. |
initDir | () => void | Restore persisted direction on page load. |
onSystemThemeChange | (cb?: () => void) => () => void | Listen for OS theme changes. Only reacts when preference is 'system'. Returns cleanup function. |
See Theme Guide for usage examples.
Hooks
useIsDark()
function useIsDark(): booleanReturns true when the effective theme is 'dark'. Reacts to theme changes via the data-theme attribute on .middag-root elements.
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.
interface PageContract {
version: '1';
shell: 'product' | 'admin' | 'course' | 'immersive';
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
variant?: string;
title?: string;
subtitle?: string;
actions?: PageAction[];
meta?: Record<string, unknown>;
}
interface LayoutDescriptor {
template: 'stack' | 'split' | 'dashboard' | 'master-detail' | 'wizard' | 'canvas';
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.
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.
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.
// 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 16 registered block types. Each block component receives a BlockDescriptor<TData> where TData is one of these interfaces.
| Type Key | Data Interface | Description |
|---|---|---|
dense_table | DenseTableBlockData | Full-featured table with sort, filter, pagination, bulk/row actions, density toggle. |
metric_card | MetricCardBlockData | KPI card with value, delta, icon, optional link. |
status_strip | StatusStripBlockData | Horizontal health indicators with score dots and key/value pairs. |
detail_panel | DetailPanelBlockData | Sections of label/value fields (text, status, timestamp, link, code, email). |
activity_timeline | ActivityTimelineBlockData | Grouped timeline entries with actor, action, icon, color, timestamp. |
card_grid | CardGridBlockData | Card-based grid. Variants: default, store, connector. |
markdown_panel | MarkdownPanelBlockData | Sanitized markdown rendering with syntax highlight and copy button. |
link_list | LinkListBlockData | Vertical list of links with icon, label, description. |
tabbed_panel | TabbedPanelBlockData | Tab container that nests other blocks. Each tab has key, label, and blocks array. |
empty_state | EmptyStateBlockData | Empty/error/first-use placeholder. Variants: first-use, no-results, error, permission. |
form_panel | FormPanelBlockData | Schema-driven form with sections, groups, conditional fields, dirty tracking. |
action_grid | ActionGridBlockData | Grid of action cards with icon, title, description, execute button. |
condition_tree | ConditionTreeBlockData | AND/OR tree for condition rules with nesting. |
sentence_builder | SentenceBuilderBlockData | Natural language rule builder for audience segments. |
flow_editor | FlowEditorBlockData | Canvas with draggable nodes and edges for workflow editing. |
form_builder | FormBuilderBlockData | Drag-and-drop form field builder. |
See Block Catalog for data shape examples.
Types: Theme
type Appearance = 'system' | 'light' | 'dark';
type EffectiveTheme = 'light' | 'dark';
type AsyncStringResolver = (key: string, component?: string) => Promise<string>;Other Page Types
type Shell = 'product' | 'admin' | 'course' | 'immersive';
type LayoutTemplate = 'stack' | 'split' | 'dashboard'
| 'master-detail' | 'wizard' | 'canvas';
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; }