Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Storia Bundle Laravel Package

iq2i/storia-bundle

A Symfony bundle for integrating Storia into PHP applications. It provides the service wiring and configuration needed to enable Storia features in a Symfony project, so you can install via Composer, register the bundle, and start using it without building custom integration code.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Package posture (updated for 0.6.0): iq2i/storia-bundle remains a single-vendor package from IQ2i with no visible downstream ecosystem — the 15 stars / 0 dependents signal is unchanged and still the binding constraint on adoption confidence. What 0.6.0 does change is the evidence base. This release ships a documentation site with per-page splits, a getting-started guide, a YAML reference, an explicit comparison with Storybook and Fractal, a documented compatibility policy, an upgrade guide, a public demo built from the test application, and CLI-driven snapshot testing. Four of the open questions from the prior assessment are now answerable from published material rather than vendor interrogation. The release cadence is also no longer unverifiable: 0.5.0 → 0.6.0 with a changelog, an upgrade guide, and a semver-adjacent posture.

Net: technical confidence up, commercial/ecosystem confidence unchanged, and a new risk class introduced (pre-1.0 API churn).

Architecture fit — the Symfony diagnosis is now confirmed, not inferred. The release notes settle it: kernel.debug, iq2i_storia.enabled config, a removed iq2i_storia_menu() Twig function, Twig components via symfony/ux-twig-component, and "the blocks of a component are printed as HTML and are no longer compiled as Twig." This is a Symfony bundle in the full sense — DI configuration, Twig templates, kernel environment semantics. There is still no Laravel analogue for any of it.

Two changes materially soften the architecture verdict relative to the last review:

  • symfony/form and symfony/ux-twig-component are now optional. "The bundle compiles without them and only the matching view types are unavailable." This removes two of the four constraint families I previously flagged as collision candidates and, more importantly, means the core catalog/render pipeline is not structurally welded to Symfony Forms. Our prior framing — "worst case is Twig templates + Form types" — is now partially defused: Forms are an optional view type, not a load-bearing layer.
  • Headless consumption now exists as a first-class mode. storia:export generates the catalog as a static website with its assets; storia:render renders a component from the CLI. Combined with storia:list for discovery, the bundle can now be driven as a build-time or out-of-process renderer rather than a runtime library. This is the single most consequential addition in the release from a TPM standpoint, because it provides a consumption path that never requires the package to coexist with Laravel's symfony/* pins.

Integration feasibility: materially improved, with a new best path. Three tiers now exist:

  1. Static/headless (new, lowest risk). Run the Symfony sidecar at build time, storia:export to static HTML + assets, consume the artifact. Laravel never loads the bundle.
  2. Service-layer extraction (option A, unchanged). Viable only if logic separates from Twig — still gated on the source audit, but the optional-Forms change makes separation more plausible than before.
  3. In-process Twig bridge (option C, unchanged). Still not recommended.

Note a pleasant constraint coincidence: Laravel already pins symfony/process, so invoking storia:render from a Laravel job/command via Process requires nothing new in the dependency graph.

Technical risk — reordered:

  • NEW, top risk: pre-1.0 API instability. 0.6.0 is a minor version carrying breaking changes: debug-gated enablement, removal of a public Twig function, a YAML variant options precedence inversion, and a change to how component blocks are compiled (HTML, no longer Twig). A project consuming this must expect breakage on every minor bump through 1.0. This is manageable via exact pinning plus the upgrade guide, but it kills any "just require ^0.6" instinct. Note the blocks change is a semantic change to rendering output, not just an API rename — it is exactly the class of change that silently alters what users see.
  • Composer collision — reduced but not eliminated. Forms and UX TwigComponent are out of the hard dependency set, which was two of my four flagged constraint families. symfony/framework-bundle, symfony/twig-bundle, and symfony/dependency-injection almost certainly remain. Gate 0 is still the right first check, but I would now expect it to fail less often — and the static export path means a failure is no longer fatal to the initiative.
  • Debug-gated default enablement is an operational footgun, not a technical blocker. "Enabled when kernel.debug is true" is a sensible DX default for a dev tool and a hazard for anyone deploying the catalog as a live sidecar in a non-debug Symfony environment — the UI silently vanishes unless iq2i_storia.enabled: true is set. Must be captured in deployment config for any non-production-only usage.
  • Security posture improved. Args and blocks now flow through the rendering context rather than the Twig source; data-provider values are never compiled as Twig; the Twig tab's absolute-template disclosure is scoped to the project directory; CSRF is disabled for form previews to keep snapshots stable. The "data provider returns a string, string gets compiled as Twig" injection class is closed. This is a genuine hardening release and raises my confidence in the vendor's engineering judgement.
  • Residual risks unchanged: single-vendor maintenance, Encore/Vite asset pipeline question (partially answered — storia:export emits assets with the site, so the export path sidesteps Vite integration), and hidden Symfony-runtime coupling.

Key questions — status after 0.6.0:

# Prior question Status
1 Twig/Form-coupled vs. plain PHP Partly answered. Forms + UX TwigComponent now optional; Twig itself remains core.
2 Exact symfony/* constraints, composer why-not Open. Gate 0.
3 Runtime dependency on framework-bundle Open, but narrowed.
4 Asset delivery (Encore/Vite/CDN) Partly answered — export bundles assets; live mode unclear.
5 Documented public API surface Answered. Docs site, YAML reference, per-page docs.
6 Maintainer cadence, support arrangement Partly answered. Cadence evidenced; commercial support still unconfirmed.
7 Storybook/CMS dimension Answered. Explicitly positioned against Storybook and Fractal — it's a component catalog / design-system tool, not a CMS.

New questions: 8. Does the compatibility policy commit to Symfony LTS windows, and what is the stated 1.0 timeline? A 1.0 date materially changes the pinning calculus. 9. Is storia:export output self-contained (relative asset paths, no CDN assumptions) enough to host behind our own auth/CDN? 10. What is the supported invocation contract for storia:render — stdin/stdout, exit codes, deterministic output? This determines whether it is safe to call from Laravel Process. 11. Can storia:test be pointed at externally-produced HTML (i.e., could it validate a Blade port against Symfony golden masters)?

Verdict (revised): Still not a drop-in Laravel dependency — the Twig/DI/kernel coupling is now confirmed rather than suspected. But 0.6.0 upgrades this from "design reference only" to credible build-time/out-of-process dependency, with a documented path, a public demo, a security hardening release, and a static export mode that structurally avoids the Composer risk. Recommendation moves from "evaluate as reference" to "spike the export path; treat as a tooling dependency rather than a library dependency."

Integration Approach

Stack fit. The fundamental mismatch is unchanged: Laravel's UI stack is Blade + Livewire/Inertia + Vite; this is Twig + Twig Components + Symfony DI + (optionally) Symfony Forms. Nothing in 0.6.0 bridges that. What changed is that the package no longer needs to fit the stack — storia:export produces a static site, and storia:render produces rendered output on demand. The integration seam has moved from "PHP library inside our app" to "build artifact or subprocess output." That is a far better seam for a TPM to own, because it is versioned, testable, and reversible.

Consumption options — four, ranked for 0.6.0:

Option Effort Risk When to choose
D. Build-time static export → host artifact (new) Low-Medium Low Default. Run Symfony sidecar in CI, storia:export, deploy static catalog; Laravel never resolves the bundle.
A. Extract services, rebuild UI in Blade Medium Low-Medium Only if the value is domain logic, not presentation. Cheaper now that Forms/UX are optional, but still needs the value audit.
B. Sidecar Symfony app, integrate over HTTP or Process Medium-High Medium If the catalog must run live/interactive (controls tab, live reload) and be embedded in our admin surface.
C. Twig bridge inside Laravel Low initial, high long-term High Still never recommended.

Option D is the headline change to this assessment. It preserves the vendor's value (component catalog, variant tabs, controls, snapshot tests) while eliminating the Composer collision question entirely, because the bundle is only ever installed in the sidecar's own composer.json. It also inherits storia:test snapshot coverage for free — if the export breaks on a version bump, CI tells us before we ship.

Option B now has a lighter variant worth naming: subprocess rendering. Laravel's symfony/process is already in the dependency tree. A queued job that shells out to bin/console storia:render --variant=X on a sidecar checkout, returning HTML on stdout, is operationally simple and keeps the bundle out of our runtime. It is slower and less ergonomic than in-process, and it deserves a hard look at failure semantics (cold start, timeouts, no session — note 0.6.0 specifically made storia:render session-free, which is what makes this viable at all).

Migration path. If adopting option D, the "migration" is really a CI pipeline change: sidecar repo pinned to an exact 0.6.x, a build step producing storia:export output, artifact upload, and a route or CDN mapping in Laravel. There is no framework migration and no data migration. If later pivoting to A or B, the export artifact serves as the reference implementation and the visual regression baseline.

If adopting A, 0.6.0 helps in one specific way: the blocks-as-HTML breaking change and the removal of iq2i_storia_menu() mean the customisation surface has shrunk, which reduces the number of Twig override points we would need to reproduce in Blade. Fewer knobs is good news for a port.

Compatibility. The documented compatibility policy is now the first artifact to read — it should answer whether constraints track Symfony LTS windows or float. Build the matrix anyway: PHP version, symfony/framework-bundle / twig-bundle / dependency-injection ranges, and whether symfony/form and symfony/ux-twig-component are satisfied by our versions if we ever want the form views. Asymmetric cost remains: we control a fork, we do not control upstream constraint bumps — and pre-1.0 breaking changes in minors make upstream bumps a recurring event rather than a rare one.

Sequencing (revised):

  1. Gate 0 — Composer spike (0.5 day). Still worth running for information, but it is no longer the gating decision, because option D does not require it to pass.
  2. Gate 0b — Export spike (1–2 days, NEW). Stand up a throwaway Symfony skeleton with the bundle at 0.6.x, point it at a representative slice of our design system, run storia:export, and host the output. Measure: does the output look like our components, is asset wiring self-contained, how long does the build take, does it survive a minor version bump.
  3. Gate 1 — Value audit (2–3 days). Component inventory mapping each feature to (a) plain PHP, (b) Twig template, (c) Symfony Form/DI coupling. Also answer the revised question set, particularly the storia:render contract and the 1.0 timeline.
  4. Gate 2 — Prototype one vertical slice (1 week). Only if pursuing A or B. For D, "one vertical slice" means one real component shipped through the export pipeline end-to-end.
  5. Decision, then pin exactly and vendor/fork only if we commit past 1.0 or need patches upstream will not take.

Do not composer require this into the Laravel application root as an experiment. With pre-1.0 breaking changes landing in minors and three symfony/* packages in the constraint set, the cost of a failed resolution attempt is low but the cost of a successful resolution that later pins us is high.

Operational Impact

Maintenance — still assume we own it, but the surface is smaller. 15 stars and 0 dependents still means no third-party triage, no security advisory channel beyond the vendor and GitHub, and no folk knowledge to draw on. However, 0.6.0 shifts the burden in two ways: (a) option D means we are pinned to a build artifact, not an API — when the vendor breaks something, our CI fails and we bump the pin deliberately rather than discovering it in production; (b) the published upgrade guide converts each minor bump from archaeology into a checklist. Budget one planned upgrade per minor release through 1.0, each requiring a re-run of storia:export and a visual diff of the catalog. That is a real recurring cost, and it is the price of adopting a 0.x tool.

Support. Unchanged in structure — GitHub issues with a small French agency, a possible commercial arrangement, or internal ownership — but the documentation release materially lowers the volume of support we will need. The docs site, YAML reference, getting-started guide, and public demo mean most questions are answerable without the vendor. The remaining unconfirmable item is a paid support line; for a 0.x dependency in a build pipeline, I would accept its absence, because the blast radius is a failed build rather than an outage.

Scaling. Improved. Option D moves all rendering cost to CI/build time — no runtime footprint, no cache pool to reconcile with Laravel's cache configuration, no DI container interaction. For option B, the caveats from the prior review stand: verify whether the bundle registers its own cache pools or non-default cache namespaces, and whether the dev-time live reload stream (now documented to close at 55 seconds with browser reconnect) is properly confined to the dev environment. That last detail is a small but useful tell: the vendor is actively managing a long-lived dev connection, which implies a dev server component that must not be enabled in production. The debug-gating default mostly handles this, but not if someone sets enabled: true to get the catalog in a staging environment.

Failure modes, reordered by likelihood × impact:

  • Breaking change on a minor bump breaks our export build — high likelihood (evidenced in 0.6.0 across four separate breaking changes), low-to-medium impact under option D (CI catches it, we pin and schedule), high impact under option A (API removal ripples through our extraction). Mitigation: exact pinning, upgrade-guide review per bump, snapshot diff on the exported catalog.
  • Catalog silently absent in a non-debug environment — medium likelihood, medium impact, entirely a config mistake. kernel.debug gating is a safe default for DX and a trap for deployment. Mitigation: explicit iq2i_storia.enabled in every non-local environment config, smoke test on the catalog route.
  • Composer resolution failure against Laravel — medium likelihood now (down from high), low impact under D (irrelevant), high impact under A/C (blocks).
  • Unmaintained upstream within 6–12 months — unchanged likelihood, high impact if we have adopted A; low-to-medium under D, because a stale export build still produces the same static artifact. This is the clearest argument for D over A.
  • Twig→Blade escaping/sanitisation divergence — unchanged under option A/C. Note the 0.6.0 security work (args/blocks via rendering context; data providers never compiled as Twig) hardens the source, not our port. Hand-porting still carries the XSS risk, and that risk lives in our code, not theirs.
  • Asset pipeline drift (Encore vs. Vite) — reduced. storia:export emits assets with the site, so the export path is self-contained; only live/sidecar mode retains this risk.
  • Hidden Symfony runtime coupling (session, security, request stack) — low-medium likelihood, high impact, still only flushable via Gate 2 or a real sidecar deployment. The session-free storia:render in 0.6.0 removes one instance of this.

Ramp-up. Unchanged for anyone reading or forking the source: Symfony bundle structure, DI/compiler passes, Twig inheritance and macros, and Symfony Forms remain prerequisites — realistically one to two weeks before a mid-level engineer is productive, longer before structural changes are safe. What 0.6.0 does shorten is the consumer ramp-up: the docs site, YAML reference, getting-started guide, public demo, and the AI-agent tooling (storia:list, storia:render, skills/ui-storia, Symfony Mate tools) mean a developer can produce a working catalog in hours without Symfony depth. That split matters for staffing: option D needs one person with modest Symfony literacy plus CI ownership; option A needs genuine Symfony fluency on the team.

Bottom line for the roadmap: 0.6.0 is a substantive release that improves the vendor's engineering credibility — documentation, compatibility policy, snapshot testing, a security fix, and a static export path that structurally sidesteps our main integration objection. The recommendation changes from "study it as a design reference" to "run a 1–2 day export spike (Gate 0b) as the default path, with a 0.5-day Composer spike for information." Do not adopt as an in-process Laravel dependency, and do not plan past 1.0 without a documented stability commitment. The absent dependents still argue that whatever value exists here must be extracted deliberately — but 0.6.0 finally gives us a low-risk, reversible way to extract it.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource