Filament Atelier
A visual page builder for Laravel, shipped as a Filament plugin. Developers define blocks in code, clients arrange them and watch the real page update as they type.
- Role
- Architecture, build and documentation
- Timeline
- 14 to 16 August 2026, spec to published package
- Year
- 2026
- Status
- In Progress
Atelier turns a Laravel app into a CMS a non-technical client can actually use, without giving up the performance and SEO of a hand-coded site.
A page is stored as a JSON tree of typed blocks and rendered by Blade at request time. A block type is one PHP class plus one Blade view; the class returns a Filament schema, which becomes its settings form. The client opens a full-screen builder, picks sections from a list, fills them in, and watches the real page change as they type. The public site is server-rendered, bilingual in English and Arabic, and drafts cannot leak.
Published as safi/filament-atelier on Packagist at v0.1.1. Nine blocks ship, several with image uploads and reorderable repeaters.
Every existing option forced a trade nobody wanted to make. Bespoke code per site means a developer for every copy change. WordPress with Elementor means leaving the stack and accepting the performance. Existing Laravel block builders give you a stack of collapsible forms, which tells a client nothing about whether their headline wraps onto three lines.
The specific problem was the preview. A preview you open when you are finished is a second browser tab, which is what clients already do. A preview that updates while you type is only worth having if it is the real page, because the moment a client finds one thing that looks different from the live site, they stop trusting it and open that second tab again.
The second problem was control. A permissive editor lets a client break the design. The fix had to come from the architecture rather than from locking things down case by case.
One render path. The preview iframe points at a signed, noindex route that renders the draft tree through the public layout and the public stylesheet. Same Blade views, same CSS, different data source. A second rendering path for the editor would make the preview an approximation, and an approximation is what everyone already has.
Refresh by swapping the canvas. On change, Livewire persists the draft and dispatches an event; the editor fetches the preview and replaces the contents of one container inside the iframe. The document never reloads, so scroll position survives and the stylesheet never re-fetches. A twelve-section render measures 16ms, so full-page rendering was never the bottleneck the plan assumed it would be.
Gutenberg's data model without its storage model. Storing structured JSON instead of rendered HTML removes four subsystems WordPress had to build: attribute sourcing, block validation, deprecation chains, and the pure-save constraint. Changing a block's shape becomes an ordinary data migration.
Control by construction. The block author writes the schema, so a client can only arrange and fill what exists. There is nothing to lock down.
Two content columns. Editing writes a draft; publishing copies it to a separate column the public route reads. An unpublished page 404s rather than rendering.
One tree, two languages. Translatable attributes hold a per-locale map inside the same block, so English and Arabic share one structure and one section order, mirrored with dir="rtl" and logical properties.
About this project
-
The developer model is deliberately close to Fabricator's: a block type is a PHP class plus a Blade view, registered at boot. What differs is the editing surface. Both Filament's Builder field and Fabricator give the client a vertical stack of collapsible forms, so on a twelve-section page you scroll through accordions trying to remember which hero is the one near the pricing table, and nothing tells you how the page will actually look. Atelier replaces that with a full-screen editor: a section list on one side, the settings for the selected section, and the real page rendering in the middle at desktop, tablet or mobile width. The preview is not a mock of the front end, it is the front end, loaded through the same layout and stylesheet the public route uses.
-
Because storing rendered HTML as the source of truth is what forces Gutenberg to build attribute sourcing, block validation with its modified-externally errors, runtime deprecation chains, and the constraint that a block's save function must be pure. All four exist to solve the problem of getting structured data back out of markup and keeping that markup consistent across changes. Storing the tree as JSON and rendering with Blade at request time removes the problem rather than solving it: every attribute is a field, there is no stored markup to diff, and changing a block's attribute shape is an ordinary data migration.
-
No, and that is the point. Block types are code, so the client can arrange and fill sections but cannot invent them, which is what keeps a site on-design after handover. Authoring block types from the panel is researched and specified as a later phase, and it carries real problems worth respecting: user-supplied templates must never be compiled as Blade, because that turns a textarea into remote code execution, and Tailwind cannot see classes stored in a database, so pasted utility classes generate no CSS. For the one-off case there is a raw HTML block, which covers the need without any of that.
-
Translatable attributes hold a map keyed by locale inside the same block, so a heading is stored as one field containing both the English and the Arabic value. One tree, one section order, translated text. Arabic is mirrored with a direction attribute and CSS logical properties rather than by reordering blocks, and a missing translation falls back to the default locale so a half-translated page reads as untranslated rather than broken. The accepted cost is that Arabic cannot have a different section order from English, which for a marketing site is the right trade and stops the two languages drifting into different pages.
-
Yes, and it is the constraint the whole architecture is built around. The public route reads the published block tree and renders one Blade view per block, so the full content is present in the initial HTML response and the page works with JavaScript disabled. Nothing is injected client-side. The editor canvas is an editing tool that happens to render the same views; it never becomes a second rendering path. Per-locale meta, canonical URLs, hreflang pointing both ways, and Open Graph tags are generated from the page record.