Blockstudio
BlocksAttributes

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
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:

block.json
{
  "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:

fields/section-header/field.json
{
  "name": "section-header",
  "attributes": [
    {
      "id": "section-header",
      "type": "group",
      "title": "Header",
      "attributes": [
        { "id": "kicker", "type": "text", "label": "Kicker" },
        { "id": "title", "type": "text", "label": "Title" }
      ]
    }
  ]
}
block.json
{
  "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:

block.json
{
  "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:

block.json
{
  "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.

fields/font-size/field.json
{
  "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 }]]
    }
  ]
}
block.json
{ "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:

block.json
{
  "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:

block.json
{
  "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:

block.json
{
  "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;
});

On this page