Frontend Asset Optimization
Who is this for? Frontend package developers tuning public CSS, JavaScript, preload hints, and DNS prefetch without leaking package/editor internals into cached HTML.
TL;DR: Package runtime assets are declared through
FrontendResourceContributorimplementations orFrontendResourceRegistrygroups.AssetOptimizationMiddlewareis available as the opt-infrontend.asset-optimizationroute middleware alias and injects safe response hints from the resolved public frontend context.
When To Use This
Section titled “When To Use This”Register assets when:
- a widget or public component needs CSS or JavaScript only when it appears on the page;
- a package needs to contribute runtime manifest hints;
- a theme asset host should be hinted with DNS prefetch;
- non-critical JavaScript should load lazily through the frontend asset manifest.
Do not add asset tags with render hooks when those tags belong to a reusable widget or block. Use render hooks for small public HTML fragments only.
How It Is Wired
Section titled “How It Is Wired”Packages have two supported asset paths:
FrontendResourceRegistrygroups for widget-scoped CSS and JavaScript.- Tagged
FrontendResourceContributorimplementations for lower-level runtime resource contributions.
AssetOptimizationMiddleware is registered as frontend.asset-optimization. It is not globally applied; attach it to the route group or route that should receive response hints:
Route::middleware(['frontend.asset-optimization'])->group(function (): void { // routes here can receive safe asset hints});Widget Resource Groups
Section titled “Widget Resource Groups”Use FrontendResourceRegistry when the asset belongs to a widget, block, or public component that declares its resource group.
<?php
declare(strict_types=1);
namespace App\Providers;
use Capell\LayoutBuilder\Data\LayoutWidgets\LayoutWidgetDefinitionData;use Capell\LayoutBuilder\Support\LayoutWidgets\LayoutWidgetRegistry;use Capell\Frontend\Data\Assets\FrontendResourceData;use Capell\Frontend\Data\Assets\FrontendResourceGroupData;use Capell\Frontend\Data\Assets\PublicResourceSourceData;use Capell\Frontend\Support\Assets\FrontendResourceRegistry;use Illuminate\Support\ServiceProvider;
final class FrontendServiceProvider extends ServiceProvider{ public function boot(FrontendResourceRegistry $resources, LayoutWidgetRegistry $widgets): void { $resources->register(new FrontendResourceGroupData( key: 'example.carousel', label: 'Carousel', package: 'example/carousel', resources: [ FrontendResourceData::style( handle: 'example/carousel:style', package: 'example/carousel', source: new PublicResourceSourceData('/vendor/example/carousel.css'), ), FrontendResourceData::classicScript( handle: 'example/carousel:runtime', package: 'example/carousel', source: new PublicResourceSourceData('/vendor/example/carousel.js'), ), ], ));
$widgets->registerDefinition(LayoutWidgetDefinitionData::frontendBlade( key: 'carousel', component: 'example::widgets.carousel', resourceGroups: ['example.carousel'], )); }}The public asset manifest exposes generated resource IDs. Keep package names, model IDs, field paths, signed URLs, and editor metadata out of rendered public HTML.
Asset Contributors
Section titled “Asset Contributors”Use FrontendResourceContributor::TAG when a package needs to contribute directly to the frontend resource plan instead of a widget resource group. Register contributors as tagged container services from the package provider.
AssetOptimizationMiddleware Behaviour
Section titled “AssetOptimizationMiddleware Behaviour”When a response passes through AssetOptimizationMiddleware:
- It only changes HTTP 200 HTML responses.
- It reads the current frontend context.
- It injects safe DNS prefetch hints for the resolved theme asset URL.
- It leaves the response unchanged if context is unavailable.
The middleware reads resolved public render/context state. It does not use static package state.
Public Media Defaults
Section titled “Public Media Defaults”Capell treats the page image as the conservative LCP candidate when that relation is already loaded. The frontend render data keeps the media’s canonical URL for identity and emits a responsive image preload when conversions or Media Library responsive sources are available. The preload uses the selected conversion’s MIME type. This lets the browser select the viewport-appropriate candidate while the rendered image still receives fetchpriority="high" and eager loading even when its fallback src uses a different conversion.
The shared media component defaults other images to native lazy loading. Pass loading="eager" explicitly for additional above-the-fold media that is not the page image. The logo component does this automatically. Video preload behaviour is unchanged.
Local theme fonts generated by the public head use font-display: swap. URL-based font stylesheets remain controlled by their provider.