Component Blocks Reference
Reusable UI widgets for the StudioBrain Layout Designer.
Overview
Component blocks are self-contained UI widgets that can be placed in layout sections alongside form fields. They are managed through the Component Block Registry (src/lib/component-block-registry.ts) and rendered by ComponentBlockRenderer.
The Layout Designer provides a drag-and-drop palette where admins can place blocks into entity layouts. Blocks support both edit and view modes, and each block can declare configurable settings via a config_schema.
For the full Layout System architecture, see the Architecture page.
Built-in Blocks
entity-assets
| Property | Value |
|---|---|
| Category | media |
| Modes | edit, view |
| Source | builtin |
Full asset browser with upload, primary image selection, and asset role management.
Config Options:
title(string, default: “Assets”) — Section headingshowUpload(boolean, default: true) — Show upload controls
entity-timeline
| Property | Value |
|---|---|
| Category | display |
| Modes | edit, view |
| Source | builtin |
Interactive timeline panel for entity events and milestones with auto-populate capability.
Config Options: None
production-status
| Property | Value |
|---|---|
| Category | workflow |
| Modes | edit, view |
| Source | builtin |
Production pipeline status editor showing general/game/TV status with inline save.
Config Options: None (uses entity’s production_status field directly)
markdown-content
| Property | Value |
|---|---|
| Category | display |
| Modes | view |
| Source | builtin |
Rendered markdown body content using the MarkdownRenderer component.
Config Options: None
primary-image
| Property | Value |
|---|---|
| Category | media |
| Modes | view |
| Source | builtin |
Hero/primary image display with placeholder fallback when no image is set.
Config Options:
max_height(select, default: “500px”) — Maximum display height: Small (200px), Medium (300px), Large (500px), X-Large (700px), No Limitobject_fit(select, default: “cover”) — Fit mode: Cover (crop to fill), Contain (show all), Fill (stretch)rounded(select, default: “lg”) — Border radius: None, Medium, Large, Extra Large, Circle
entity-chat
| Property | Value |
|---|---|
| Category | interaction |
| Modes | view |
| Source | builtin |
AI-assisted chat panel for entity editing and brainstorming.
Config Options: None
entity-relationships
| Property | Value |
|---|---|
| Category | display |
| Modes | view |
| Source | builtin |
Automatically scans common relationship fields and renders them as grouped link cards. Scanned fields include: relationships, allies, enemies, members, key_personnel, associated_brands, subsidiaries, parent_company, primary_npcs, controlled_territories.
Config Options:
showAddButton(boolean, default: false) — Show add relationship button
entity-workflow-trigger
| Property | Value |
|---|---|
| Category | workflow |
| Modes | edit |
| Source | builtin |
Trigger ComfyUI AI generation workflows for the current entity.
Config Options: None
static-content
| Property | Value |
|---|---|
| Category | display |
| Modes | edit, view |
| Source | builtin |
Add static text, markdown notes, headers, separators, or info/warning boxes to the layout.
Config Options:
content_type(select, default: “text”) — Content type: Plain Text, Markdown, Header, Separator / Divider, Info Box, Warning Boxcontent(textarea, default: "") — The content to displayheading_level(select, default: “h3”) — Heading level for header type: H2 (Large), H3 (Medium), H4 (Small)
Plugin Blocks
Third-party plugins can register blocks via their plugin.json manifest. These render as sandboxed iframes via PluginBlockIframe. The component ID format for plugin blocks is:
plugin:{pluginId}:{panelId}For example: plugin:my-analytics:network-graph
Plugin blocks automatically appear in the Layout Designer palette under the “plugin” category. See Plugin Development for details on declaring plugin panels.
Creating a Custom Block
Step 1: Create the Component
// src/components/blocks/MyCustomBlock.tsx
interface MyCustomBlockProps {
config?: Record<string, any>;
entityType: string;
entityId: string;
data: any;
mode: 'edit' | 'view';
}
export default function MyCustomBlock({ config, data, mode }: MyCustomBlockProps) {
return <div>{/* Block UI */}</div>;
}Step 2: Register in ComponentBlockRenderer
// src/components/ComponentBlockRenderer.tsx
case 'my-custom-block':
return <MyCustomBlock config={config} entityType={entityType} entityId={entityId} data={data} mode={mode} />;Step 3: Add to Registry
// src/lib/component-block-registry.ts -- add to BUILTIN_BLOCKS array
{
id: 'my-custom-block',
label: 'My Custom Block',
description: 'Does something useful',
icon: 'Sparkles',
category: 'display',
source: 'builtin',
default_config: {},
config_schema: {
title: { type: 'string', label: 'Title', default: 'Default Title' },
showExtra: { type: 'boolean', label: 'Show Extra', default: false },
},
modes: ['edit', 'view'],
}The block will automatically appear in the Layout Designer palette under its category.
Config Schema
Blocks can declare a config_schema for designer-editable settings. The BlockConfigPanel renders this dynamically.
Supported Field Types
| Type | Renders As | Example |
|---|---|---|
string | Text input | type: 'string', label: 'Title', default: 'Assets' |
number | Number input | type: 'number', label: 'Max Items', default: 10 |
boolean | Toggle checkbox | type: 'boolean', label: 'Show Upload', default: true |
select | Dropdown | type: 'select', label: 'Visibility', default: 'team' with options array |
textarea | Multi-line text | type: 'textarea', label: 'Content', default: '' |
Select Options Format
{
type: 'select',
label: 'Max Height',
default: '500px',
options: [
{ value: '200px', label: 'Small (200px)' },
{ value: '500px', label: 'Large (500px)' },
{ value: 'none', label: 'No Limit' },
],
}Block Definition Interface
interface ComponentBlockDefinition {
id: string; // Unique block ID
label: string; // Display name in palette
description: string; // Tooltip description
icon: string; // Lucide icon name
category: 'display' | 'media' | 'interaction' | 'workflow' | 'plugin';
source: 'builtin' | string; // 'builtin' or plugin ID
default_config?: Record<string, any>;
config_schema?: Record<string, {
type: string;
label: string;
default?: any;
options?: Array<{ value: string; label: string }>;
}>;
modes?: ('edit' | 'view')[];
}Rendering
ComponentBlockRenderer receives these props:
interface ComponentBlockRendererProps {
componentId: string; // Block ID or "plugin:{id}:{panel}"
config?: Record<string, any>;
entityType: string;
entityId: string;
data: any;
mode?: 'edit' | 'view';
}It uses React.lazy for code-splitting large components (EntityAssets, EntityChatPanel) and dispatches to the correct block based on componentId. Unknown blocks show a warning fallback. Plugin blocks (matching plugin:*:*) are routed to PluginBlockIframe.
Registry API
The registry provides utility functions for working with blocks:
import { getAllBlocks, getBlockById, getBlocksByCategory } from '@/lib/component-block-registry';
// Get all blocks (built-in + plugin)
const blocks = getAllBlocks(pluginPanels);
// Look up a specific block
const block = getBlockById('entity-assets');
// Group blocks by category for palette display
const grouped = getBlocksByCategory(pluginPanels);
// { display: [...], media: [...], workflow: [...], plugin: [...] }Key Files
| Area | Path |
|---|---|
| Registry | src/lib/component-block-registry.ts |
| Renderer | src/components/ComponentBlockRenderer.tsx |
| Block types | src/types/layout.ts |
| Plugin iframe | src/components/blocks/PluginBlockIframe.tsx |
| Primary image | src/components/blocks/PrimaryImageBlock.tsx |
| Relationships | src/components/blocks/RelationshipsBlock.tsx |
| Read-only fields | src/components/blocks/ReadOnlyFieldRenderer.tsx |
| Designer palette | src/components/LayoutDesigner/ComponentBlockPalette.tsx |
| Config panel | src/components/LayoutDesigner/BlockConfigPanel.tsx |