Blocks
A block in Wonderpress is a thin Gutenberg wrapper over a partial. It cannot exist without one. The wrapper is two files in blocks/<slug>/: a block.json and a render.php that news up the partial class and echoes ->render(). The PHP partial stays the only source of HTML.
Creating blocks
The usual path is at partial creation time:
wonderpress partial create --name Hero --blockTo add a wrapper to a partial that already exists:
wonderpress block create HeroOutput is identical either way. Run block create with no name and it offers the partials that do not yet have a block. If the partial does not exist at all, create it with --block rather than scaffolding an empty one.
wonderpress block list # blocks and the partials behind them wonderpress block remove # delete the wrapper, keep the partial
block remove deletes the block directory and strips the block from the manifest; the partial is left intact.
How registration works
wonderpress-core runs an init pass that calls register_block_type() on every block.json in the active theme’s blocks/ directory. The CLI writes the block; core registers it. Blocks appear in the inserter under a Wonderpress editor category named after your project. If a new block is missing from the inserter, run composer install in the theme directory: registration lives in vendor/.
Blocks are emitted as <namespace>/<slug>, using the namespace pinned at init. WordPress writes that name into post content, which is why the namespace can never change. See the Quickstart.
How the editor preview works
There is no per-block JSX and no editor bundler. Editor presence is one buildless script in wonderpress-core, editor-preview.js. It registers each block with:
- Inspector controls drawn from the manifest schema, which Wonderpress passes to the editor as
window.wonderpressBlockSchemas. Controls match the manifest semantics: aselectgets a dropdown of choice keys, animagegets MediaUpload, arepeatergets add/remove row UI, andwhenconditionals hide fields based on sibling values. - A live preview via ServerSideRender: the editor asks WordPress to render the block over REST, so what you see is the real PHP output.
Generated block.json has no editorScript. block.json stores only WordPress attribute types (string, boolean, object, array); the richer semantics live in the manifest schema.
Content model
Wonderpress blocks have no inner content. They render from the partial and take their values from the sidebar; a client cannot type directly into one. ServerSideRender’s HTML is inert, so live React InnerBlocks cannot occupy a slot inside that PHP shell without duplicating markup. This is deferred by design rather than an unfinished feature: content is edited through block sidebar controls.
Block attribute storage is JSON-safe by convention: IDs and plain arrays or objects, never a WP_Post or a PHP resource. The exact wire format per property type is specified in Property types.
Dual partials: block plus ACF
--block and --acf combine. The block authors through Gutenberg attributes; a PHP caller hydrates the same partial from ACF. Every property type on a dual partial must be authorable in both editors with the same semantic value reaching the view, a guarantee the CLI enforces at create and sync time. See Working with ACF and Property types.
One placement rule to respect: do not locate the ACF field group on a screen where editors also insert the block, or the same Hero UI shows up twice.
Things worth knowing before they surprise you
- A theme switch takes the blocks with it. wonderpress-core registers blocks from the active theme’s
blocks/directory, so switching themes leaves already-published block content rendering as its raw HTML fallback. That is ordinary WordPress behavior for theme-provided blocks, and worth knowing before you switch a live site. - A grey placeholder instead of a real preview means the editor script did not load. Check that the theme’s
vendor/directory exists and the browser console is clean.