onelegstudios/shape
Shape offers a simple, expressive way to define and transform structured data in Laravel, so you can build consistent arrays, DTOs, and API payloads with less boilerplate and clearer intent. It helps keep validation, casting, and output formatting predictable across your app.
shape:eject copies a component and its dependency graph into your codebase, records what the package held at copy time, and shape:eject:status distinguishes "you edited this" from "the package changed underneath you." The build-vs-buy decision is therefore reversible component-by-component, not all-or-nothing. This materially lowers the cost of saying yes now.<dialog>; dropdown, popover and tooltip use the popover attribute. Focus trapping, top layer, Escape, light dismiss and the scrim come from the browser. This is a defensible bet that reduces our own JS surface, but it is a bet with a browser-baseline requirement that should be checked against the support matrix we actually promise.Shape::toast() and Shape::confirm() dispatch through Livewire when a request is available and flash to session otherwise, arriving as the same event so one code path builds the toast either way — Livewire is not a dependency. That means feedback UX is standardized early, before three teams each invent a flash-message pattern.shape:icon generates components from SVG sets (Heroicons, Lucide, Tabler, Phosphor, Bootstrap Icons, Remix Icon, Material Symbols, or any directory), writing the resolved upstream commit into every file, with shape:icon:status reporting what has been redrawn upstream. shape:icon:replace --set=lucide redraws the library in another set without touching a call site. That makes "switch icon family" a one-command decision instead of a refactor, and gives an auditable answer to "which upstream version are we shipping?"Consider it when most of the following hold:
variant) and semantics (tone) as separate props — so a destructive action can stay quiet until it matters — and every default carries zero specificity so application classes win without !important. That is a library designed to be overridden, which suits product teams and frustrates teams wanting a fully finished visual identity.<dialog> and the popover attribute. Verify this against your real support commitments, including any enterprise or embedded-webview audience, before adoption — this is the single most consequential technical assumption in the package.shape:eject, shape:eject:all and shape:eject:status make partial or full in-housing a supported workflow with change tracking, not a fork.shape:doctor exits non-zero and checks ejected components for request-scoped state (auth(), session(), config(), translations) that a folded component resolves once and then serves to everybody — a failure with no stack trace and no symptom in development. If you plan to use Blaze, this check is close to mandatory and its existence is a genuine adoption argument.Look elsewhere when:
For executives — one paragraph. Shape is a free, open-source Blade component library that gives a Laravel/Livewire product its entire UI kit — buttons, forms, tables, modals, toasts and the rest of thirty components — in a single composer require. The commercial case is time-to-market: it removes the undifferentiated build-out of a design system that would otherwise sit in front of our first release, and it removes it reversibly. Any component can be copied into our codebase on demand, with tracking that tells us whether we or the upstream package changed a file, so adopting it does not lock us in. Theming is configuration, including derived brand palettes for white-label work. The costs are real but bounded: it is a 0.x pre-release, so component APIs may shift; it requires a modern stack (Laravel 12/13, PHP 8.3+) and a modern browser baseline; and it commits us to Tailwind and Livewire on the front end. Recommended posture: adopt behind the eject escape hatch in a contained area, pin the version, and treat "reach 1.0 or we take ownership of the components we use" as an explicit decision point rather than an open question.
For engineering — the honest summary. Anonymous Blade files, Tailwind-styled, annotated for Blaze compile-time folding; Blaze is optional and annotations compile away without it. No compiled CSS and no asset build step — the trade-off is that our Tailwind content configuration must include the package's templates, which needs verifying in our pipeline before we commit. Overlays sit on native <dialog> and popover, so focus trap, top layer, Escape, light dismiss and scrim are the browser's, not ours; the remaining script is one dependency-free file — no Alpine, no Livewire. Feedback dispatch rides a Livewire request when one exists and flashes to session otherwise, arriving as the same event with a single code path, and Livewire is not a dependency of the package. shape:install publishes tokens, the script and a chosen set. shape:doctor exits non-zero and catches request-scoped state in folded components — the silent, untraceable bug class — so it belongs in CI from day one if Blaze is on. Icon components are generated from named SVG sets with the resolved upstream commit written into every file, and shape:icon:replace --set=… swaps the whole family without a call site changing. The testing story is above average for a 0.x: Ubuntu and Windows across the PHP 8.3/8.4/8.5 and Laravel 12/13 matrix, on both prefer-stable and prefer-lowest, plus a test asserting every documentation example compiles. What we do not get: a 1.0 API guarantee, a published support policy, or component coverage for rich inputs and data grids — scope those separately.
The one-line version. Thirty Tailwind/Blade components, browser-native overlays, zero JS dependencies and a real exit door — at the price of 0.x API churn and a modern-browser, modern-Laravel baseline.
How can I help you explore Laravel packages today?