Introducing Laravel SEO

Samuel Štancl

Samuel Štancl


Published on

Today we have released the first version of our SEO package.

The package provides an expressive API for adding meta tags relevant for SEO.

Out of the box, the package manages your <title>, OpenGraph tags, and — optionally — Twitter card tags.

OpenGraph is considered a core feature, whereas Twitter is considered an extension.

The package also provides an API for registering custom extensions, similar to the Twitter extension.

And as an extra feature, the package can generate cover images using Flipp.

What the package solves

SEO meta tags are very simple bits of HTML, but managing them from your business logic can be a bit painful.

For instance, title suffixes. Odds are, you've written — multiple times — code that goes like "if title is set, echo title + suffix, otherwise echo the default title".

Then, you need a way to pass the title to the layout. The most basic — and generally efficient — way of doing this is to literally pass the values to the title component in your Blade views.

<x-layout :title="$post->title">

But that breaks down when you want to set titles from controllers, and it makes your views unnecessarily repetitive and concerned with things unrelated to them. SEO meta tags in your layout component are in no way a responsibility of your "show blog post" view.

So you may start using code that sets global Blade variables in your controllers.

view()->share('title', 'About us');

But then that starts to feel messy and too low level. So you build an abstraction to be able to call something like this:

Something::setTitle('About us');

This is getting better, but you're still concerned about view global state potentially causing unexpected issues. So it's starting to become clear that any solutions that directly glue stuff into Blade layouts just don't work.

The thing is:

  • I do like setting SEO metadata from views, but I don't like having to do it.
  • I do like setting it from controllers, but I don't like global view state.
  • I do like logic for suffixing titles, but I don't like inlining that logic.

And now consider that we've only talked about titles. What about page-specific descriptions, cover images, different Twitter authors, and all the other metadata.

SEO meta tags are simple, but managing them is not.

Say hello to Laravel SEO

Laravel SEO provides a fluent and expressive layer for handling SEO metadata.

It lets you set the values like this:

seo()->title('About us')->description('We are a web development agency ...')

And read them like this:

<meta property="og:title" content="@seo('title')" />
<meta property="og:description" content="@seo('description')" />

As mentioned, the meta tags for OpenGraph site name, title, description, and image are provided by default.

Twitter (& extensions in general)

When you make a call such as:


the package converts the camelCase to twitter.title. Since there is a dot in the key, the value is considered to be the title for the twitter extension.

Extensions can be enabled by calling seo()->extension('foo'). However, you generally don't need to do this, because they're enabled automatically when the package notices that it's setting a value for an extension (i.e. it's prefixed with twitter. or foo.).

A major benefit of having things like the Twitter card as extensions is that you don't have to set all values repetitively. Extensions fall back to the main set of metadata.

seo()->title('About us'); // og:title will be added

seo()->description('We are a web development agency'); // og:description will be added

seo()->twitterTitle('Our company');
// twitter title will be Our company
// twitter description will be We are a web development agency

Default values

The metadata can have default values that will be used when no data is provided specifically for the page.

To set these, simply pass the values to the default: parameters:

    ->title(default: 'ArchTech — Web development agency')
    ->description(default: 'We are a web development agency that specializes in ...');


A unique feature of this package is modifiers. They will be used to modify the values when non-default values are used. The best example is title():

seo()->title(modifier: fn (string $title) => $title . ' | ArchTech');

seo()->title(default: 'ArchTech — Web development agency');

// title: ArchTech — Web development agency

seo()->title('About us');

// title: About us | ArchTech

Automatic cover images with Flipp

Something we almost always use on websites with lots of written content is generating cover images for social media. We use Flipp for this, because it works extremely well for that.

With Flipp, you create a template in a simple visual editor and you get an API endpoint. You generate URLs to that API with substituted parameters, such as title to replace the text used in the title layer.

Flipp editor

Flipp responds with an image and caches it on the server.

It's extremely simple, but extremely useful. For that reason we included it in the SEO package as well. It doesn't add any dependencies and gives you the option to generate cover images with a single method call (Flipp has a free tier as well).

This is not an advertisement post, but we really like Flipp and fully recommend it to anyone who needs cover images for social media, generated in a programmatic way. The generated URLs are also signed, so they can't be tampered with.

The usage of our Flipp integration is simple. First, you alias the template IDs, because you don't want to be referencing random 12-letter strings in your code, when you can just use blog for blog post previews:

seo()->flipp('blog', 'd5qx5kbcovru');

After that, you can use it by passing an array of data to the second argument:

seo()->flipp('blog', ['title' => $page->title, 'content' => $page->excerpt]);

// returns a signed URL
// sets seo()->image() to the URL

Alternatively, you can just call seo()->flipp('blog') and it will use the title and description from the main metadata.

Blade usage

The package also provides a convenient Blade helper:

<h1>@seo('title', $post->title)</h1>

<img src="@seo('image')" alt="@seo('description')>

<img src="@seo('flipp', 'blog')" alt="@seo('description')>

Calls with a single argument retrieve values, and calls with multiple arguments also set values.

To only set values without echoing them, use the array syntax:

@seo(['title' => $post->title])

This can be useful for configuring the metadata at the top of your Blade views, without necessarily rendering anything in those views.

The same syntax can be used with the PHP helper:

seo(['title' => 'foo', 'description' => 'bar', 'twitter.title' => 'baz'])


And that's all for this article. The package is on GitHub at archtechx/laravel-seo, where the installation and other things are documented as well.

We are moving all of our sites to this package — the clean yet flexible API can fully replace any custom SEO logic we used, while making the codebase much cleaner.

Lean Admin is launching this year!