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
Laravel Sluggable

Laravel Sluggable Laravel Package

spatie/laravel-sluggable

Generate unique slugs for Eloquent models on create or update via a Sluggable attribute or HasSlug trait. Includes collision suffixes, translatable slugs, and self-healing URLs with 308 redirects.

View on GitHub
Deep Wiki
Context7

spatie/laravel-sluggable automatically generates unique slugs for Eloquent models whenever they are created or updated. Just add a #[Sluggable] attribute to your model and it handles the rest—no manual slug logic needed. For advanced cases like custom source closures, scoped uniqueness, or conditional skipping, use the HasSlug trait with a getSlugOptions() method.

  • Unique slugs out of the box, with configurable -1, -2, ... suffixes on collisions
  • Self-healing URLs that combine the slug with the primary key and redirect stale links with a 308
  • Translatable slugs via HasTranslatableSlug and spatie/laravel-translatable
  • Overridable actions for swapping the slug generator or URL logic
  • Laravel Boost skill bundled for AI-assisted model scaffolding
Frequently asked questions about Laravel Sluggable
How do I install spatie/laravel-sluggable and generate a slug on an Eloquent model?
Run composer require spatie/laravel-sluggable, add a nullable slug column to the table, and mark the model with the #[Sluggable] attribute pointing at your source and destination fields. From then on every save() writes a unique slug automatically. The package adds no service provider configuration or migrations of its own, so the column and index are yours to own.
Should I use the #[Sluggable] attribute or the HasSlug trait with getSlugOptions()?
Use the attribute for the declarative cases: source column, destination column, separator, language, max length, uniqueness, create/update flags and preventOverwrite. Switch to the HasSlug trait as soon as you need closures, scoped uniqueness via extraScope(), conditional skipping, custom suffix generators, translatable slugs or self-healing URLs. Treat that as an architecture decision, because teams that start with the attribute and later need multi-tenant scoping have to refactor the model.
Which Laravel and PHP versions does spatie/laravel-sluggable support?
The package tracks currently supported Laravel releases, but the #[Sluggable] attribute API depends on PHP 8 attributes, so confirm your PHP constraint before adopting it. Check the composer.json on the repository and the Packagist release history to match the package major version to your app's Laravel version. In mixed-fleet environments, verify every service can use the same API before standardising on the attribute.
How do I make slugs unique per tenant or per locale?
Uniqueness is global by default, so add extraScope() in getSlugOptions() to scope it to a tenant_id or locale column. That requires the HasSlug trait, since extraScope() takes a column name and cannot be expressed through the attribute alone. Pair it with a matching composite unique index in the database so the rule holds at the storage level too.
Are slugs regenerated when a model is updated, and how do I stop old URLs from breaking?
Yes, onUpdate defaults to true, so editing the source field silently changes the slug and breaks inbound links. Set onUpdate to false for permalink semantics, or enable self-healing URLs so old links keep resolving. Either way, decide the policy before launch, because retrofitting it after links are indexed means redirect chains you have to own.
How do self-healing URLs work in laravel-sluggable?
Self-healing URLs build the route key from the slug plus the primary key, for example hello-world-5, so renaming a model never invalidates an existing link. The trait overrides getRouteKey() and resolveRouteBinding(), and a stale slug returns a 308 redirect to the canonical URL. Note that this pushes a routing concern into the model, so it is the highest-leverage but also most coupled option in the package.
What causes duplicate slug collisions under concurrent requests and how do I fix them?
Uniqueness is resolved by checking availability and then appending a -1, -2 suffix, which is not an atomic database guarantee, so two concurrent creates can both see the same slug as free. Add a real unique index on the slug column and catch the integrity constraint exception, then retry generation. Also watch maxLength, which defaults to 250 and truncates before suffixing, so size the column at 260 or lower maxLength.
How do I backfill slugs for existing rows in a large table?
Add the slug column as nullable with a non-unique index first, then ship a chunked job that regenerates slugs deterministically instead of relying on current titles alone. Preserve any legacy URL to new slug mapping in a redirect table rather than assuming a 308 is enough. Add the unique index only after the backfill and reconciliation complete, otherwise a single deploy can fail mid-migration.
Can I generate translatable slugs with spatie/laravel-translatable?
Yes, the package ships HasTranslatableSlug, which composes with spatie/laravel-translatable to write per-locale slugs. Install both packages and use the trait rather than the attribute, since translatable behaviour needs the getSlugOptions() API. Make sure the uniqueness scope matches your intent, because a slug that is unique globally behaves differently from one unique per locale.
How do I customise the separator, max length, or replace the slug generator entirely?
Separator, language, max length, uniqueness and create/update flags are all options on the attribute or in getSlugOptions(). For anything more custom, bind your own action class through the package config to replace the generator or the self-healing URL logic. Closures in getSlugOptions() also let you derive the slug from multiple columns or skip generation conditionally.
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.
dbravoan/dbcms-admin-theme-dbcms
dbravoan/dbcms-theme-db-cyber
dbravoan/dbcms-blocks-basic
dbravoan/dbcms-module-content-audit
dbravoan/dbcms-module-ai-assistant
paygate-to/paygate-php
padosoft/askmydocs-connector-base
defstudio/supercharged-carbon
bernskioldmedia/laravel-matomo
bernskioldmedia/laravel-harvest
bernskiold/laravel-data-scrubber
bernskiold/laravel-currency-converter
bernskiold/laravel-campaign-monitor
bernskioldmedia/laravel-autotranslate
siberfx/telegram-bot
roy-sandip/laravel-view-extended
gregsphotography/site-builder-core
roy-sandip/laravel-view
anodyne/blade-fluentui-icons
webx-ui/module-catalog-collections