Custom blocks in 3 files

Define fields in block.json, render them in a template, style with CSS or SCSS. No JavaScript, no build step, no boilerplate.

Define fields in JSON

Add a blockstudio key to your block.json. Define attributes with types like text, toggle, and color. Blockstudio generates the editor UI automatically. 26 field types available, from simple text inputs to repeaters and code editors.

Full JSON Schema support gives you autocomplete and validation in VS Code, PhpStorm, and any editor that supports it.

Field types reference
{
  "name": "starter/hero",
  "title": "Hero",
  "blockstudio": {
    "attributes": {
      "heading": {
        "type": "text"
      },
      "showCta": {
        "type": "toggle",
        "label": "Show CTA"
      },
      "background": {
        "type": "color"
      }
    }
  }
}

3-file workflow

block.json for fields, a template for markup, an optional stylesheet. No boilerplate, no build step.

React in PHP

RichText, InnerBlocks, and useBlockProps work directly in PHP, Twig, or Blade templates.

SCSS and Tailwind

Use plain CSS, SCSS, or Tailwind CSS. Blockstudio compiles and minifies assets automatically.

26 field types

Text, color, image, repeater, select, code editor, and more. All configured in JSON.

JSON Schema

Full autocomplete and validation in VS Code, PhpStorm, and any editor with JSON Schema support.

Three template languages

PHP out of the box. Twig via Timber. Blade via jenssegers/blade. Same variables, same components.

npm imports

Import npm packages directly in script.js. Downloaded locally for production, CDN in the editor.

Interactivity API

Enable WordPress Interactivity API with a single flag. Works in both the editor and frontend.

React components in PHP templates

Use <RichText /> for inline editing in the block editor and static HTML on the frontend. <InnerBlocks /> for nested child blocks. And useBlockProps to mark the block wrapper. All from a single template file.

In the editor, these become real React components. On the frontend, they render as plain server-side HTML. No JavaScript shipped to your visitors.

Explore components
<div useBlockProps class="container">
  <RichText
    class="text-xl font-semibold"
    tag="h1"
    attribute="richtext"
    placeholder="Enter headline"
  />

  <?php if ($a['showCta']): ?>
    <a href="<?= $a['ctaUrl'] ?>">
      <?= $a['ctaLabel'] ?>
    </a>
  <?php endif; ?>

  <InnerBlocks class="mt-4 p-4 border" />
</div>

Write in PHP, Twig, or Blade

Use the template language you already know. PHP templates work out of the box. Add Timber for Twig or jenssegers/blade for Blade. Same variables, same components, same behavior.

Template variables $a (attributes), $b (block), $c (context), and $innerBlocks are available in all three languages.

Template languages
{# index.twig #}
<div useBlockProps class="card">
  <RichText
    tag="h2"
    attribute="title"
    placeholder="Card title"
  />

  <img src="{{ a.image.url }}"
       alt="{{ a.image.alt }}" />

  <div class="card__body">
    {{ a.description }}
  </div>

  <InnerBlocks allowedBlocks='["core/button"]' />
</div>

Conditional logic and dynamic options

Show and hide fields based on other values. Operators include ==, !=, includes, empty, and comparison operators. Combine multiple conditions with OR logic.

Populate select options dynamically from posts, users, terms, or external APIs. Mix static options with populated data. No hardcoded lists.

Conditional logic
{
  "attributes": {
    "layout": {
      "type": "select",
      "options": ["default", "featured", "minimal"],
      "populate": {
        "type": "query",
        "query": "posts",
        "arguments": {
          "post_type": "layout",
          "posts_per_page": -1
        }
      }
    },
    "columns": {
      "type": "range",
      "min": 1,
      "max": 4,
      "conditions": [{
        "id": "layout",
        "operator": "!=",
        "value": "minimal"
      }]
    }
  }
}

ES modules without a build step

Add a script.js and it loads as an ES module. Use import syntax natively. Pull packages from npm with the npm: prefix and Blockstudio downloads them locally for production. No bundler, no config.

The editor uses a CDN for instant preview. The frontend uses local modules for zero external dependencies. Same module, same version, enqueued only once.

Asset processing
import { register } from
  "npm:swiper@11.0.0/element/bundle";

import "npm:swiper@11.0.0/swiper.min.css";

register();

const el = document.querySelector(".swiper");
Object.assign(el, {
  slidesPerView: 3,
  spaceBetween: 20,
  loop: true,
});
el.initialize();

Post meta and options storage

Store field values in post meta or site options alongside block attributes. Queryable via WP_Query.

Conditional logic

Show and hide fields based on other values with operators like ==, !=, includes, empty, and comparisons.

Dynamic populations

Populate select options from posts, users, terms, functions, or external APIs.

Variations and overrides

Register block variations with different defaults. Override any existing block's attributes, assets, or templates.

Plus

The official extension kit

Premium site templates, AI system instructions, and a private Discord community. One-time purchase, lifetime updates.

  • 150+ site templates
  • AI system instructions
  • Lifetime updates
  • Private Discord community
Get Plus →
block.json
{"name": "starter/hero","title": "Hero","blockstudio": { ... }}
index.php
style.css
150+ templates includedPlus