code-docs/plugins/blocks/overview.md
Blocks are the visual building blocks of Lowdefy applications. Each block is a React component that renders UI elements.
Blocks are:
| Category | Purpose | Examples |
|---|---|---|
| Container | Layout and grouping | Box, Card, Collapse, Tabs |
| Input | User data entry | TextInput, NumberInput, Selector |
| Display | Show data | Title, Paragraph, Table, List |
| Feedback | User feedback | Alert, Message, Progress |
| Navigation | App navigation | Menu, Breadcrumb, Anchor |
| Package | Description | Block Count |
|---|---|---|
| @lowdefy/blocks-antd | Primary UI kit (Ant Design) | 61 |
| @lowdefy/blocks-basic | HTML primitives | 8 |
| @lowdefy/blocks-aggrid | AG Grid data tables | 1 |
| @lowdefy/blocks-diff | Diff visualizations | 4 |
| @lowdefy/blocks-echarts | ECharts visualizations | 1 |
| @lowdefy/blocks-markdown | Markdown rendering | 2 |
| @lowdefy/blocks-google-maps | Google Maps | 1 |
| @lowdefy/blocks-loaders | Loading spinners | 1 |
| @lowdefy/blocks-qr | QR code generation | 1 |
Each block package has:
src/blocks/{BlockName}/{BlockName}.js — React componentsrc/blocks/{BlockName}/meta.js — Block metadata (category, icons, valueType, cssKeys, events, properties schema)src/blocks.js — Named exports of all block componentssrc/metas.js — Named exports of all block meta.js filessrc/types.js — Type declarations derived from metas via extractBlockTypesmeta.js)Each block has a meta.js file that is the single source of truth for all metadata:
// src/blocks/Anchor/meta.js
export default {
category: 'display',
icons: ['AiOutlineLoading3Quarters'],
valueType: null,
cssKeys: {
element: 'The anchor element.',
},
events: {
onClick: 'Called when Anchor is clicked.',
},
properties: {
type: 'object',
additionalProperties: false,
properties: {
title: { type: 'string', description: 'Text to display in the anchor.' },
disabled: { type: 'boolean', default: false, description: 'Disable the anchor if true.' },
// ...
},
},
};
| Field | Type | Description |
|---|---|---|
category | string | Block category (container, input, display, list, context) |
icons | string[] | React-Icon names used by the block |
valueType | string|null | Value type for input blocks (e.g., 'string', 'number') |
cssKeys | object | Map of CSS key names to descriptions (e.g., { element: '...' }) |
events | object | Map of event names to descriptions (e.g., { onClick: '...' }) |
properties | object | JSON Schema for the block's properties |
slots | string[] | Named slot names for containers (e.g., ['content', 'title', 'extra']) |
metas.js)Named exports of all block metadata for the package:
// src/metas.js
export { default as Anchor } from './blocks/Anchor/meta.js';
export { default as Box } from './blocks/Box/meta.js';
export { default as Icon } from './blocks/Icon/meta.js';
// ...
types.js)Each block package has a types.js that derives type information from the metas barrel using extractBlockTypes from @lowdefy/block-utils:
// src/types.js
import { extractBlockTypes } from '@lowdefy/block-utils';
import * as metas from './metas.js';
export default extractBlockTypes(metas);
This produces a types object with { blocks, icons, blockMetas } — see @lowdefy/block-utils for details. The build pipeline reads types.js to resolve plugin types without loading full block component trees (which would pull in CSS, browser APIs, and heavy libraries).
Block packages expose three entry points:
{
"exports": {
"./blocks": "./dist/blocks.js",
"./metas": "./dist/metas.js",
"./types": "./dist/types.js"
}
}
Blocks are configured in YAML:
blocks:
- id: submitButton
type: Button
properties:
title: Submit
type: primary
icon: AiOutlineSend
events:
onClick:
- id: submitForm
type: Request
params:
requestId: saveData
Each block has:
blocks: shorthand for the default content slot)1. Build time
└── Block config validated against schema
2. Page load
└── Block component loaded (code split)
└── Block registered in Slots
3. Render
└── Properties evaluated (operators resolved)
└── Component rendered
└── Events attached
4. Interaction
└── User triggers event
└── Actions executed
└── State updates
└── Re-render with new properties
Each block's meta.js file includes a properties JSON Schema. At build time, writeBlockSchemaMap imports the metas barrel from each package, calls buildBlockSchema(meta) to generate a full block schema (including class, style, events, and container blocks/areas), and writes plugins/blockSchemas.json. It also writes plugins/blockMetas.json with runtime metadata (category, valueType, initValue).
When a BlockError occurs at runtime, the server validates the received properties against the block's schema and produces a diagnostic ConfigError with a human-readable message:
[ConfigError] Block "Button" property "title" must be type "string".
This is reactive validation — it only runs when an error is caught, not on every render. See plugin-system.md for schema format details.
Blocks declare their category for:
Categories: container, input, display, list, context
meta.js Instead of Separate Files?Previously, block metadata was split between a .meta static property on the component and a separate schema.js file. The meta.js file consolidates everything into a single source of truth that:
metas.js) for lightweight type extraction