>
Software

WordPress 7.0 lets you register blocks in pure PHP

A block in WordPress is a discrete chunk of content: a paragraph, a button, an image gallery, a call-to-action. To create one, a developer has historically had to register it twice, once in PHP and once in JavaScript, and stand up a build pipeline to bundle the JavaScript. The JavaScript half is what makes the block editable inside the Gutenberg editor (the block-based editing interface that has shipped with WordPress since 5.0). Drop the JavaScript half and you lose the editor preview.

WordPress 7.0, released in 2026, adds a third option. You can now register a block entirely from PHP, with a single 'autoRegister' => true flag in the block’s supports array, and the editor will generate the client-side registration and preview for you. There is no build step, no package.json, no React component. Just a register_block_type() call on init.

The feature is not a replacement for the JavaScript path. It is a path for cases where the JavaScript path has been overkill.

What the PHP-only path actually does

The minimum viable PHP-only block is fifteen lines. The CSS-Tricks writeup that prompted this article shows one for a Hello World block:

function css_tricks_hello_world_block() {
    register_block_type(
        'css-tricks/hello-world',
        [
            'title' => 'Hello World',
            'render_callback' => function () {
                return sprintf(
                    '<div %s>Hello World!</div>',
                    get_block_wrapper_attributes()
                );
            },
            'supports' => [
                'autoRegister' => true,
            ],
        ]
    );
}
add_action('init', 'css_tricks_hello_world_block');

The block is fully usable in the editor. Add 'attributes' => [...] to expose user-editable settings, and WordPress generates matching controls in the editor’s Settings sidebar.

The mechanism behind the curtain: when WordPress needs to render the block in the editor, it calls a REST API endpoint that runs your render_callback server-side and returns the resulting HTML. The phrase “server-side rendered block” describes what the editor does. On the public front end of the site, the same render_callback runs during the normal PHP request lifecycle, the same way it would for any server-rendered WordPress content.

What you cannot do with a PHP-only block

The trade-off is real. WordPress 7.0’s PHP-only path bakes in four structural limits that come from how the editor preview is generated.

  • No in-block controls. The block preview is HTML rendered by your PHP function. You cannot put a button, dropdown, or text input inside that preview the way a JavaScript block can. All controls live in the right-hand Settings sidebar.
  • No fresh data in the editor. When the user edits a post, the JavaScript-side data store holds the in-progress content. PHP-only blocks query the database directly, which is one save behind. If your block shows the current post title, it will show the saved title, not the title being typed.
  • No access to the current post context. The REST endpoint that renders the preview is stateless. It does not pass a post ID, so functions like get_the_title() and get_post_meta() inside your render_callback have no way to know which post is being edited. Front-end rendering works normally because it runs inside The Loop (WordPress’s main post-rendering cycle, which sets the global $post variable); the editor preview does not.
  • Limited attribute types. WordPress 7.0 supports three attribute types: strings, numbers, and booleans. The editor surfaces these as text inputs, number inputs, checkboxes, and a dropdown. The dropdown is the only advanced control, and it does not support keyed arrays (where the label shown to the user differs from the value stored in the database). Image uploads, rich text, and date pickers are not available.

The architectural limits, no in-block controls, no fresh data, no post context, are flagged in the WordPress documentation as not changing in future releases. They come from the REST-based preview architecture, not from missing features.

Where the PHP-only path actually helps

The killer use case is migration. Block themes have been in WordPress since 5.9, and they are easier to maintain than classic themes. They are also harder to adopt, because any theme feature that depended on PHP had to be rewritten in JavaScript before it could live as a block.

PHP-only registration removes that rewrite. If you have a classic theme with a PHP header, a custom widget, or a shortcode that produces structured markup, you can wrap that PHP in a block and use it inside a block theme. The editor preview will be limited, but the front-end render will match what the legacy PHP produced, because it is the same PHP.

Concretely, the things worth migrating this way:

  • Legacy widgets. The Settings sidebar is enough to reproduce a widget’s options, and the block can render on the front end with no JavaScript at all.
  • Shortcodes. Past advice has been to keep using shortcodes inside block templates. PHP-only blocks are a cleaner replacement when you control the shortcode’s PHP.
  • Template parts. Headers, footers, author bios, related-posts blocks, anything that was a get_template_part() call (a WordPress function that loads a reusable PHP file) or a custom template tag. These are exactly the pieces that have historically blocked block-theme adoption.
  • Display logic with no editor interactivity. Anything that reads from options, custom fields (per-post metadata stored via update_post_meta), or external APIs and does not need to react to user input in the editor.

The 2022 case study in the CSS-Tricks article, a classic theme with a complex PHP header, illustrates the win. The author wrapped the existing header in a server-side rendered block and migrated the rest of the theme to blocks in hours instead of days. The block preview in the editor was not responsive and dropdowns did not work in the preview, but none of that mattered. The front end rendered correctly.

Practical tips from real migrations

A few patterns from the field help avoid dead ends.

  • Do not try to make the preview match the front end exactly. The preview is a limited approximation. Aim for “good enough to recognize” rather than pixel parity. If you need pixel parity, you need JavaScript.
  • Save stable identifiers. When the dropdown limitation forces you to use category slugs instead of IDs, document the constraint. If the user later renames a category, every block referencing the old slug will silently break. A migration script that re-keys blocks on category rename is cheaper than the alternative.
  • Keep render_callback short. The function is called on every render, both in the editor (via REST) and on the front end. Heavy database queries inside render_callback will hit twice during editing, once for the initial preview, again on each attribute change. Move expensive work to a transient (a short-lived cached value stored in the WordPress options table) or a cron-fed cache.
  • Distinguish front-end and editor renders. A is_admin() check inside the callback lets you skip expensive work when the editor is the only consumer. Most of the time, you want the same output on both sides, but when you do not, this is the lever.

Trade-offs

PHP-only registration trades editor fidelity for development speed. The block preview will be less interactive than a JavaScript block, the editor will display stale data when the user changes post fields that the block reads, and you give up the ability to attach JavaScript behaviors to the block’s DOM in the editor. For a brand-new block with rich editing experience, those costs make the JavaScript path the better fit. For a migration of existing PHP, or a block that does not need editor interactivity, those costs are not real costs at all.

The feature is also new in 7.0, which means older WordPress installations cannot use it. If you maintain a plugin or theme that targets a range of WordPress versions, you cannot depend on autoRegister being available everywhere. The fallback is the traditional double registration.

The third trade-off is debugging. When a PHP-only block misbehaves, the failure surfaces as a generic REST API error in the editor. There is no React DevTools (the browser’s JavaScript inspection tools) trace to follow, no component tree to inspect. You debug by reading the render_callback and the request it generates.

If you are starting a brand-new block from scratch and want full editor controls, build it the JavaScript way. If you are migrating PHP code into a block theme, or you need a block that does not need to be editable in place, the PHP-only path is the cleanest answer WordPress has shipped in seven and a half years.

Leave a comment