Install
This is the repository archive. It installs, but the folder name will carry the branch suffix and updates will not flow.
Install straight from GitHub with WP-CLI:
wp plugin install https://github.com/busayor2020/craft-blocks/archive/refs/heads/master.zipA small, production-shaped WordPress plugin that adds two custom Gutenberg blocks built with React. It exists to show, in one compact codebase, how the two halves of block development fit together: the React editor experience and the PHP that renders on the front end.
Both blocks are registered from block.json, compiled with @wordpress/scripts, and written to the current block API (version 3).
What this demonstrates
| Concept | Where it lives |
|---|---|
| Dynamic, server-rendered block | Post Grid (render.php + WP_Query) |
| Static block with saved markup | Callout (save.js) |
Editor data layer with useSelect and the core-data store |
Post Grid editor preview |
Nested content via InnerBlocks (useInnerBlocksProps) |
Callout |
block.json metadata API (apiVersion 3) |
Both blocks |
| Declarative block supports (color, spacing, typography, alignment) | Both blocks |
Inspector controls from @wordpress/components |
Both blocks |
| Block styles and content variants | Callout (solid and outline styles, four color variants) |
| Escaped, injection-safe server output | Post Grid render.php |
| Shared module reused across edit and save | Callout (shared.js icon) |
| Modern build tooling and linting | Whole repo (@wordpress/scripts) |
| Translation-ready strings | Both blocks (__() plus a text domain) |
The blocks
Post Grid (dynamic)
A responsive grid of recent posts. In the editor it pulls live posts straight from the block editor data layer with useSelect and the core store, so the preview reflects real content without a server round-trip. On the front end it is rendered by render.php using WP_Query, so the output always reflects the current set of published posts.
Controls: number of posts, columns, order and order-by, and toggles for the featured image, excerpt, and date. It also opts into the color, spacing, and alignment supports, so the block inherits theme styling and works with wide and full widths.
Callout (static)
A flexible callout box for notes, tips, and warnings. It uses InnerBlocks, so any content can go inside (paragraphs, lists, buttons, and more). The saved markup is produced by save.js and stored in post content.
It ships with four color variants (info, success, warning, tip), two registered block styles (solid and outline), an optional icon, and typography and spacing supports. The icon component is shared between the editor and the saved output, which keeps block validation clean.
Tech stack
- React (via the
@wordpress/elementand block editor packages) - WordPress Block API v3 with
block.json - PHP for dynamic server rendering
- SCSS, compiled by
@wordpress/scripts @wordpress/scriptsfor build, lint (ESLint and Stylelint), and packaging
Requirements
- WordPress 6.5 or newer
- PHP 7.4 or newer
- Node.js 20 or newer (only needed to build the assets)
Getting started
git clone https://github.com/Busayor2020/craft-blocks.git
cd craft-blocks
npm install
npm run build
Then place the folder in wp-content/plugins/ (or symlink it) and activate Craft Blocks from the Plugins screen. The two blocks appear in the editor inserter under the Widgets and Text categories.
Local development
npm start
This runs the build in watch mode and recompiles on save.
To spin up a throwaway WordPress site with the plugin already active (requires Docker):
npx wp-env start
The site runs at http://localhost:8888 (admin at /wp-admin, user admin, password password).
Other useful scripts:
npm run lint:js # lint JavaScript
npm run lint:css # lint styles
npm run format # format the source
npm run plugin-zip # build a distributable zip
Project structure
craft-blocks/
├── craft-blocks.php Plugin bootstrap, registers both blocks
├── src/
│ ├── post-grid/ Dynamic block
│ │ ├── block.json Metadata, attributes, supports
│ │ ├── index.js Registration
│ │ ├── edit.js Editor UI and live data preview
│ │ ├── render.php Server-side front-end render
│ │ ├── style.scss Front-end and shared styles
│ │ └── editor.scss Editor-only styles
│ └── callout/ Static block
│ ├── block.json
│ ├── index.js
│ ├── edit.js Editor UI with InnerBlocks
│ ├── save.js Saved markup
│ ├── shared.js Icon and variants shared by edit and save
│ ├── style.scss
│ └── editor.scss
├── eslint.config.cjs Extends the wp-scripts default config
├── .wp-env.json Local environment definition
└── build/ Compiled output (generated, not committed)
A note on static versus dynamic
The two blocks are a deliberate pairing.
A static block saves its HTML into post content when you press save. It is fast to serve and self-contained, which suits presentational content like a callout whose markup does not change after it is written.
A dynamic block saves only its attributes and renders its HTML in PHP on each request. That is the right choice when the output depends on data that changes over time, like a list of recent posts. Post Grid uses this approach so the grid stays current as posts are published or updated, with no need to re-save the page that contains it.
Author
Victor Busayo GitHub: https://github.com/Busayor2020 LinkedIn: https://www.linkedin.com/in/victor-busayo/
License
GPL-2.0-or-later. See LICENSE.