Weave Code
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 Package

spatie/laravel-sluggable

Generate unique slugs for Eloquent models on create/update. Supports collision suffixes, translatable slugs, and customizable slug options. Includes self-healing URLs that keep old links working via slug+ID route keys with 308 redirects to the canonical URL.

View on GitHub
Deep Wiki
Context7

title: Generating your first slug weight: 1

This walkthrough takes a Post model from nothing to a working slug in three steps: the #[Sluggable] attribute, a migration, and a query. An optional fourth step wires the slug into a route with self-healing URLs so renaming a model never breaks an old link.

1. Add the attribute to the model

Place #[Sluggable] on the model class and tell it which column to read from and which to write to.

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Spatie\Sluggable\Attributes\Sluggable;

#[Sluggable(from: 'title', to: 'slug')]
class Post extends Model
{
}

That's all the configuration the package needs. Its service provider listens for Eloquent's creating and updating events and reads the attribute at runtime, so no trait or extra registration is required.

2. Add a slug column to the migration

The package writes the slug into the column you named in the attribute, so the table needs a matching string column.

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    public function up(): void
    {
        Schema::create('posts', function (Blueprint $table) {
            $table->id();
            $table->string('title');
            $table->string('slug')->unique();
            $table->timestamps();
        });
    }
};

The unique() constraint is optional. The package appends -1, -2, etc. on collisions whether or not the database enforces uniqueness, but the constraint is a useful safety net for code paths that bypass Eloquent.

3. Use it

Slugs are written when the model is created and regenerated whenever the source field changes.

$post = Post::create(['title' => 'Hello World']);
$post->slug; // "hello-world"

$post->update(['title' => 'Hello Universe']);
$post->slug; // "hello-universe"

4. Wire it into a route with self-healing (optional)

This step is optional. Skip it if you don't need slugs in your URLs, or if your slugs genuinely never change after creation (see Self-healing URLs for plain {post:slug} binding).

Most user-editable content (blog posts, articles, products, documentation pages, events) eventually gets renamed, and a renamed slug breaks every existing link unless you opt into self-healing URLs. With self-healing enabled the route key becomes {slug}-{id}, the primary key drives the lookup, and a stale slug 308-redirects to the canonical URL instead of returning a 404.

To follow along, add selfHealing: true to the attribute on the Post model from step 1 and add use HasSlug; to the class. The trait is required because self-healing has to override getRouteKey() and resolveRouteBinding().

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Spatie\Sluggable\Attributes\Sluggable;
use Spatie\Sluggable\HasSlug;

#[Sluggable(
    from: 'title',
    to: 'slug',
    selfHealing: true,
)]
class Post extends Model
{
    use HasSlug;
}

Bind the model to a route the usual way; the package handles the route key for you.

// routes/web.php
Route::get('/posts/{post}', fn (Post $post) => view('posts.show', compact('post')));

A request to /posts/hello-world-5 resolves the post; renaming it later doesn't break the link. See Self-healing URLs for the full mechanics, including the plain {post:slug} alternative for slugs that genuinely never change.

What's next

That is enough for most projects. From here you can:

  • Tune the generation rules (separator, language, uniqueness, when to skip): see the rest of this section.
  • Look slugs up directly with findBySlug().
  • Translate slugs per locale with HasTranslatableSlug.

Let the Laravel Boost skill set things up for you

If your project uses Laravel Boost, this package ships a Boost skill that teaches Boost-aware AI assistants (Claude Code, Cursor, Copilot CLI, Gemini CLI, and others) how to scaffold every step above, including the optional self-healing route binding. Ask your assistant something like "set up sluggable on the Post model" and it will write the migration, add the attribute, and wire up the route.

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.
jayeshmepani/jpl-moshier-ephemeris-php
elnasnato/laraliveui
labrodev/rest-sdk
sampaui/sampaui
babelqueue/php-sdk
facebook/capi-param-builder-php
babelqueue/symfony
hamzi/corewatch
minionfactory/raw-hydrator
hexters/coinpayment
rjcodes/rjcms
act-training/laravel-permissions-manager
alimarchal/laravel-chart-of-accounts
babenkoivan/elastic-scout-driver
mkwebdesign/filament-watchdog-v5
renatomarinho/laravel-page-speed
zedmagdy/filament-business-hours
renatovdemoura/blade-elements-ui
devgeek/beacon-admin
benjamin-rqt/data-watcher-bundle