Laravel Head: A New First-Party Package for Fluent HTML Head Management
Announced around Laracon US 2026, laravel/head is a new first-party package that gives Laravel a proper, fluent API for managing everything that lives in a document's <head>. Install it with:
composer require laravel/head
If you've built more than a couple of Laravel apps, you know the usual story with head management: a title tag hardcoded in the layout, a meta description that's right for the homepage and wrong for every other page, Open Graph tags copy-pasted into three different Blade partials, and a canonical URL that someone forgot to update when the routing changed. It's not hard, individually, but it's exactly the kind of scattered, easy-to-drift-out-of-sync problem that a first-party package is well suited to solve.
What it does
laravel/head covers the full surface area of what modern pages actually need in the <head>: title and meta description, Open Graph tags, canonical URLs, robots directives, theme colors, PWA and app metadata (web app manifest, Apple touch icons), performance hints (preload, prefetch, preconnect, dnsPrefetch), locale alternates, feed discovery links, and JSON-LD structured data builders for common schema types — articles, products, breadcrumbs, FAQs, and more. It also has a Head::twitter(...) method for Twitter/X card tags, which reuses the same title, description, and image data you've already set for Open Graph instead of making you specify it all twice.
Five layers of resolution
The interesting design decision is how it resolves head data when several places could plausibly set the same field. laravel/head merges five layers, from lowest to highest priority:
- Page defaults — registered once in a service provider via
Head::defaults(fn (HeadBuilder $head) => ...). - Route group metadata — set on a group of routes that share the same head configuration.
- Route metadata — set on an individual route via
->withHead(...). - Runtime metadata — set inside a controller or Livewire component via the
Headfacade, typically per-record data like a post title. - Error-page metadata — registered via
Head::errors(fn (ErrorPages $errors) => ...), applied when rendering 404/500/etc. pages.
Higher layers override lower ones field by field, not wholesale, so a route can override just the title while still inheriting the description and Open Graph image from the defaults.
A quick look at the API
Registering sensible defaults in a service provider:
use Laravel\Head\Facades\Head;
use Laravel\Head\HeadBuilder;
Head::defaults(function (HeadBuilder $head) {
$head->title('PhpArchitect')
->titleTemplate('%s — PhpArchitect')
->description('Practical PHP and Laravel tutorials, tips, and news.')
->themeColor('#1b1b1f')
->ogImage(asset('images/og-default.png'));
});
Setting metadata for a specific route without touching a controller:
Route::get('/pricing', PricingController::class)
->withHead(
title: 'Pricing',
description: 'Simple, transparent pricing for PhpArchitect subscriptions.'
);
And setting it at runtime, where you actually have the model in hand:
class PostController
{
public function show(Post $post)
{
Head::title($post->title)
->description($post->excerpt)
->ogImage($post->featured_image_url)
->article(fn ($article) => $article
->publishedTime($post->published_at)
->author($post->author->name));
return view('posts.show', compact('post'));
}
}
All of this renders through a single directive in your layout's <head>:
<head>
@head
</head>
Because resolution happens the same way regardless of what's rendering the page, the same @head directive works whether the page is plain Blade, a Livewire component, or an Inertia page.
The Inertia story
Inertia support deserves its own note, since a lot of Inertia apps already hand-roll their own <Head> component. laravel/head shares its resolved elements as a head prop on the Inertia page, rather than requiring a client-side head component at all. To use it, you need Inertia v3.5 or later with serverHead: true enabled. Each resolved element gets a stable data-inertia key, which is how Inertia knows to reconcile head tags across client-side visits instead of just appending duplicates.
If your app already has a custom <Head> implementation, that's the thing to check before adopting this — you'll want to either replace it outright or make sure the two don't fight over the same tags.
What to do next
This is a genuinely useful, low-risk addition to try. A good first step: pull laravel/head into a side project or a lower-traffic part of an existing app, set up defaults in a provider, and move a page or two's worth of hand-written meta tags over to it. If you're running Inertia, check your Inertia version first — you need 3.5+ for serverHead — and audit any existing custom <Head> component before turning this on, since you don't want two systems both trying to own the same tags.