Laravel Head: Manage Meta Tags, Open Graph, and JSON-LD in Laravel 13
Laravel Composer Pacakge #Laravel #SEO #Open Graph #JSON-LD #Meta Tags #Laravel 13

Laravel Head: Manage Meta Tags, Open Graph, and JSON-LD in Laravel 13

4 min read Mohamed Said Mohamed Said

What Is Laravel Head?

Laravel Head is a first-party package from the Laravel open-source team, introduced during Taylor Otwell's day-one keynote at Laracon US 2026. It provides a fluent API for everything that belongs in your HTML <head> element: titles, meta descriptions, canonical URLs, Open Graph tags, robots directives, JSON-LD structured data, and resource hints. Metadata is resolved per request and works across Blade, Livewire, and Inertia applications.

Five-Layer Metadata Precedence

One of the package's core design decisions is a five-layer precedence system. From lowest to highest priority:

  1. Page defaults
  2. Route group metadata
  3. Route metadata
  4. Runtime metadata
  5. Error metadata

Higher layers override lower layers one field at a time. A runtime title replaces a route title without affecting the route's description. Site-wide defaults are registered in a service provider:

use Laravel\Head\Enums\OgType;
use Laravel\Head\Facades\Head;
use Laravel\Head\HeadBuilder;

Head::defaults(function (HeadBuilder $head) {
    $head
        ->title('Laravel', suffix: ' - Laravel')
        ->description('Build something great.')
        ->canonical()
        ->og(siteName: 'Laravel', type: OgType::Website)
        ->searchableByRobots()
        ->preconnect('https://fonts.example.com');
});

The suffix registered in defaults carries into higher layers automatically, so Head::title('About') renders About - Laravel.

Route-Level Metadata

For pages with metadata known at definition time, withHead() attaches it directly to the route:

Route::view('/contact', 'contact')
    ->name('contact')
    ->withHead(
        title: 'Contact Us',
        description: 'Get in touch.',
    );

It also works on route groups, resources, and singletons. Under the hood it writes plain arrays through Laravel's native route metadata API, so route caching remains fully compatible.

For data only available at request time, use the facade in your controller:

public function show(Post $post)
{
    Head::title($post->title)
        ->description($post->description)
        ->when($post->isDraft(), fn ($head) => $head->hiddenFromRobots());

    return view('posts.show', ['post' => $post]);
}

Open Graph, Twitter Cards, and JSON-LD

Document title and description automatically fill in missing og:title and og:description. Register a Twitter card type in your defaults and the card tags are derived from the same values — no duplication required.

Structured data uses a separate Schema facade with nestable builders:

use Laravel\Head\Enums\OfferAvailability;
use Laravel\Head\Facades\Schema;

Head::schema(
    Schema::product()
        ->name($product->name)
        ->offers(
            Schema::offer()
                ->price($product->price)
                ->currency('USD')
                ->availability(OfferAvailability::InStock)
        )
);

Built-in schema types include article, blogPosting, product, offer, brand, breadcrumbs, faq, organization, person, webPage, and webSite. Custom types can be registered with a #[SchemaType] attribute. Invalid JSON-LD throws an exception outside production and logs a warning in production.

Rendering in Blade, Livewire, and Inertia

Blade and Livewire both use a single @head directive in the layout:

<head>
    <meta charset="utf-8">
    @head
</head>

For Inertia, the package shares resolved head tags as an array of rendered element strings under a head prop on every page object, compatible with Inertia 3.5 and later. Each element carries a stable data-inertia key that Inertia keeps synchronized across visits and back/forward navigation. Because tags appear in the initial HTML response, crawlers and link-preview bots read them without executing JavaScript.

Installation

Laravel Head requires PHP 8.3 and Laravel 13.17 or later:

composer require laravel/head

Register defaults in a service provider and add @head to your layout. Call Head::toArray() if you need the resolved metadata as a structured array rather than rendered markup.

Key Takeaways

  • Five-layer precedence merges metadata field by field, so higher layers never accidentally wipe lower-layer values.
  • withHead() on routes and groups is cache-compatible — no route caching workarounds needed.
  • Open Graph, Twitter cards, and JSON-LD schemas share the same title and description source, reducing repetition.
  • Inertia support requires no client-side <Head> component; tags are server-rendered and synchronized automatically.
  • Invalid JSON-LD surfaces as an exception in development, keeping structured data errors visible early.
  • Requires PHP 8.3 and Laravel 13.17+.

Source: Laravel Head: Manage Meta Tags, Open Graph, and JSON-LD — Laravel News

Found this useful?

Frequently Asked Questions

3 questions
Q01 What are the minimum requirements for the Laravel Head package?
Laravel Head requires PHP 8.3 and Laravel 13.17 or later. Install it via Composer with `composer require laravel/head`.
Q02 Does Laravel Head work with Inertia.js, and does it require a client-side Head component?
Yes. When Inertia is installed, the package shares resolved head tags as rendered element strings under a `head` prop on every page object, compatible with Inertia 3.5 and later. No client-side `<Head>` component is needed because tags are included in the initial HTML response and synchronized automatically across visits.
Q03 How does the five-layer metadata precedence work in Laravel Head?
Metadata is resolved from five layers in order: page defaults, route group metadata, route metadata, runtime metadata, and error metadata. Higher layers override lower layers one field at a time, so setting a runtime title replaces the route title without affecting the route's description or other fields.

Continue reading

More Articles

View all