Blocks Guide
Extend the UI by creating custom block types that the backend can reference in page contracts.
Block Architecture
Blocks are React components that receive a typed BlockProps<TData> prop. Each block is registered via registerBlock(type, Component) so the rendering engine can resolve it at runtime. The backend sends a BlockDescriptor with a matching type field, and the block receives the full descriptor including data, key, title, actions, and meta.
// blocks/MyWidgetBlock.tsx
import type { BlockProps } from '@middag-io/react';
interface MyWidgetData {
label: string;
value: number;
chart: { x: number; y: number }[];
}
export function MyWidgetBlock({ block }: BlockProps<MyWidgetData>) {
const { label, value, chart } = block.data;
return (
<div className="rounded-lg border p-4">
<h3>{label}</h3>
<span className="text-2xl font-bold">{value}</span>
{/* render chart... */}
</div>
);
}Registration
Register your block once at your app entry point, after calling registerDefaults(). The type string must match what the backend sends in the contract.
// main.tsx
import { registerBlock } from '@middag-io/react';
import { MyWidgetBlock } from './blocks/MyWidgetBlock';
registerBlock('my_widget', MyWidgetBlock);Backend Contract
The backend includes the block in a page contract by specifying the matching type and providing the data payload:
$block = [
'type' => 'my_widget',
'key' => 'sales-widget',
'data' => [
'label' => 'Sales Today',
'value' => 42,
'chart' => [...],
],
];Block Props Reference
Every block receives a block object with the following properties:
| Prop | Type | Required | Description |
|---|---|---|---|
block.type | string | Yes | Block type identifier used to resolve the component from the registry. |
block.key | string | Yes | Unique key for this block instance within the page. |
block.data | TData | Yes | Typed data payload specific to this block type. |
block.title | string | No | Optional display title rendered by the layout above the block. |
block.actions | Action[] | No | Optional array of action buttons associated with the block. |
block.meta | Record<string, unknown> | No | Arbitrary metadata for presentation hints (polling, fullBleed, etc.). |
block.variant | string | No | Optional visual variant for the block (e.g. "compact", "card"). |
Tips
Keep blocks stateless
Data comes from the contract, not from internal state. If a block needs to change data, send a request to the backend and let it return an updated contract.
Use block.meta for presentation hints
The meta field is ideal for signaling rendering preferences like fullBleed, polling intervals, or custom layout options without polluting the typed data payload.
Block type names must be globally unique
Since all blocks share a single registry, prefix your type names with your plugin name to avoid collisions: myplugin_widget instead of just widget.
See Block Catalog for all 16 built-in block types with data shape examples.