How to Create a Custom Gutenberg Block Without React
Custom blocks are the most useful thing you can add to a WordPress site: a testimonial, a pricing card, a feature grid that editors can drop onto any page without breaking the design. The problem is how WordPress expects you to build them. The official way means React, JSX, npm and a build step. This guide shows the three realistic ways to build a custom Gutenberg block, and how to build one with PHP only.
What a custom Gutenberg block is
A custom block is a new block type that appears in the block inserter next to Paragraph, Image and Heading. Every time an editor inserts it, they get the same markup and styles, and they only change the content you allow them to change. That is the big win over copy-pasted HTML or patterns: the design stays in your code, and the content stays in the editor’s hands.
Every block has three parts:
- Configuration (
block.json): the name, category, attributes and which files to load. - The editor side: what the block looks like and which settings it shows while editing.
- The front end: the HTML visitors see.
Option 1: the official way (React + build step)
WordPress’s own tooling scaffolds a block with one command:
npx @wordpress/create-block@latest my-testimonial
cd my-testimonial
npm start
You get a plugin with block.json, an edit.js written in React/JSX, a save.js (or a render.php for dynamic blocks) and a webpack build. Every change goes through npm start or npm run build.
This is the most flexible approach, and it’s what core blocks use. It is also a lot of machinery if what you want is “a card with a title, text and a button”: you need Node.js on every machine that edits the block, you ship build output, and the editor preview is a separate React component you have to keep in sync with the front end.
Option 2: ACF Blocks
If you already use Advanced Custom Fields Pro, you can register a block in block.json with an acf section and render it with a PHP template. Fields come from an ACF field group. It avoids React, but the block depends on ACF Pro, the fields live in a separate field-group screen, and the editor preview is ACF’s rendering rather than native Gutenberg controls.
Option 3: PHP only, with FanCoolo WP
FanCoolo WP builds native Gutenberg blocks from inside WordPress. You write the output in PHP, the style in SCSS, and add fields with a visual attributes manager. FanCoolo generates the block.json for you, so the result is a real block type, registered like any core block.
Step 1: create the block
In WordPress, open FanCoolo and add a new block. Give it a name, for example “Testimonial”. That’s the name editors will see in the block inserter.
Step 2: add the fields editors can change
Open the Attributes tab and click + Add attribute for each field:
| Field | Type | Attribute name |
|---|---|---|
| Author name | Text | authorName |
| Quote | Textarea | quoteText |
| Show photo | Toggle | showPhoto |
FanCoolo shows a PHP example under each attribute that you can copy straight into your code. The fields appear in the Gutenberg sidebar when the block is selected, using native WordPress components, so editors get the same experience as with core blocks.
Step 3: write the output in PHP
In the Content tab, write the block’s HTML. The values editors entered are in $attributes:
<?php
$author = $attributes['authorName'] ?? '';
$quote = $attributes['quoteText'] ?? '';
?>
<figure <?php echo get_block_wrapper_attributes( [ 'class' => 'testimonial' ] ); ?>>
<blockquote><?php echo esc_html( $quote ); ?></blockquote>
<figcaption><?php echo esc_html( $author ); ?></figcaption>
</figure>
Because this is PHP, the same template can pull data from the database, loop over posts, or include Symbols: reusable PHP components (a button, a card) that you write once and use in many blocks.
Step 4: style it
Add SCSS in the Style tab. It’s compiled to CSS and loaded both in the editor and on the front end, so what editors see is what visitors get. Shared variables and mixins can live in SCSS Partials, and a partial marked as global is included in every block. If the editor needs something different (for example no animations while editing), use the Editor Style tab.
Step 5: add interactivity (optional)
Tabs, accordions and sliders need JavaScript on the front end. Put it in the View tab. It loads only on the front end, not in the editor. Turn on the Module toggle if you want to use the WordPress Interactivity API.
Save, and the block is in the inserter, ready to use on any page.
Which option should you pick?
| React + build | ACF Blocks | FanCoolo WP | |
|---|---|---|---|
| Language | React/JSX + PHP | PHP | PHP (+ optional JS) |
| Build step | Yes (npm) | No | No |
| Fields | Hand-written React controls | ACF field groups | Visual attributes manager, native controls |
| Needs | Node.js | ACF Pro | FanCoolo WP |
| Best for | Complex editor UIs, core-like blocks | Sites already built around ACF | Client sites, agencies, PHP developers |
If your blocks need a completely custom editing interface, the official React route is worth it. For the everyday blocks most sites need (cards, heroes, testimonials, feature grids, post lists), PHP is enough, and skipping the build step makes them faster to build and easier to hand over.
