Weave Code
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

Web Link Laravel Package

symfony/web-link

Symfony WebLink component helps manage link relationships between resources. Create and serialize HTTP Link headers for preload, prefetch, and resource hints (HTML5/Web standards), enabling better performance via HTTP/2 push and client hints.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Installation:

    composer require symfony/web-link
    

    No Laravel-specific dependencies; works with PHP 8.0+.

  2. Basic Usage: Inject a Link header for preloading a critical CSS file in a Laravel controller or middleware:

    use Symfony\Component\WebLink\Link;
    use Symfony\Component\WebLink\HttpHeaderSerializer;
    
    // In a controller or middleware
    $linkProvider = (new \Symfony\Component\WebLink\GenericLinkProvider())
        ->withLink(new Link('preload', '/css/main.css', ['as' => 'style']));
    
    return response()->header(
        'Link',
        (new HttpHeaderSerializer())->serialize($linkProvider->getLinks())
    );
    
  3. First Use Case: Preload critical assets for a homepage to improve Largest Contentful Paint (LCP):

    // app/Http/Middleware/PreloadCriticalAssets.php
    public function handle(Request $request, Closure $next)
    {
        $response = $next($request);
    
        $linkProvider = (new GenericLinkProvider())
            ->withLink(new Link('preload', asset('css/main.css'), ['as' => 'style']))
            ->withLink(new Link('preload', asset('fonts/roboto.woff2'), ['as' => 'font', 'crossorigin' => '']));
    
        return $response->header(
            'Link',
            (new HttpHeaderSerializer())->serialize($linkProvider->getLinks())
        );
    }
    

    Register the middleware in app/Http/Kernel.php:

    protected $middleware = [
        \App\Http\Middleware\PreloadCriticalAssets::class,
    ];
    
  4. Where to Look First:

    • Symfony WebLink Documentation for API reference.
    • Link class constants (e.g., Link::AS_STYLE, Link::AS_SCRIPT) for standardized attributes.
    • HttpHeaderSerializer for header formatting.

Implementation Patterns

Core Workflows

1. Route-Specific Preloading

Dynamically add preload headers based on the current route:

// app/Providers/RouteServiceProvider.php
public function boot()
{
    $router->preloadAssets(function (Request $request) {
        $links = [];

        if ($request->is('home')) {
            $links[] = new Link('preload', asset('css/home.css'), ['as' => 'style']);
        }

        if ($request->is('dashboard')) {
            $links[] = new Link('preload', asset('js/dashboard.js'), ['as' => 'script']);
        }

        return $links;
    });
}

Use middleware to inject these links:

// app/Http/Middleware/InjectRouteLinks.php
public function handle(Request $request, Closure $next)
{
    $links = app()->make('router')->preloadAssets($request);
    $linkProvider = (new GenericLinkProvider())->withLinks(...$links);

    return $next($request)->header(
        'Link',
        (new HttpHeaderSerializer())->serialize($linkProvider->getLinks())
    );
}

2. Asset-Based Preloading

Integrate with Laravel Mix/Vite to auto-generate preload links for compiled assets:

// app/Providers/AppServiceProvider.php
public function boot()
{
    if ($this->app->environment('production')) {
        $mixManifest = json_decode(file_get_contents(public_path('mix-manifest.json')), true);

        $links = collect($mixManifest)
            ->filter(fn ($path) => str_contains($path, '.css') || str_contains($path, '.js'))
            ->map(fn ($path) => new Link(
                'preload',
                asset($path),
                ['as' => str_contains($path, '.css') ? 'style' : 'script']
            ))
            ->toArray();

        $this->app->singleton('preload.link-provider', fn() => (new GenericLinkProvider())->withLinks(...$links));
    }
}

Inject in middleware:

return $response->header(
    'Link',
    (new HttpHeaderSerializer())->serialize(app('preload.link-provider')->getLinks())
);

3. API HATEOAS Links

Add navigation links to API responses:

// app/Http/Controllers/API/PostController.php
public function show(Post $post)
{
    $linkProvider = (new GenericLinkProvider())
        ->withLink(new Link('related', route('posts.index'), ['title' => 'All Posts']))
        ->withLink(new Link('next', route('posts.index', ['page' => 2]), ['title' => 'Next Page']));

    return response()->json($post)->header(
        'Link',
        (new HttpHeaderSerializer())->serialize($linkProvider->getLinks())
    );
}

4. PWA Manifest and Icons

Inject PWA-related links for offline support:

$linkProvider = (new GenericLinkProvider())
    ->withLink(new Link('manifest', asset('manifest.json')))
    ->withLink(new Link('apple-touch-icon', asset('icons/apple-touch-icon.png')))
    ->withLink(new Link('icon', asset('favicon.ico')));

Integration Tips

Laravel-Specific Patterns

  1. Service Container Binding: Bind the GenericLinkProvider to the container for reuse:

    $this->app->singleton(GenericLinkProvider::class, fn() => new GenericLinkProvider());
    

    Then inject it into controllers/middleware:

    public function __construct(private GenericLinkProvider $linkProvider) {}
    
  2. Response Macros: Extend Laravel’s Response class to simplify header injection:

    // app/Extensions/Response.php
    use Symfony\Component\WebLink\HttpHeaderSerializer;
    
    Response::macro('withLinks', function (array $links) {
        $linkProvider = (new GenericLinkProvider())->withLinks(...$links);
        return $this->header('Link', (new HttpHeaderSerializer())->serialize($linkProvider->getLinks()));
    });
    

    Usage:

    return response()->json($data)->withLinks([
        new Link('preload', asset('css/app.css'), ['as' => 'style']),
    ]);
    
  3. Caching Links: Cache precomputed links for static routes (e.g., homepage):

    $links = Cache::remember('preload.homepage.links', now()->addHours(1), function () {
        return [
            new Link('preload', asset('css/home.css'), ['as' => 'style']),
            new Link('preload', asset('fonts/roboto.woff2'), ['as' => 'font']),
        ];
    });
    
  4. Blade Directives: Create a Blade directive to output <link> tags for client-side preloading:

    // app/Providers/BladeServiceProvider.php
    Blade::directive('preload', function ($asset) {
        return "<?php echo '<link rel=\"preload\" href=\"'.$asset.'\" as=\"'.pathinfo($asset, PATHINFO_EXTENSION).'\" crossorigin=\"\">'; ?>";
    });
    

    Usage in Blade:

    @preload(mix('css/app.css'))
    

HTTP/2 Server Push

For HTTP/2 server push, configure your web server (e.g., Nginx, Apache) to respect Link headers:

# Nginx example
location / {
    add_header Link "</css/main.css>; rel=preload; as=style" always;
    # Or dynamically via Laravel:
    # proxy_set_header Link $http_link;
}

Note: Server push requires HTTPS and explicit configuration.


Gotchas and Tips

Pitfalls

  1. Header Injection Timing:

    • Issue: Injecting Link headers after the response is sent (e.g., in a finished middleware) will fail.
    • Fix: Inject headers in early middleware or directly in the response chain.
    • Example:
      // Wrong: Runs after response is sent
      $response->onFinish(function () use ($response) {
          $response->header('Link', '...'); // Too late!
      });
      
      // Correct: Runs during response generation
      return $response->header('Link', '...');
      
  2. Duplicate Links:

    • Issue: Multiple middleware/controllers may add duplicate Link headers, causing malformed headers.
    • Fix: Use a singleton GenericLinkProvider or merge links before serialization:
      $linkProvider = app(GenericLinkProvider::class);
      $linkProvider->withLink($newLink);
      $serializer = new HttpHeaderSerializer();
      $serializer->setLinks($linkProvider->getLinks()); // Overwrite duplicates
      
  3. Cross-Origin Restrictions:

    • Issue: Preloading cross-origin resources (e.g., fonts, scripts) may fail
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.
calmfox/watch-sylius
damienfern/grpc-symfony-bundle
atoolo/index-bundle
atoolo/genai-bundle
coprotoai/laravel-ticket
davidjln/llm-carbon-bundle
cryonighter/valid-request-bundle
coolms/taxonomy-bundle
coolms/field-bundle
articulate-orm/symfony
aaix/laravel-tall-architect
ephoto/akeneo-connector
emmanuelballery/eb-plantumlbundle
emielburgman/symfony-visitor-beacon
emielburgman/symfony-visit-storage
emielburgman/symfony-security-headers
emielburgman/symfony-log-viewer
emarref/xdebug-bundle
emarref/pubnub-bundle
elriseio/finance-money-bundle