symfony/asset
Symfony Asset Component generates URLs for web assets (CSS, JS, images) with built-in versioning for cache busting. Supports base paths, packages, and CDNs to keep asset links consistent across environments and deployments.
composer require symfony/asset
@inject('asset', 'Symfony\Component\Asset\Packages')
<link href="{{ $asset->getUrl('css/app.css') }}" rel="stylesheet">
// config/app.php
'providers' => [
Symfony\Component\Asset\AssetServiceProvider::class,
],
Then access via asset() helper (if using Symfony’s bridge):
<link href="{{ asset('css/app.css') }}" rel="stylesheet">
Enable versioning for a single file:
use Symfony\Component\Asset\PathPackage;
$package = new PathPackage(public_path(), true); // true = enable versioning
echo $package->getUrl('css/app.css');
// Output: /css/app.css?id=abc123 (or similar hash)
symfony/asset alongside laravel-mix or vite-laravel for hybrid versioning.dev environments by passing false to PathPackage.// app/Providers/AppServiceProvider.php
public function register()
{
$this->app->singleton('asset.package', function () {
return new PathPackage(public_path(), config('app.env') !== 'local');
});
}
Blade Usage:
@inject('asset', 'asset.package')
<link href="{{ $asset->getUrl('js/main.js') }}" rel="stylesheet">
$package = new PathPackage(public_path(), false, [
'base_urls' => ['https://cdn.example.com'],
'base_paths' => ['/themes/current'],
]);
echo $package->getUrl('css/styles.css');
// Output: https://cdn.example.com/themes/current/css/styles.css
Use JsonManifestVersionStrategy for Mix-manifest compatibility:
use Symfony\Component\Asset\JsonManifestVersionStrategy;
$strategy = new JsonManifestVersionStrategy(public_path('mix-manifest.json'));
$package = new PathPackage(public_path(), false, [], $strategy);
echo $package->getUrl('js/app.js');
// Output: /js/app.js?id=abc123 (from mix-manifest.json)
// app/Http/Middleware/AssetMiddleware.php
public function handle($request, Closure $next)
{
$response = $next($request);
$response->headers->set('Content-Type', 'text/html');
$dom = new \DOMDocument();
@$dom->loadHTML($response->getContent());
$xpath = new \DOMXPath($dom);
foreach ($xpath->query('//link[@href]') as $node) {
$href = $node->getAttribute('href');
$node->setAttribute('href', $this->app['asset.package']->getUrl($href));
}
$response->setContent($dom->saveHTML());
return $response;
}
JsonManifestVersionStrategy with Vite’s asset-manifest.json.Blade::directive('asset', function ($expression) {
return "<?php echo app('asset.package')->getUrl({$expression}); ?>";
});
Usage:
<link href="@asset('css/app.css')" rel="stylesheet">
Packages instance in Laravel’s cache:
$this->app->singleton('asset.package', function () {
return Cache::remember('asset.package', now()->addHours(1), function () {
return new PathPackage(public_path(), true);
});
});
Double Hashing with Mix:
PathPackage (with true for versioning) and Laravel Mix, assets may get duplicate hashes.JsonManifestVersionStrategy to defer to Mix’s hashes.Case-Sensitive Paths:
/CSS/App.css and /css/app.css are treated as different files.PathPackage or use strtolower() in URLs.Strict Mode Exceptions:
JsonManifestVersionStrategy throws exceptions if mix-manifest.json is missing in strict mode.strict: false in the strategy constructor.Base URL Conflicts:
https://example.com/js/app.js), conflicting with base_urls.JsonManifestVersionStrategy or manually strip protocols from Mix paths.$package = new PathPackage(public_path(), config('app.env') === 'production');
$url = $package->getUrl('css/app.css');
\Log::debug("Asset URL: {$url}");
mix-manifest.json or asset-manifest.json exists and is readable.Custom Version Strategies:
class CustomVersionStrategy implements VersionStrategyInterface
{
public function getVersion(string $path): string
{
return filemtime(public_path($path));
}
}
Usage:
$package = new PathPackage(public_path(), false, [], new CustomVersionStrategy());
Package Decorators:
Extend Packages to add logic (e.g., analytics tracking):
class AnalyticsPackage implements PackagesInterface
{
private $decorated;
public function __construct(PackagesInterface $decorated)
{
$this->decorated = $decorated;
}
public function getUrl($path)
{
$url = $this->decorated->getUrl($path);
// Add UTM parameters or other logic
return $url.'?utm_source=analytics';
}
}
Register as a decorator in Laravel’s service container.
Event Listeners:
Listen to AssetUrlGenerated events (if using Symfony’s event system) to modify URLs globally.
PathPackage uses filemtime() for versioning, which can be slow for many files.
filemtime results or use a database-backed version strategy.Packages instances (e.g., thousands of assets) may consume memory.
asset() helper to use symfony/asset:
// app/Helpers/AssetHelper.php
if (!function_exists('asset')) {
function asset($path)
{
return app('asset.package')->getUrl($path);
}
}
config/app.php:
'asset' => [
'versioning' => env('APP_ENV') !== 'local',
'base_url' => env('ASSET_BASE_URL', null),
],
Then use in AppServiceProvider:
$package = new PathPackage(
public_path(),
config('app.asset.versioning'),
['base_urls' => [config('app.asset.base_url')]]
);
How can I help you explore Laravel packages today?