michaelr0/buildamic
Buildamic is a Laravel package for composing pages from reusable, block-based content components. It offers a clean API for defining fields, rendering views, and managing structured content, making it simple to add flexible page-building capabilities to any Laravel application.
Buildamic is a Statamic 3 addon that adds a "page builder" fieldtype — a structured, drag-and-drop list of blocks (sets) that you define in your blueprints and render in your templates. It's aimed at sites where editors compose layouts from reusable sections rather than editing a single long Bard field.
Minimal setup:
composer require michaelr0/buildamic
php artisan vendor:publish --provider="Michaelr0\Buildamic\ServiceProvider"
Publishing typically gives you a config file (config/buildamic.php) and front-end assets (CSS/JS for the CP fieldtype). If the package ships compiled assets, check whether you need php artisan vendor:publish --tag=buildamic-assets or whether the CP loads them from the addon directly — this is the single most common first-install stumble.
Then add the fieldtype to a blueprint. In resources/blueprints/collections/pages/page.yaml, add a field with type: buildamic (confirm the exact handle in the addon's Fieldtypes directory — most page builders register a single fieldtype and you configure available blocks through the field's config or through a separate "sets" config):
fields:
-
handle: page_sections
field:
type: buildamic
display: Page Sections
sets:
hero:
display: Hero
fields: [...]
text_and_image:
display: Text & Image
fields: [...]
Where to look first, in order:
config/buildamic.php — this is where block/set definitions, view paths, and rendering options usually live.resources/views directory — this tells you the expected partial naming convention, which is what you'll be overriding.First use case: build a single "Flexible Content" field on your homepage, define two or three sets (hero, rich text, image + text), render them with a loop, and verify Live Preview updates. Once that round-trip works, scale the set library up.
Register blocks in config, not ad hoc in blueprints. The healthiest pattern with any Statamic page builder is a central registry: one config file (or one PHP file of set definitions) that every blueprint imports. This keeps your block library consistent across collections and makes it trivial to add a new block everywhere at once. Duplicating set definitions across five blueprints is the fastest way to an unmaintainable build.
Render with a set-to-partial mapping. The core rendering loop looks roughly like:
{{ page_sections }}
{{ if type == "hero" }}
{{ partial:blocks/hero }}
{{ elseif type == "text_and_image" }}
{{ partial:blocks/text_and_image }}
{{ /if }}
{{ /page_sections }}
Prefer a partial:blocks/{type} dynamic include if the addon exposes the block type as a variable, so adding a block is purely a matter of adding a partial — no template edits. This is the difference between a build that scales to 30 blocks and one that collapses at 12.
One partial per block, colocated with its styles. Keep resources/views/blocks/hero.antlers.html next to whatever CSS it needs, and give each block a scoped class. Page builders invite divergence: two blocks that look similar drift apart because nobody owns the shared component. Naming partials after the block handle (not after the visual design) keeps the mapping obvious.
Blueprint design beats template design. Spend your effort on making the editor's experience good: sensible field order, instructions on every ambiguous field, max_items on repeaters, and image fields constrained with container and max_files. A page builder is judged by editors, and the templates are the easy part.
Nest deliberately, and limit depth. Most page builders allow blocks inside blocks (e.g. a columns block containing content blocks). Depth 2 is usually enough; depth 3+ makes the CP unusable and the Antlers nested loops unreadable. If you need columns, consider a fixed column layout block with a single nested "content" region rather than unlimited recursion.
Integrate with the rest of Statamic rather than around it. Keep page builder output inside normal Antlers templates so Live Preview, the static cache, and multi-site all behave as expected. If blocks need data from entries or global sets, resolve it in the template or via an augmentation step — don't build a parallel content model.
Watch the save payload. Large pages with many blocks and nested sets can produce heavy YAML. Keep the field on the entry itself (not on a deeply nested grid), and avoid repeaters with dozens of rows inside each block.
Version and licensing constraints. Statamic 3 addons are sensitive to the Statamic minor version — check composer.json constraints before upgrading Statamic, and pin the addon until you've smoke-tested the CP. A page builder touches the CP deeply, so breakage shows up in the editor UI, not in errors.
Assets and the control panel. If blocks render oddly in the CP (no drag handles, no previews), it is almost always unpublished or stale front-end assets. Re-publish, clear caches (php artisan cache:clear, php artisan statamic:stache:clear), and hard-refresh the CP. If you have a build pipeline that copies assets, make sure the addon's assets aren't being clobbered by a mix/vite clean step.
Caching and page builders interact badly by default. Full-page static caching plus Live Preview plus a JS-heavy fieldtype is a classic source of "my edits don't appear." Confirm your cache invalidation runs on entry save, and that the static cache is bypassed for logged-in CP users. Also verify blocks don't output anything time-sensitive (dates, random IDs) in a way that gets baked into a cached page.
Empty and partial data. Editors will save a block with an empty image or an unset field. Guard in the partial — {{ if image }} around the whole wrapper, not just the <img> — otherwise you ship broken layout containers. This is the number-one source of visual bugs in page-builder sites.
YAML indentation and set handles. Set handles are your template contract. Renaming a set handle in the blueprint orphans existing content silently: the block still exists in the saved data but your if type == branch no longer matches, so it renders nothing. Treat handles as immutable; if you must change one, write a migration or one-off script to rewrite the stored data.
Extension points. The places you'll actually want to hook are: the set/block config (to add blocks), the view path (to override the addon's default partials without forking), and — if the addon exposes it — a block-rendering event or a custom fieldtype that extends the base one for specialized blocks. Prefer overriding views/config over editing vendor files; the package is small (11 stars), so expect infrequent releases and assume any vendor edit will be lost.
Debugging a block that renders nothing. Work outward: dump the raw field ({{ page_sections | dump }} or {{ dump }} in a debug route) to confirm data is saved; confirm the field handle matches the blueprint; confirm the set handle in the saved data matches your if type == string; then confirm the partial path resolves. Nine times out of ten it's a handle mismatch between blueprint, saved YAML, and template.
Consider whether you need it. At 11 stars and a single maintainer, this addon is a dependency risk. If your requirement is a handful of fixed, ordered sections, a replicator field with sets gives you 80% of the behavior with zero third-party exposure. Adopt Buildamic when you specifically want its editor UX (drag-and-drop blocks, block previews) — and budget time to fork it if the maintainer goes quiet.
How can I help you explore Laravel packages today?