arcanedev/seo-helper
Laravel package for managing SEO metadata with a clean API. Set page titles, meta descriptions, keywords, canonical URLs, robots directives, Open Graph, and Twitter Card tags, with Blade helpers for easy integration.
Install via Composer; Laravel's package discovery registers the service provider and the SEO facade automatically:
composer require arcanedev/seo-helper
Publish and review the config first — it is the source of truth for defaults, Open Graph/Twitter data, analytics, and webmaster verification tags:
php artisan vendor:publish --provider="Arcanedev\SeoHelper\SeoHelperServiceProvider"
If you want model-backed metadata (per-post titles/descriptions), also publish and run the migration that creates the polymorphic seo_meta table:
php artisan vendor:publish --tag=seo-helper:migrations
php artisan migrate
First use case: in your base layout's <head>, render everything in one call:
<head>
<meta charset="utf-8">
{!! SEO::render() !!}
</head>
Then set values per page from the controller (or a view composer) before the layout renders:
public function show(Post $post)
{
SEO::setTitle($post->title);
SEO::setDescription(Str::limit(strip_tags($post->excerpt), 155));
SEO::setCanonical($post->url);
SEO::setImage($post->featured_image_url); // absolute URL
SEO::setKeywords($post->tags->pluck('name')->all());
return view('posts.show', compact('post'));
}
Centralize defaults, override per page. Put site-wide defaults in config/seo-helper.php (site title suffix, default description, robots, Open Graph image) and let controllers set only what's page-specific. This keeps {!! SEO::render() !!} in one layout file and avoids duplicating tags in every view.
Use a middleware or view composer for the common case. A small middleware that sets title/description from the route, or a View::composer('*', ...) that applies the site name, prevents every controller from repeating boilerplate:
View::composer('*', function ($view) {
SEO::setTitle(config('app.name'), ' | ');
});
Model-backed SEO via the SeoMeta trait. For CMS-style apps, add the trait to models that need editable metadata and manage it in an admin form with updateOrCreate:
use Arcanedev\SeoHelper\Traits\SeoMeta;
class Post extends Model
{
use SeoMeta;
}
// Controller
SEO::setModel($post); // pulls title/description/keywords from the relation
// Admin save
$post->seoMeta()->updateOrCreate([], [
'title' => $request->seo_title,
'description' => $request->seo_description,
'keywords' => $request->seo_keywords,
]);
Open Graph / Twitter integration. Configure site-level OG type, Twitter handle, and fallback image once in config, then override per page (setImage, setTitle, setUrl) so shared links preview correctly. Always use absolute URLs for images and canonicals.
Webmaster and analytics tags. Rather than pasting verification/analytics snippets into Blade, register them through the helper (or config) so they render consistently and only when configured:
SEO::addWebmaster('google', config('services.google.verification'));
SEO::addAnalytics('google', config('services.google.analytics_id'));
Keep the render call last. Anything set after SEO::render() in the layout is silently ignored, so treat setting SEO metadata as a controller/middleware responsibility and rendering as a pure output concern.
{!! SEO::render() !!} — using {{ }} will HTML-escape the tags and dump them as text.<head>.setUp()/tearDown() (inspect the SeoHelper class for the reset/clear method) or refresh the application between tests.config/seo-helper.php, run php artisan config:clear (and re-cache in production). Stale cached config is the usual cause of "my default description didn't change."setKeywords accepts arrays or strings — pass an array for tagged content, a comma-separated string for static pages; both render as a single keywords meta tag.url()/asset().illuminate/* constraints in composer.json against your Laravel version before upgrading majors; older Laravel installs may need a pinned older release and manual provider/facade registration (pre-auto-discovery).Meta/Open Graph/Twitter objects are resolvable from the container if you need to add custom tags (SEO::addMeta('name', 'content')) or build a Blade component wrapping common patterns. For anything the package doesn't cover (JSON-LD structured data, sitemaps), generate it separately and include it alongside SEO::render() — don't try to force it through the meta builder.How can I help you explore Laravel packages today?