Independent Editorial DeskWordPress Releases, Builds, and Operations
Back to Archive
Implementation Notes

WordPress block.json Example: Create a Custom Block Without Theme Lock-In

Create a reusable custom block with block.json metadata, scoped assets, dynamic rendering, and frontend output escaping.

Published

April 17, 2026

Reading Time

3 min read

Updated

April 17, 2026

Abstract block editor grid representing a WordPress block.json custom block.
Build PatternImplementation Notes

Implementation Notes

Extension points, code paths, and implementation choices that should survive contact with production.

Best For

WordPress developers, agencies, and technical teams building custom plugin or theme functionality with cleaner operational defaults.

Primary Topics

Implementation Notes

Editorial Focus

Build Pattern: Extension points, code paths, and implementation choices that should survive contact with production. Updated on April 17, 2026.

Full Report

Last reviewed: April 17, 2026

A modern WordPress custom block should not start as a random editor script pasted into a theme. The maintainable pattern is a small plugin with block.json metadata, registered assets, clear attributes, and a rendering strategy that matches the job. That is why searches for a WordPress block.json example keep growing as more sites move real editorial workflows into the block editor.

This guide shows the shape of a production-ready custom block plugin. It uses block.json as the source of truth, keeps the block independent from the active theme, and avoids the most common mistakes around asset loading and frontend output.

Who this guide is for

This article is for WordPress developers building reusable editor components, agencies replacing shortcodes, and teams that want custom editorial blocks without locking business logic into a theme.

Why block.json matters

WordPress supports block metadata through block.json. The metadata file describes the block name, title, scripts, styles, attributes, rendering behavior, editor support, and related details. Keeping this configuration in one file makes registration more predictable and helps WordPress load block assets only where they are needed.

Recommended plugin structure

vulnwp-notice-card/
  vulnwp-notice-card.php
  build/
    block.json
    index.js
    render.php
    style-index.css

In a real build process, the build directory is usually generated from a src directory by @wordpress/scripts or another bundler. The important part for this guide is the runtime contract: WordPress registers the built block metadata, not a loose collection of manually enqueued files.

Main plugin file

<?php
/**
 * Plugin Name: VulnWP Notice Card Block
 * Description: Example custom block registered from block.json.
 * Version: 1.0.0
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

add_action(
	'init',
	function () {
		register_block_type( __DIR__ . '/build' );
	}
);

The plugin does one thing: it registers the block from the metadata directory. This keeps block registration short and lets block.json define the important details.

Example block.json

{
	"$schema": "https://schemas.wp.org/trunk/block.json",
	"apiVersion": 3,
	"name": "vulnwp/notice-card",
	"version": "1.0.0",
	"title": "Notice Card",
	"category": "widgets",
	"icon": "shield",
	"description": "Displays a reusable editorial notice card.",
	"textdomain": "vulnwp-notice-card",
	"attributes": {
		"message": {
			"type": "string",
			"default": "Review this deployment note before release."
		}
	},
	"supports": {
		"html": false
	},
	"editorScript": "file:./index.js",
	"style": "file:./style-index.css",
	"render": "file:./render.php"
}

The name must be namespaced. Use a plugin or organization prefix and a stable block slug. Do not use a generic name that may collide with another plugin.

Editor script example

import { registerBlockType } from '@wordpress/blocks';
import { TextControl } from '@wordpress/components';
import { useBlockProps } from '@wordpress/block-editor';
import metadata from './block.json';

registerBlockType( metadata.name, {
	edit( { attributes, setAttributes } ) {
		const blockProps = useBlockProps( {
			className: 'vulnwp-notice-card',
		} );

		return (
			<div { ...blockProps }>
				<strong>Notice Card</strong>
				<TextControl
					label="Message"
					value={ attributes.message }
					onChange={ ( message ) => setAttributes( { message } ) }
				/>
			</div>
		);
	},
} );

This editor script keeps the block simple: one string attribute and one editor control. Complex blocks should still start with a small attribute model. If every content decision becomes deeply nested JSON, future migrations become harder.

Dynamic render example

<?php
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

$message = isset( $attributes['message'] )
	? sanitize_text_field( $attributes['message'] )
	: '';

if ( '' === $message ) {
	return;
}
?>

<div <?php echo get_block_wrapper_attributes( array( 'class' => 'vulnwp-notice-card' ) ); ?>>
	<strong><?php esc_html_e( 'Deployment note', 'vulnwp-notice-card' ); ?></strong>
	<p><?php echo esc_html( $message ); ?></p>
</div>

This example uses dynamic rendering because the final HTML is controlled by PHP. Dynamic rendering is useful when output should stay centrally controlled, depend on server-side data, or evolve without opening every post in the editor.

When to use static rendering instead

Static blocks save the final markup into post content. They can be ideal for simple presentational components that do not need server-side data. Dynamic blocks are better when output depends on plugin settings, database data, permissions, API responses, or markup you may need to change later across many posts.

Production checklist

  • Register the block from a plugin unless the block is truly theme-only.
  • Use a namespaced block name such as vendor/block-name.
  • Declare attributes explicitly in block.json.
  • Use supports.html: false when raw HTML editing would create risk or confusion.
  • Escape frontend output in the render callback.
  • Keep frontend styles scoped to the block class.
  • Test the editor, frontend, REST response, and deactivation behavior.

Common mistakes

  • Registering blocks in the theme by default. Reusable editorial blocks should survive theme changes.
  • Using vague block names. Generic names increase collision risk and make debugging harder.
  • Loading scripts globally. Block metadata lets WordPress load block assets more selectively.
  • Skipping output escaping. Attribute data still needs context-aware escaping on the frontend.
  • Overbuilding the first version. Start with the smallest block contract that solves the editorial need.

Related reading

If you are replacing shortcodes with blocks, read our safe shortcode example first. It helps identify which shortcode attributes should become block attributes and which output should remain server-side.

References and further reading

Popular Guides

Popular WordPress guides to read next.

These articles connect recurring production concerns: implementation details, updates, troubleshooting, recovery paths, and operational cleanup.

Continue Reading

More from the archive.

Diagnostic dashboard scene representing a WordPress Site Health review before major updates.
01Build Pattern
Implementation Notes

Build Pattern

Extension points, code paths, and implementation choices that should survive contact with production.

May 21, 2026 · 3 min read

WordPress Site Health Check Before Major Updates: What to Review First

A pre-update WordPress Site Health checklist covering loopbacks, connectivity, debug settings, and environment readiness.

Structured data and route review scene representing permalink validation after a WordPress migration.
02Build Pattern
Implementation Notes

Build Pattern

Extension points, code paths, and implementation choices that should survive contact with production.

May 21, 2026 · 3 min read

WordPress Permalink Checklist After Migration: Catch URL Problems Early

A post-migration WordPress permalink checklist for checking rewrite rules, post URLs, archives, and redirect noise.

Technical media workspace representing image preparation and optimization before upload to WordPress.
03Build Pattern
Implementation Notes

Build Pattern

Extension points, code paths, and implementation choices that should survive contact with production.

May 21, 2026 · 3 min read

WordPress Image Optimization Checklist: What to Fix Before Upload

A practical WordPress image optimization checklist covering dimensions, compression, formats, and Media settings before upload.