Custom Fields
Custom fields let you define reusable field groups that can be referenced across multiple blocks. Think of them as field templates: define once, use everywhere.
File System Registration
Create a field.json file inside a fields/ directory in your blockstudio directory:
theme/
└── blockstudio/
└── fields/
└── hero/
└── field.json{
"$schema": "https://blockstudio.dev/schema/field",
"name": "hero",
"title": "Hero Section",
"attributes": [
{ "id": "heading", "type": "text", "label": "Heading", "default": "Hello World" },
{ "id": "description", "type": "textarea", "label": "Description" },
{ "id": "enabled", "type": "toggle", "label": "Enabled" }
]
}PHP Filter Registration
Register custom fields programmatically using the blockstudio/fields filter:
add_filter('blockstudio/fields', function ($fields) {
$fields['cta'] = [
'title' => 'Call to Action',
'attributes' => [
['id' => 'text', 'type' => 'text', 'label' => 'Button Text'],
['id' => 'url', 'type' => 'text', 'label' => 'URL'],
],
];
return $fields;
});Usage in block.json
Reference a custom field using the custom/{name} type:
{
"blockstudio": {
"attributes": [
{ "type": "custom/hero" }
]
}
}This expands inline to the fields defined in the custom field definition. The expanded fields use their original IDs (heading, description, enabled).
Custom fields can also reuse complete group definitions:
{
"name": "section-header",
"attributes": [
{
"id": "section-header",
"type": "group",
"title": "Header",
"attributes": [
{ "id": "kicker", "type": "text", "label": "Kicker" },
{ "id": "title", "type": "text", "label": "Title" }
]
}
]
}{
"blockstudio": {
"attributes": [
{ "type": "custom/section-header" }
]
}
}The group children are stored as section-header_kicker,
section-header_title, and so on. In templates, use bs_get_group() to
extract them into a clean group object.
ID Structure
Use idStructure to prefix or transform field IDs. The {id} placeholder is replaced with the original field ID:
{
"blockstudio": {
"attributes": [
{ "type": "custom/hero", "idStructure": "hero_{id}" }
]
}
}This produces fields with IDs: hero_heading, hero_description, hero_enabled.
Overrides
Customize individual fields per-instance using overrides. Keys are the original field IDs from the definition:
{
"blockstudio": {
"attributes": [
{
"type": "custom/hero",
"idStructure": "hero_{id}",
"overrides": {
"heading": { "default": "Welcome", "label": "Title" },
"enabled": { "id": "active" }
}
}
]
}
}Override properties are merged on top of the original field definition. An id override bypasses the idStructure pattern. In the example above, enabled becomes active instead of hero_enabled.
Conditions
Conditions inside custom field definitions are automatically rewritten when
idStructure is applied. A field that conditionally depends on a sibling field
keeps working after expansion.
{
"name": "font-size",
"attributes": [
{ "id": "enable", "type": "toggle", "label": "Modify font size" },
{
"id": "min", "type": "number", "label": "Min Size",
"conditions": [[{ "id": "enable", "operator": "==", "value": true }]]
},
{
"id": "max", "type": "number", "label": "Max Size",
"conditions": [[{ "id": "enable", "operator": "==", "value": true }]]
}
]
}{ "type": "custom/font-size", "idStructure": "title_font_{id}" }Blockstudio expands this to title_font_enable, title_font_min,
title_font_max and rewrites the conditions to reference
title_font_enable instead of enable. The toggle controls work
exactly as if the fields were defined inline.
Reference-level conditions
Add conditions on the reference to apply them to every expanded field. This
is useful for hiding an entire group behind an external toggle:
{
"type": "custom/font-size",
"idStructure": "subtitle_font_{id}",
"conditions": [
[{ "id": "show_advanced", "operator": "==", "value": true }]
]
}These conditions merge with any conditions already defined inside the field
definition. Condition IDs are also rewritten via idStructure.
Multiple Custom Fields
You can use multiple custom fields in a single block, alongside regular fields:
{
"blockstudio": {
"attributes": [
{ "id": "intro", "type": "text", "label": "Intro" },
{ "type": "custom/hero", "idStructure": "hero_{id}" },
{ "type": "custom/cta", "idStructure": "cta_{id}" }
]
}
}Nesting
Custom fields work inside groups, tabs, and repeaters:
{
"blockstudio": {
"attributes": [
{
"type": "group",
"id": "content",
"title": "Content",
"attributes": [
{ "type": "custom/hero", "idStructure": "hero_{id}" }
]
}
]
}
}Additional Discovery Paths
Use the blockstudio/fields/paths filter to add additional directories for field discovery:
add_filter('blockstudio/fields/paths', function ($paths) {
$paths[] = get_stylesheet_directory() . '/blockstudio/fields';
return $paths;
});