handmadeweb/buildamic
Buildamic is a Laravel page-builder package for composing flexible, block-based content layouts. It provides reusable components, configurable fields, and a simple editor experience so developers can ship editable pages without reworking their app's structure.
Install and confirm the pairing first. Buildamic is a Statamic 3 addon, so lock your Statamic version before anything else:
composer require handmadeweb/buildamic
php artisan vendor:publish --provider="HandmadeWeb\Buildamic\ServiceProvider"
Then immediately check composer show handmadeweb/buildamic against your statamic/cms constraint. With 11 stars and zero dependents, this is effectively a single-agency internal tool that escaped — treat compatibility as unverified until you've clicked through the Control Panel yourself.
Read these three places before writing a line of template code.
config/buildamic.php (after publishing) — which fieldsets are exposed as blocks, and any rendering/recursion settings.src/Fieldtypes/ — the Vue component and PHP fieldtype tell you exactly what shape gets saved (_type, _id, nested blocks) far faster than the README.resources/views/ — any shipped Blade/Antlers partials show the author's intended render loop.First use case: a "Page Sections" tab on a single collection. Don't retrofit an entire site. Add the Buildamic field to one blueprint (e.g. pages), define two or three blocks as fieldsets (hero, text_image, cta), and render them on one template. That gives you a working vertical slice and exposes any CP/UI breakage within an hour instead of a sprint.
Where to look when the CP misbehaves: open the browser console on the publish form. A broken builder UI is almost always a Vue asset mismatch, not a PHP error — the PHP side will happily save whatever the JS sends.
Fieldset-as-block is the whole mental model. Each block type is a Statamic fieldset; Buildamic stores an ordered array of { _type, _id, ...fields }. So your resources/fieldsets/ directory becomes the component library, and blueprints just reference it.
Render with a one-partial-per-block convention. Keep the loop dumb and push logic into the partial:
{{ buildamic }}
{{ if type == "hero" }}
{{ partial:blocks/hero }}
{{ elseif type == "text_image" }}
{{ partial:blocks/text_image }}
{{ /if }}
{{ /buildamic }}
Name partials to match the fieldset handle exactly and you can collapse that to {{ partial src="blocks/{type}" }} — which scales to dozens of blocks without touching the parent template again.
Wrap the loop once, reuse everywhere. Put the render loop in resources/views/partials/blocks.antlers.html and call it with a scope variable. Adding a fourth collection that uses page builder content then costs one blueprint field and zero template work.
Compose layout blocks for nesting. Sections → columns → content blocks is the pattern that keeps editors happy. Nesting a Buildamic field inside a block's fieldset works, but cap it at two levels; deep recursion makes both the CP and the render loop miserable to reason about.
Build block data programmatically for seeders and imports. Because the value is just an array, you can generate it in a migration or importer. Keep _type matching the fieldset handle and generate a stable _id per block so re-runs don't duplicate:
$blocks = collect($sections)->map(fn ($s) => [
'_id' => (string) Str::uuid(),
'_type' => $s['type'],
...$s['data'],
])->all();
Keep templates presentational. If you find yourself doing lookups inside block partials (resolving entry IDs, formatting money), add a small presenter or a Blade component per block and pass it the augmented block. That's also the seam that lets you unit-test block rendering without booting Statamic's tag parser.
Reuse fieldsets across contexts. The same fieldset can back a Buildamic block and a plain Replicator set. Define shared field groups as nested fieldsets rather than copy-pasting fields — renaming a field in one place is the difference between a five-minute change and a content migration.
"handmadeweb/buildamic": "dev-main#<commit-sha>" or an exact tag. With zero dependents there is no ecosystem pressure keeping releases stable; a composer update can move you onto an unreviewed commit. Better still, fork it into your VCS and require the fork._type. Rename it and existing content silently renders nothing (or throws). Add a data migration that rewrites stored _type values, or keep a legacy alias in your render partial.->augmented() / augment() explicitly and dump the type when a value looks like an ID instead of an object.{{ cache }} or a response cache) for content that changes rarely.{{ buildamic | dump }} (or ray()) shows you the true stored shape — array vs. Value object, whether _type is present, whether the block saved at all. Nine times out of ten "the block doesn't render" is a missing or mismatched _type, not a template bug.resources/views/vendor/buildamic so your customizations survive composer update. Same for any JS: build your own asset if you need CP changes.How can I help you explore Laravel packages today?