orchestra/asset
Asset management and pipeline component for Laravel: define named containers, register scripts and stylesheets with dependencies, then render combined, versioned output—keeping templates clean while cache busting and asset URLs stay automatic. Part of the Orchestra Platform ecosystem.
Asset::container('head')->styles() once; each module registers from its own boot(). That indirection is the product.@vite() renders compiled, hashed, tree-shaken app bundles; Orchestra Asset renders the runtime tail — vendor/CDN scripts with hard ordering constraints, per-tenant theme bundles, marketing tags, legacy pages that haven't migrated. That lets a Vite migration ship page-by-page instead of as a big-bang cutover. A PM decision to adopt this is really a decision to decouple two migrations.<script> tags scattered across Blade partials with implicit ordering assumptions, this package is a mechanical path to a two-render-point discipline (styles in <head>, scripts before </body>). That's a measurable reduction in "why did checkout break" incidents, and it's a much smaller scope than a full front-end refactor.Adopt when most of these hold:
Look elsewhere when:
integrity= attributes on third-party scripts, is a hard requirement. The rendered output is plain <link>/<script> strings; you'd need to post-process or extend the render path. Validate this in staging with production headers enforced — a Report-Only violation won't surface until it's a broken page.dump(Asset::container(...)->all()) when a tag goes missing).Non-negotiable conditions if you do adopt:
php artisan vendor:publish --tag=asset --force --no-interaction immediately after composer install), never at runtime from a controller or route — that's a write to the public directory under your process user.billing.app, reports.app, never bare app); container names may be shared, asset names must not be. Silent overwrite is the collision failure mode.RequestReceived listener) with an explicit flush, plus a soak test hitting the same worker twice with different tenant context and diffing the rendered <head>.To executives: This is a small, cheap dependency that unblocks a strategic capability — modules, plugins, and white-label tenants can ship their own CSS and JS into the product without us editing layouts for each one. It also lets us sequence the Vite migration page-by-page instead of as a risky big-bang cutover, because it handles the "runtime tail" (vendor scripts with hard ordering constraints, per-tenant themes, marketing tags) that the bundler doesn't want. The cost is genuinely small: a few hundred lines of container and ordering logic, framework-native API, minimal configuration. The honest caveat is supply-chain: the package has zero dependents and a single maintainer, so we should vendor or fork the core behind the same interface as a ~1-day insurance policy before rollout — at that size it's a composer.json edit to exit, not a rewrite. Net recommendation: adopt for the modular/theming use case, do not adopt as a bundler, and treat the Octane support claim as unproven until we soak-test it.
To engineering: The value proposition is narrow and real. You get named containers, coarse dependency ordering, versioned URLs, and Blade helpers — the parts of asset management that are tedious to write and easy to write badly. You do not get a build pipeline, CSP nonce support, or SRI attributes, and you should not ask it for those. The disciplines that make it safe: render from exactly two places (head for styles, end-of-body for scripts) and never inline mid-body; register in providers/controllers/composers and never in Blade; namespace asset names per module to avoid silent overwrites; put vendor:publish --tag=asset --force in the release script next to composer install; version containers from the git SHA, not the clock. It fails open — a typo yields a missing tag and no exception — so wire up a non-production listener that logs each container's resolved contents, and use assertSeeInOrder(['jquery.js','widget.js']) as the regression test for every ordering constraint you declare. Keep declarations coarse: ['jquery'] on each plugin is fine; a 30-node DAG is where ordering bugs live. If you need Octane, read the service provider for singleton container state first and confirm the flush hook exists — then soak-test with low max_requests so workers recycle. Finally, budget an afternoon to vendor the container/ordering core: we're the only users of this thing, so on-call for it is us either way, and a local copy makes the upgrade-or-abandon decision ours rather than the maintainer's.
How can I help you explore Laravel packages today?