Model or dataset
spatie/laravel-sluggable avatar
spatie/laravel-sluggable

spatie/laravel-sluggable: Laravel Slugs with Self-Healing URLs

An opinionated package to create slugs for Eloquent models

1,560 stars190 forksPHPMIT

At a glance

What is it?
spatie/laravel-sluggable generates unique slugs for Eloquent models and can preserve old routes after a title changes. It suits Laravel applications that want slug generation and route repair built into model conventions.
Who is it for?
Adopt spatie/laravel-sluggable when your Laravel application stores model slugs and can adopt its attribute or HasSlug conventions. Do not choose it solely for generic slug generation if you need a different routing or translation model.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 3 days ago.
What is it written in?
Mainly PHP, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on November 6, 2026, and from our analysis. They are not legal advice.

From daily snapshots

Star history

  • spatie/laravel-sluggable1,560 ★ · +0
Star history
Repository2026-10-232026-11-06
spatie/laravel-sluggable1.6k1.6k

0 Stars in the last 14 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

Eloquent Model Slug Generation

spatie/laravel-sluggable addresses a narrow Laravel problem: turning model attributes into readable, unique URL segments. It is intended for developers who use Eloquent models and want slug creation to happen when a record is created or updated rather than in each controller or form request. The basic configuration uses the `#[Sluggable]` attribute and identifies the source field and destination field.

The README gives this example:

php
use Spatie\Sluggable\Attributes\Sluggable;

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

When a `Post` is created with a title such as `activerecord is awesome`, the package assigns the value `activerecord-is-awesome` to the slug. The documented example reads that value through `$post->slug`. This keeps URL creation close to the model definition, although it also makes the model class the place where application-specific slug behavior is declared.

HasSlug and Closure-Based Configuration

The attribute is the simple entry point, but the README directs more advanced cases to the `HasSlug` trait and a `getSlugOptions()` method. That distinction matters because the trait can expose options that are difficult or inappropriate to express as a simple attribute. The documented alternatives include custom source callables, scoped uniqueness, conditional skipping, and custom suffix generators.

This gives the package two configuration styles rather than forcing every project through one abstraction. A basic model can declare its source and target fields. A model that needs conditional generation can place a callable or option definition in the method supplied by the trait. A model with several fields contributing to one slug can use a custom source callable. Scoped uniqueness is relevant when slugs should be unique within a category, author, or another model-specific partition rather than globally.

The README does not show the full `getSlugOptions()` syntax or a complete example for each option. The documentation site therefore remains necessary for implementation details. The repository layout includes `docs/`, `src/`, `config/`, and `tests/`, which separates documentation, implementation, configuration, and test coverage, but the README does not define every configuration key.

Collision Suffixes and Unique Slug Rules

Unique slugs are generated by adding a configurable suffix when a generated value collides. The README describes the default shape as `-1`, `-2`, and subsequent numbered suffixes. This is a practical compromise for readable URLs because it avoids replacing every later duplicate with a random token while still enforcing uniqueness.

The important boundary is the scope of that uniqueness. Basic package behavior is described as unique slug generation, while the `HasSlug` documentation specifically mentions scoped uniqueness. A project should not assume that every model uses the same uniqueness rule. A global slug and a slug unique only inside a tenant or category can look identical in application code while requiring different persistence checks.

The README does not describe database indexes, collation behavior, or the result of two writers creating colliding records concurrently. Those are application and database design questions that the package documentation leaves open. If concurrent writes or case-insensitive collisions matter, the generated value should be checked against the actual schema and uniqueness enforcement rather than inferred from the example.

Self-Healing URLs and Route Binding

The self-healing URL feature changes the routing strategy. Instead of using the slug as the complete route key, the documented format combines the slug with the model's primary key, producing values such as `hello-world-5`. This keeps the readable segment while retaining the record identity. If the title changes from `old title` to `hello world`, the old route can return a `308` redirect to the canonical URL.

The README shows the required setup:

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

The trait is required in this configuration because it overrides Eloquent's route key and route binding methods. That is a meaningful behavioral change, not just a display setting. Routes and application code that assume the slug alone is the model key must follow the documented `slug-primary-key` convention. The package also documents replacing the self-healing URL logic with a custom class through configuration.

This feature fits applications that need old links to survive content edits. It is less suitable if external systems, cached URLs, or route contracts require a stable slug-only format.

Installation and First Model Configuration

The README gives one installation command for a Composer-based Laravel project:

bash
composer require spatie/laravel-sluggable

After installation, the first documented use is to add the `Sluggable` attribute to an Eloquent model and save a record. The attribute requires the `Spatie\Sluggable\Attributes\Sluggable` import. The example below is copied from the README:

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

$post = Post::create(['title' => 'activerecord is awesome']);
$post->slug; // "activerecord-is-awesome"

The expected result is a `Post` whose slug is `activerecord-is-awesome`. If route model binding or the self-healing URL format is required, the model must also use `HasSlug`, as shown in the package example. The README links to the documentation site for the complete setup, but it does not document a configuration publication step, environment variable, or additional CLI command.

Translatable Slugs and Extension Boundaries

For applications using `spatie/laravel-translatable`, the README points to `HasTranslatableSlug` for translatable slugs. This indicates that translation support is a separate concern from the ordinary `Sluggable` attribute. The README does not show the trait configuration, expected locale selection behavior, or whether each translated field receives independent uniqueness handling, so those details cannot be treated as established behavior.

The package also supports replacing the slug generator or the self-healing URL logic through a config file. That is useful when the default opinionated behavior does not fit a project, but it transfers responsibility to application code. The README does not identify the exact configuration file path or replacement class names in its displayed examples, so an implementation should consult the repository configuration before assuming the extension interface.

Compared with a hand-written model observer, this package reduces repeated slug code and centralizes common suffix behavior. Compared with a full routing package, it does not replace Laravel's routing system. It works through Eloquent model configuration and route binding conventions, which makes it narrower and easier to combine with existing Laravel routing, but also limits it when slug generation needs a separate service or a different persistence architecture.

Maintenance, License, and Upgrade Surface

The repository is not archived. Its last push was on 2026-07-28, and the listed releases include `4.0.3` from 2026-07-28, `4.0.1` from 2026-04-29, and `4.0.0` from 2026-04-27. The README points to `CHANGELOG.md` and `UPGRADING.md` for release-specific changes and migration details. Those files are present in the repository layout, but the README does not summarize their contents, so the exact upgrade procedure and compatibility changes cannot be stated here.

The project is distributed under the MIT License. The repository includes `LICENSE.md`, and the README identifies the package as MIT licensed. This is a permissive code license, but an application still needs to review its own dependencies and obligations. The package is PHP and Laravel-specific, so upgrading it should be considered alongside the framework version, model changes, route binding changes, and any custom generator classes.

The feature set has a moderate extension surface: model attributes, the `HasSlug` trait, translatable slugs, config-file replacements, and route behavior. Changes in any of those areas can affect application routing even when the database schema does not change.

Editorial conclusion

Adopt spatie/laravel-sluggable when your Laravel application stores model slugs and can adopt its attribute or HasSlug conventions. Do not choose it solely for generic slug generation if you need a different routing or translation model. Verify first that your Laravel and PHP environment matches the package requirements, then confirm whether your routes should use the documented self-healing slug plus primary key format. The repository is not archived, its last push was on 2026-07-28, and version 4.0.3 was released on 2026-07-28.

Frequently asked questions

How do I install spatie/laravel-sluggable?

The README instructs Composer users to run `composer require spatie/laravel-sluggable`. It does not document additional installation commands or environment configuration.

How does spatie/laravel-sluggable create a slug?

Add the `#[Sluggable]` attribute to an Eloquent model and specify the source and destination fields. The README's example maps `title` to `slug`, producing `activerecord-is-awesome` from the title `activerecord is awesome`.

Does spatie/laravel-sluggable support self-healing URLs?

Yes. The README documents a `selfHealing: true` configuration that combines the slug with the primary key and returns a `308` redirect from a stale URL to the canonical URL. The model must use `HasSlug` for this setup.

Can spatie/laravel-sluggable handle translations?

The README states that translatable slugs are available through `HasTranslatableSlug` and `spatie/laravel-translatable`. It does not provide the full configuration example or locale-specific behavior.

Official sources

  1. License: MIT
  2. Project website
  3. README
  4. Releases
  5. spatie/laravel-sluggable on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/spatie-laravel-sluggable.svg)](https://hysenlabs.com/projects/spatie-laravel-sluggable)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/spatie-laravel-sluggable.svg)](https://hysenlabs.com/projects/spatie-laravel-sluggable)

Was this useful?