Weave 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

Seo Bundle Laravel Package

rami/seo-bundle

rami/seo-bundle is a Laravel package for adding search-engine-friendly metadata to your app. It simplifies managing meta tags, titles, descriptions, Open Graph data, and canonical URLs through configuration and Blade integration, helping keep SEO output consistent across pages and models.

View on GitHub
Deep Wiki
Context7

Getting Started

Read this first: rami/seo-bundle is a Symfony bundle, not a Laravel package. There is no service provider, no facade, no config/seo.php publish tag, and no package discovery. So "getting started" splits into two real paths:

  1. You actually run Symfony somewhere (legacy monolith, headless CMS, a sidecar app) — use the bundle as intended.
  2. You're in Laravel — you don't install it. You read its src/ for the tag model and sitemap generator and re-implement ~200 lines of it as a service + Blade component. Given 34 stars and 0 dependents, treating it as a reference implementation is the honest move.

Symfony path, minimal steps:

composer require rami/seo-bundle
# config/bundles.php  ->  Rami\SeoBundle\RamiSeoBundle::class => ['all' => true]
php bin/console config:dump-reference <alias>   # alias is whatever the bundle's Configuration.php declares

Then look, in this order, at the vendored source:

Where Why
README.md Usually the whole doc surface; expect it to be thin
DependencyInjection/Configuration.php The real config schema — this is the actual documentation
Resources/config/*.xml|yaml Service IDs and aliases you can inject
Twig/ The extension + function names you'll call in templates
Sitemap/ or Command/ Whether generation is a console command or a controller

First use case: one _seo.html.twig partial included in your base layout, driven by defaults from config and overridden per-action. Don't wire structured data on day one — get <title>, description, canonical, and OG tags rendering once, from one place, before expanding.

Laravel path, first use case: a SeoData DTO + a singleton SeoManager bound in AppServiceProvider, rendered by a single <x-seo :seo="$seo" /> component in your layout. Sitemap as an Artisan command writing to public/sitemap.xml. That gets you 90% of the value in an afternoon.

Implementation Patterns

Single source of truth for tags. The most common failure mode is tags emitted from three places (layout, controller, entity). Build one DTO — title, description, canonical, robots, og:*, twitter:*, JSON-LD graph — and let exactly one renderer emit it. In Symfony that's the bundle's Twig function; in Laravel that's your Blade component.

Defaults + per-route override. Configure site-wide values (site name, default OG image, Twitter handle, Organization JSON-LD) once, then override only what differs per page. In Symfony this is usually controller attributes or an event subscriber reading the request; in Laravel, a middleware that resolves the model from the route and populates the DTO.

Structured data as an array, not a template. Build PHP arrays and json_encode them; never hand-write JSON-LD in Twig/Blade. Keep a global @graph (Organization, WebSite, optional BreadcrumbList) and append page-specific nodes (Article, Product, FAQPage). This is the single most portable idea in the bundle.

Sitemap generation, batched. Pull from the repository/query builder with toIterable() or chunk(), not findAll(). Emit a sitemap index once you pass ~50k URLs or ~50MB. Generate on deploy or on a schedule, not per-request.

Cache invalidation belongs to the model. In Symfony, a Doctrine postPersist/postUpdate listener clears the sitemap cache. In Laravel, a model observer doing the same. Without this you'll ship a stale sitemap for weeks and never notice.

Testing. Both frameworks make this cheap: HTTP-level assertions that the rendered HTML contains <link rel="canonical" href="https://..."> and a parseable <script type="application/ld+json">. Validate JSON-LD with json_decode in the test rather than string-matching — it catches escaping bugs early.

Gotchas and Tips

No Laravel interop, at all. Don't attempt to composer require it into a Laravel app and instantiate the services manually — you'll pull in Symfony DI, Twig, and HttpFoundation assumptions that fight Laravel's container. Port the logic instead; it's small.

Twig dependency is a hard wall. If you're porting, everything Twig-side must be rewritten as Blade components/directives. Budget for that; it's usually the bulk of the work, not the tag logic.

JSON-LD escaping. json_encode with JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE is fine, but you must also neutralize </script> inside string values (replace / with \/ or use JSON_HEX_TAG). Output the block with |raw / {!! !!} — and only that block. Raw-outputting meta descriptions is an XSS hole.

CLI sitemap generation has no request host. URL generation from a console command produces http://localhost/... unless you configure framework.router.default_uri (Symfony) or pass an explicit base URL (Laravel). Silent, ugly bug — your sitemap ships with localhost URLs and Google rejects it.

Duplicate and misordered tags. Symfony's tag emission order is whatever the Twig extension iterates; some scrapers take the first <title> or og:image. If your layout still hardcodes <title>, you get two. Pick one owner and delete the other.

Config caching. YAML changes don't apply until cache:clear, and in prod you're on a compiled container. If a config key seems ignored, check the cached container before you debug the bundle.

Adoption risk is real. 34 stars, 0 dependents, and a last-release date in the future (2026-10-16) — verify the tag actually exists on Packagist before pinning, and pin an exact version. Read composer.json for PHP/Symfony constraints; a small bundle like this often lags a major Symfony release.

Extension points worth knowing. Prefer service decoration (decorates:) over forking; add custom tag types by extending the renderer rather than editing the Twig template; use a kernel/HTTP event subscriber for per-request SEO resolution so controller code stays clean.

Absolute URLs only. Canonical, og:url, and hreflang values must be absolute and consistent (same scheme/host/trailing-slash policy as your redirects). Relative canonicals are silently ignored by crawlers and are the #1 cause of "we added SEO tags and nothing changed."

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.
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource
noith/settings