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

Getting Started

Framework reality check first: this is a Symfony bundle, not a Laravel package. There is no service provider, no config/storia.php to publish, no Facade. The Laravel equivalents map like this:

Laravel Symfony / this bundle
config/*.php + vendor:publish config/packages/iq2i_storia.yaml (Flex recipe)
Service provider register() StoriaBundle in config/bundles.php
php artisan vendor:publish bin/console config:dump-reference iq2i_storia
routes/web.php bundle-provided routing, import it yourself

Minimal path:

composer require --dev iq2i/storia-bundle

Symfony Flex should register it under the dev key in config/bundles.php. If not:

// config/bundles.php
return [
    // ...
    IQ2i\StoriaBundle\StoriaBundle::class => ['dev' => true, 'test' => true],
];

As of 0.6.0 the UI boots whenever kernel.debug is true — you don't need any YAML to see the sidebar in dev. Only add config if you want it somewhere debug is off (e.g. a staging env):

# config/packages/iq2i_storia.yaml
when@dev:
    iq2i_storia:
        enabled: true   # only needed on envs where kernel.debug is false

Confirm what's actually mounted before writing anything:

bin/console config:dump-reference iq2i_storia
bin/console debug:router | grep -i storia
bin/console lista storia          # 0.6+: storia:list, storia:render, storia:test, storia:export

Where to look first, in order:

  1. https://ui-storia.com/docs/ — 0.6 split the docs into pages (getting started, YAML reference, compatibility policy) and publishes a demo. Start here now, not the README.
  2. src/DependencyInjection/Configuration.php — the actual config schema. Docs drift, the tree node does not.
  3. config/routes/ / Resources/config/routes.yaml — real route names for Twig path() calls.
  4. skills/ui-storia/ (0.6+) — the agent skill doubles as an executable "how to use this" spec; it exercises storia:list, storia:render, storia:test end-to-end.
  5. tests/ — still the most honest documentation of intended usage.
  6. storia/data/ — 0.6 shared data files; if you're inheriting a project, this tells you how fixtures were wired.

First use case: pick one existing Twig component (a button, a card, a form partial), write a single story definition, boot in dev, hit the Storia route. Even better, verify from the CLI first:

bin/console storia:list                       # confirm the variant is discovered
bin/console storia:render Button default      # confirm it renders

Then open the browser and use the Controls tab (new in 0.6) to tweak args live. That one loop — story file → discovery → render → tweak → snapshot — teaches you 80% of the bundle. Only then scale to a component inventory.

Implementation Patterns

Story-per-component, colocated or centralized. Pick one convention on day one and enforce it. The two shapes:

  • Colocated: templates/components/Button.html.twig + Button.stories.yaml|php — great for ownership, awkward when the story needs PHP data.
  • Centralized: storia/ mirroring your component tree — easier to review in PRs. In 0.6 the docs explicitly recommend treating storia/ as code (it now holds data files too, not just stories).

Shared data in storia/data (0.6). This is the single biggest day-to-day win. Stop hard-coding fixture arrays into each story:

# storia/data/products.yaml
- !lorem title 6 10
- name: !data App\Storia\Fixtures::product

Reference them with !data, generate with !repeat / !lorem. PHP providers use !data Class::method — note that bare Class::method is deprecated in 0.6, always prefix with !data.

Variants over one-off stories. Model states as a list — default, disabled, loading, error — rather than duplicating files. The variant name is now the tab label (documented behavior change in 0.6), so name them like a UI state, not like a filename.

Controls tab. Added in 0.6, it derives editable arguments from the component props, its PHP class, or {% types %} on a page. Use it during design reviews and for writing bug reports ("open Controls, set size: lg, hit reload") — it's faster than a screen share.

Variants as tests — wire this on day one. storia:test renders every variant and diffs against an HTML snapshot. For PHPUnit:

use IQ2i\StoriaBundle\Test\StoriaVariants;
use IQ2i\StoriaBundle\Test\StoriaTestCaseTrait;

class StoriaSnapshotTest extends \PHPUnit\Framework\TestCase
{
    use StoriaTestCaseTrait;

    public function testCatalog(): void
    {
        (new StoriaVariants())->assertAllMatch($this);
    }
}

This is the feature that converts Storia from "nice demo" into a regression net. Run storia:test in CI and commit snapshots — the diff on a PR is the design review.

Static export for previews. bin/console storia:export produces a static site (HTML + assets). Two uses: (a) publish to a PR-preview subdomain / GitHub Pages so non-engineers can browse without running Symfony, (b) attach as a CI artifact on every UI-touching build. No PHP runtime in prod, no route exposure risk.

AI-agent friendly. 0.6 ships storia:list and storia:render as first-class commands, plus skills/ui-storia and Symfony Mate tools (storia-list, storia-render, storia-test). If you use Claude/Codex-type agents, point them at the skill and they can inspect and render components without opening a browser. Handy for "which variants exist and what does disabled render as?" questions.

Wire real assets, not placeholders. Point the preview at your actual Webpack Encore / AssetMapper entrypoints. A component library preview rendered with no CSS is a demo, not a tool. Verify storia:export output too — the static site bundles assets, so a missing manifest.json will be obvious there.

Gate it appropriately. With kernel.debug driving enablement you mostly get this for free, but staging-without-debug needs an explicit enabled: true, and you should still assert it never appears in prod:

# config/routes/dev/storia.yaml — only if the recipe route is unconditional
storia:
    resource: '@StoriaBundle/Resources/config/routes.yaml'
    prefix: /_storia

Form previews changed semantics in 0.6. A YAML variant's options now override the global form options (previously the global won for automatic variants). If you have custom form stories, re-check them — this is a silent behavioral flip, not an error. Also, CSRF is now disabled for form previews so snapshots are stable and storia:render doesn't need a session — don't write assertions that assume a CSRF token exists.

Put it in review workflow. Link the preview URL (or the storia:export artifact) in PR descriptions for UI-touching changes; run storia:test in CI; let storia:export produce a browsable catalog for stakeholders.

If you're actually a Laravel shop: don't force this into Laravel. Two sane options — (a) stand up a throwaway Symfony skeleton app purely as a component preview harness (storia:export makes this cheap — you don't need a running app for consumers), or (b) read the source as a design reference and reproduce the discovery/render/export loop in Blade + a /dev/components route. Option (b) is usually less work than people assume, and 0.6's split docs + static export make it easier to copy the model without adopting the runtime.

Gotchas and Tips

0.6 breaking changes — the checklist before upgrading from 0.5:

  • UI enablement moved from explicit config to kernel.debug. Any non-debug env (staging, a demo host) that previously served the UI now won't unless you set iq2i_storia.enabled: true.
  • iq2i_storia_menu() was removed and the sidebar is built by the bundle. Delete any override template calling it.
  • Form variant YAML options now override globals (reversal from 0.5). Re-verify every form story.
  • Component blocks are printed as HTML and no longer compiled as Twig. If a 0.5 story put Twig in a block (e.g. {{ ... }} in a slot), it will now be output verbatim. Move logic to args or a data provider. This is a deliberate security change — args and blocks go through the rendering context, and values from data providers are never compiled.

Follow the upgrade guide at https://ui-storia.com/docs/upgrade/ and read composer show -a iq2i/storia-bundle for the tag history.

Deprecations to fix now: bare Class::method strings for data providers are deprecated — always !data Class::method. Grep your storia/ tree for un-prefixed strings once.

Optional deps: symfony/form and symfony/ux-twig-component are no longer required. The bundle compiles without them, but the matching view types simply won't be available. If form previews or TwigComponent discovery are missing, check composer show | grep -E 'symfony/(form|ux-twig-component)' before assuming a bug.

Read Configuration.php, not the README. On a small bundle the config tree is authoritative. bin/console config:dump-reference iq2i_storia costs two seconds. The 0.6 YAML reference in the docs is much better than the 0.5 README, but the tree node still wins.

Story discovery is cached. After creating a story, if it doesn't appear, bin/console cache:clear before suspecting your path config. Custom discovery services carry kernel.reset inconsistently.

Live reload closing after ~55s is intentional. The 0.6 stream closes and the browser reconnects. Don't chase it as a bug; if you have a very long-lived preview session it will look like a hiccup.

The Twig tab only shows project templates. 0.6 limits absolute template paths to the project directory — vendor templates are hidden. Expected, not a regression.

Asset 404s are a build step, not a bundle bug. Encore manifests are per-environment. Check public/build/manifest.json exists for the env, and — for static export — verify storia:export was run after yarn build / asset-map:compile.

Never let the preview route reach prod. bin/console debug:router --env=prod | grep -i storia in CI. With kernel.debug defaulting off in prod this is mostly handled, but staging hosts and custom deploys can flip debug on.

Snapshot tests need a stable environment. storia:test compares HTML, so pin the PHP version, lockfile, and assets in CI. Form previews no longer include a CSRF token (0.6) which removes one source of flake, but dates from !data providers still need to be deterministic — the docs' !lorem/!data fixtures are examples, not guaranteed-stable values. Seed or freeze anything time-dependent.

Template overrides beat forking. Standard Symfony bundle convention: drop replacements in templates/bundles/StoriaBundle/. For behavior, decorate services in services.yaml. The 0.6 command surface (storia:list|render|test|export) is your real integration point — prefer scripting those over patching internals.

Extension points worth knowing: custom story discovery/loading service, the data-provider mechanism (!data Class::method — any class with a public method works, so this is effectively a DI-friendly fixture hook), the preview controller (for auth or an env banner), and the new CLI commands. Scan src/DependencyInjection/ for service IDs you'd tag or decorate — that list is your real customization surface.

Version pinning — still pre-1.0, but maturing. 0.6 ships a compatibility policy and a proper docs site, so upgrades from here should be less archaeological. Still: commit the lockfile, read the diff, and keep story definitions boring (data + template reference) so rendering-layer churn on the bundle side doesn't cascade into your files. 0.6 is a good baseline to pin to — it fixes the "which variant exists?" CLI gap with storia:list, which is the first thing you'll want in a busy repo.

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.
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
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect