Critical Asset Optimization
Who is this for? Frontend developers tuning Largest Contentful Paint (LCP) and page-load speed via critical asset registration, preload hints, and DNS prefetch.
TL;DR:
CriticalAssetRegistrylets you mark stylesheets and scripts as critical or deferred;AssetOptimizationMiddlewareinjects resource hints (<link rel="dns-prefetch">) into HTML responses to accelerate asset delivery.
When to use this
Section titled “When to use this”Register assets when:
- You have a critical stylesheet (e.g. theme CSS) that should be preloaded to reduce render-blocking delays
- You have third-party scripts or fonts that benefit from DNS prefetch hints
- You want to defer non-critical JavaScript loads (e.g. insights, tracking)
The registry pairs with the asset optimization middleware to inject browser hints that signal the browser to resolve DNS, establish connections, or preload resources before they’re explicitly requested in the HTML.
How it’s wired
Section titled “How it’s wired”CriticalAssetRegistry is registered as a singleton in the Laravel container under both CriticalAssetRegistry::class and the alias 'capell-frontend.critical-assets' (see packages/frontend/src/Providers/FrontendServiceProvider.php line 150–151).
AssetOptimizationMiddleware is not auto-applied to all responses. It is imported in the frontend service provider but registered only as a route middleware alias. You must explicitly apply it to routes where you want asset optimization hints injected.
To enable it on a route group:
Route::middleware(['frontend.asset-optimization'])->group(function () { // routes here will have asset hints injected});(Check your routes configuration for the exact alias name; the middleware class must be aliased in FrontendServiceProvider::registerMiddlewareAliases().)
Public API (CriticalAssetRegistry)
Section titled “Public API (CriticalAssetRegistry)”| Method | Returns | Purpose |
|---|---|---|
registerCritical(string $handle, string $url, string $type = 'css'): void | void | Register a critical asset (defaults to CSS) |
registerDeferred(string $handle, string $url, string $type = 'js'): void | void | Register a deferred asset (defaults to JavaScript) |
getCriticalAssets(): array | array | Get all registered critical assets as [handle => ['url' => ..., 'type' => ...]] |
getDeferredAssets(): array | array | Get all registered deferred assets with same structure |
criticalCss(): string | string | Render all critical CSS assets as HTML <link> tags |
deferredScripts(): string | string | Render all deferred JS assets as HTML <script defer> tags |
clear(): void | void | Clear both critical and deferred asset registries |
Rendering critical and deferred assets in templates
Section titled “Rendering critical and deferred assets in templates”The registry provides two helper methods to render registered assets directly in your Blade layout:
criticalCss() — Iterates over all critical assets, filters for type === 'css', and renders each as a <link rel="stylesheet"> tag. Call this in your layout’s <head> to include critical stylesheets:
{{ CriticalAssetRegistry::criticalCss() }}deferredScripts() — Iterates over all deferred assets, filters for type === 'js', and renders each as a <script defer> tag. Call this in your layout (typically before </body>) to load deferred scripts:
{{ CriticalAssetRegistry::deferredScripts() }}Both methods filter by type, so you can register multiple types (e.g. 'font', 'image') and only CSS and JS will be rendered. If you register other types, you must handle their rendering separately.
Allowed asset types
Section titled “Allowed asset types”The registry stores assets with a type field. Two type conventions are used:
'css'— CSS stylesheet (default forregisterCritical())'js'— JavaScript file (default forregisterDeferred())
The types are stored as plain strings and consumed by the helper methods criticalCss() and deferredScripts(), which filter by type. You can technically register other types (e.g. 'font', 'image'), but they will be ignored by the built-in render methods unless you add custom logic.
Example — registering assets
Section titled “Example — registering assets”<?php
declare(strict_types=1);
namespace App\Providers;
use Capell\Frontend\Support\Assets\CriticalAssetRegistry;use Illuminate\Support\ServiceProvider;
final class AppServiceProvider extends ServiceProvider{ public function boot(): void { CriticalAssetRegistry::registerCritical( handle: 'theme-stylesheet', url: '/css/theme.css', type: 'css', );
CriticalAssetRegistry::registerDeferred( handle: 'insights', url: 'https://example.com/insights.js', type: 'js', );
CriticalAssetRegistry::registerDeferred( handle: 'tracking', url: '/js/tracking.js', type: 'js', ); }}Since CriticalAssetRegistry uses static methods, you can register assets from any service provider’s boot() method. Always register in boot(), not register(), to ensure all packages and configuration have loaded.
Behavior (AssetOptimizationMiddleware)
Section titled “Behavior (AssetOptimizationMiddleware)”When a response passes through AssetOptimizationMiddleware:
-
Checks skip conditions: if the response is not HTTP 200 or does not have
Content-Type: text/html, the middleware passes the response through unchanged. -
Fetches context: reads the current
FrontendContextto access the theme. -
Injects DNS prefetch hints: if the theme has an
assetUrlattribute, injects<link rel="dns-prefetch" href="...">pointing to that domain into the</head>tag. -
Graceful fallback: if the frontend context is unavailable (exception caught), the middleware silently skips optimization and returns the response as-is.
The middleware injects DNS prefetch hints only. It does not render asset tags; preload hints for critical CSS and defer tags for scripts are handled by the registry’s criticalCss() and deferredScripts() methods, which you call in your Blade layout (see “Rendering critical and deferred assets in templates” above).
Gotchas
Section titled “Gotchas”-
Register in
boot(), notregister(): The registry must be populated before views are rendered, so all registrations must happen in the service provider’sboot()method. -
Middleware is opt-in: The asset optimization middleware is a route alias, not globally applied. You must explicitly attach it to route groups where you want resource hints injected.
-
Static methods:
CriticalAssetRegistryuses static methods and a static registry. This is not testable in isolation — clear the registry withCriticalAssetRegistry::clear()between tests if needed. -
Fonts and resource hints: If you register fonts with type
'font', the render methods will ignore them. You must handle font preload/crossorigin hints separately in your layout or extend the middleware to support font types.