Options System
Understanding how the options system works is crucial to building effective BlocksWeb components. This guide explains the complete flow from schema definition to the visual editor to your component props.
Overview
The options system is the bridge between your React components and the visual editor. It defines what properties content editors can modify and how those properties are presented in the editor UI.
How Options Work
The Complete Flow
1. Developer defines schema →
2. Component registered in config →
3. User adds component to page →
4. Editor loads schema →
5. Options panel renders UI →
6. User changes value →
7. updateBlockProps() called →
8. Component re-renders with new props →
9. Auto-save to APISchema Definition
When you define a component schema, you're creating a contract:
import { IBlockswebComponent } from '@blocksweb/core';
type HeroProps = {
title: string;
subtitle: string;
buttonText: string;
};
const Hero: IBlockswebComponent<HeroProps> = ({ title, subtitle, buttonText }) => {
return (
<section>
<h1>{title}</h1>
<p>{subtitle}</p>
<button>{buttonText}</button>
</section>
);
};
Hero.schema = {
displayName: 'Hero Section',
options: [
{
type: 'text',
name: 'title', // Must match prop name
label: 'Hero Title',
default: 'Welcome'
},
{
type: 'text',
name: 'subtitle',
label: 'Subtitle',
default: 'Get started today'
},
{
type: 'text',
name: 'buttonText',
label: 'Button Text',
default: 'Learn More'
}
]
};
export default Hero;Behind the Scenes
When a user selects your component in the editor:
1. Block Selection
// User clicks component in canvas
// EditorContext.setSelectedBlockId('hero-123')
const selectedBlock = findRecursiveBlockById(blocks, 'hero-123');2. Schema Lookup
// Editor finds registered component
const component = editorComponents.find(c => c.schema.displayName === selectedBlock.type);
const schema = component.schema;3. Options Panel Rendering
// For each option in schema
schema.options.map(option => {
switch(option.type) {
case 'text':
return <TextInput {...option} />;
case 'richtext':
return <RichTextEditor {...option} />;
case 'image':
return <ImagePicker {...option} />;
// ... etc
}
});4. Value Updates
// User types in text field
const handleChange = (optionName, newValue) => {
updateBlockProps(selectedBlockId, {
[optionName]: newValue
});
};5. Component Re-render
// Block props updated in EditorContext
// Canvas re-renders component with new props
<Hero
title="New Title" // Updated value
subtitle="..."
buttonText="..."
/>Data Structure
Block Storage
Each component instance is stored as a block:
type BlockType = {
id: string; // Unique identifier
type: string; // Component displayName
props: Record<string, any>; // All option values
};
// Example in storage:
{
id: "hero-abc123",
type: "Hero Section",
props: {
title: "Welcome to Our Site",
subtitle: "We help you succeed",
buttonText: "Get Started",
backgroundColor: "#f0f0f0"
}
}Page Content Structure
{
blocks: [
{
id: "hero-1",
type: "Hero Section",
props: { title: "Welcome", ... }
},
{
id: "features-1",
type: "Feature Grid",
props: { title: "Features", items: [...] }
}
],
locales: {
en: { /* translations */ },
nl: { /* translations */ }
}
}Option Processing
Type Coercion
BlocksWeb automatically handles type conversions:
// Number option
{
type: 'number',
name: 'columns',
default: 3
}
// Input: "4" (string from input)
// Stored: 4 (number)
// Received in component: 4 (number)Default Values
Defaults are applied when:
- Component is first added
- Option value is undefined
- Reset to defaults is triggered
// User adds Hero component
// Initial props automatically set:
{
title: "Welcome", // From default
subtitle: "Get started today", // From default
buttonText: "Learn More" // From default
}Auto-Save System
BlocksWeb automatically saves changes:
// User changes value
updateBlockProps('hero-123', { title: 'New Title' });
// After 3 seconds of inactivity
// Auto-save hook triggers
await saveToAPI({
pageId: currentPage.id,
blocks: updatedBlocks
});
// UI shows "Saving..." then "Saved"Best Practices
1. Match Names Exactly
// ✅ Good - names match
type Props = { title: string };
options: [{ name: 'title', ... }]
// ❌ Bad - names don't match
type Props = { pageTitle: string };
options: [{ name: 'title', ... }] // Won't work!2. Always Provide Defaults
// ✅ Good - component looks complete immediately
{
type: 'text',
name: 'title',
default: 'Welcome to Our Site'
}
// ❌ Bad - empty component after adding
{
type: 'text',
name: 'title'
// No default
}3. Use Appropriate Types
// ✅ Good - right tool for the job
{ type: 'richtext', name: 'content' } // For formatted text
{ type: 'image', name: 'hero' } // For images
{ type: 'color', name: 'bg' } // For colors
// ❌ Bad - forcing wrong types
{ type: 'text', name: 'content' } // Can't format text
{ type: 'text', name: 'hero' } // Can't upload images4. Add Help Text
{
type: 'text',
name: 'metaDescription',
label: 'Meta Description',
help: 'Appears in search results. Keep it between 150-160 characters.',
maxLength: 160
}Next Steps
- Options Reference - Complete list of all 12 option types
- Creating Components - Build components with options
- Data Flow - Understand the complete data cycle
- Utilities - RichText, Image, BlockOutlet helpers