Building Components
Components are reusable UI blocks -- buttons, cards, headers, footers -- that you define once and use across pages. Each component has a visual structure and a prop interface that controls what content editors can change.
Creating a Component
Press C to open the Components tab in the sidebar, or click its tab.
Click the + button to create a new component.
Enter a name (e.g.,
HeroSection,ProductCard,NavLink). Use PascalCase.The editor switches to component editing mode. You see an empty canvas with a root element.
Build the component's DOM tree using the Command Palette (Cmd+E) to add elements.
Creating a Component from Selection
If you have already built a section on a page and want to turn it into a component:
Select the element (or group of elements) on the canvas.
Use the Command Palette (Cmd+E) and search for "Create Component."
Name the component.
The selected elements become the component's structure, and the original elements are replaced with a component instance.
Editing a Component
To go back to a component for editing:
Open the Components tab (C).
Find the component in the list.
Press Enter or double-click the component name to open it in the editor.
Make your changes -- the structure and interface are editable.
Press Escape or Shift to exit component editing mode and return to the page.
All instances of the component across your project update automatically.
Defining Props
Props are the editable "knobs" that content editors use to customize each instance of your component.
While editing a component, switch to the Interface section in the Properties panel.
Click + to add a new prop.
Give it a name (e.g.,
title,variant,showIcon).Choose a type from the table below.
Set a default value.
Prop Types
Type | Input Control | Default Value Example | Notes |
|---|---|---|---|
| Text input |
| Plain text |
| Number input |
| Numeric value |
| Toggle switch |
| On/off flag |
| Dropdown |
| Must define |
| URL + target |
| Link object |
| File picker |
| Use |
| Rich text editor |
| HTML content |
Important: There is no "image" prop type. For image fields, use file with accept: "image/*":
"avatar": { "type": "file", "accept": "image/*", "default": "" }Wiring Props to Content
Use {{propName}} template syntax in text content and attributes to connect props to the component's output.
In text content:
{
"type": "node",
"tag": "h2",
"children": "{{title}}"
}In attributes:
{
"type": "node",
"tag": "img",
"attributes": {
"src": "{{imageSrc}}",
"alt": "{{imageAlt}}"
}
}In link href:
{
"type": "link",
"href": "{{link.href}}",
"target": "{{link.target}}",
"children": [
{ "type": "node", "tag": "span", "children": "{{linkText}}" }
]
}Slots
A slot is a placeholder where page authors can inject custom content into your component. Think of it like a "content hole."
While editing a component, use the Command Palette to add a Slot element.
Position it in the structure where you want injected content to appear.
When someone uses your component on a page, they can drop elements into the slot.
Important: Each component can have only one slot. If you need multiple content areas, use props instead.
Setting Category
Organize components by category to keep the Components tab tidy:
While editing a component, find the Category field in the component settings.
Enter a category name (e.g.,
ui,layout,marketing,forms).Components are grouped by category in the sidebar.
Swapping Components
To replace one component with another on a page:
Open the Components tab (C).
Activate Swap mode in the Components sidebar.
Select the component instance on the canvas that you want to replace.
Click the new component in the sidebar list.
The instance is swapped, and matching prop names carry their values over.
Reserved Names
Important: Never use "children" as a prop name in your component interface. It is reserved for the node tree's child elements and will cause conflicts.
Components with JavaScript
For interactive behavior (dropdowns, modals, accordions), create a JavaScript file alongside the component JSON file:
Button.json-- Component definitionButton.js-- Interactive behavior
The JS file is loaded automatically. See the JavaScript guide for details.
Important: JavaScript lives in a separate .js file, never inline in the JSON.
Under the Hood
Component JSON Format
Components are stored as JSON files in the components/ directory:
{
"component": {
"structure": {
"type": "node",
"tag": "div",
"style": {
"base": {
"padding": "24px",
"borderRadius": "8px",
"backgroundColor": "var(--surface)"
}
},
"children": [
{
"type": "node",
"tag": "img",
"attributes": {
"src": "{{image}}",
"alt": "{{imageAlt}}",
"loading": "lazy"
},
"style": {
"base": {
"width": "100%",
"borderRadius": "4px"
}
}
},
{
"type": "node",
"tag": "h3",
"children": "{{title}}"
},
{
"type": "node",
"tag": "p",
"children": "{{description}}"
}
]
},
"interface": {
"title": {
"type": "string",
"default": "Card Title"
},
"description": {
"type": "string",
"default": "Card description text."
},
"image": {
"type": "file",
"accept": "image/*",
"default": ""
},
"imageAlt": {
"type": "string",
"default": "Card image"
},
"variant": {
"type": "select",
"options": ["default", "featured", "compact"],
"default": "default"
}
},
"category": "ui"
}
}Component Instance on a Page
When you place a component on a page, the page JSON contains a component instance node:
{
"type": "component",
"component": "ProductCard",
"props": {
"title": "Running Shoes",
"description": "Lightweight and fast.",
"image": "/images/shoes.jpg",
"variant": "featured"
}
}Only the props that differ from defaults need to be specified.
Structure with a Slot
A component that accepts injected content uses a slot marker:
{
"component": {
"structure": {
"type": "node",
"tag": "section",
"children": [
{
"type": "node",
"tag": "h2",
"children": "{{heading}}"
},
{
"type": "slot"
}
]
},
"interface": {
"heading": {
"type": "string",
"default": "Section Title"
}
}
}
}On the page, the component instance can have children that fill the slot:
{
"type": "component",
"component": "ContentSection",
"props": { "heading": "Our Story" },
"children": [
{ "type": "node", "tag": "p", "children": "We started in 2020..." },
{ "type": "node", "tag": "p", "children": "Today we serve thousands of customers." }
]
}Style Mappings in Components
Props can drive dynamic styles using style mappings. For example, a variant prop can change the background color:
"style": {
"base": {
"backgroundColor": {
"_mapping": true,
"prop": "variant",
"values": {
"primary": "var(--primary)",
"secondary": "var(--secondary)",
"outline": "transparent"
}
}
}
}See the Styling guide for more on style mappings.
Next Steps
Styling -- Style your components with responsive CSS.
Interactive Styles -- Add hover effects and state-based styles.
JavaScript -- Add interactive behavior to components.