PHPackages                             nunomaduro/laravel-sluggable - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Database &amp; ORM](/categories/database)
4. /
5. nunomaduro/laravel-sluggable

ActiveLibrary[Database &amp; ORM](/categories/database)

nunomaduro/laravel-sluggable
============================

Automatic slug generation for Eloquent models via the #\[Sluggable\] attribute.

v1.0.0(1mo ago)1371.1k↑186.4%3[1 issues](https://github.com/nunomaduro/laravel-sluggable/issues)[1 PRs](https://github.com/nunomaduro/laravel-sluggable/pulls)MITPHPPHP ^8.5.0CI passing

Since Apr 17Pushed 2w ago2 watchersCompare

[ Source](https://github.com/nunomaduro/laravel-sluggable)[ Packagist](https://packagist.org/packages/nunomaduro/laravel-sluggable)[ GitHub Sponsors](https://github.com/nunomaduro)[ RSS](/packages/nunomaduro-laravel-sluggable/feed)WikiDiscussions 1.x Synced 1w ago

READMEChangelog (1)Dependencies (14)Versions (3)Used By (0)

 [![Laravel Sluggable code example](https://raw.githubusercontent.com/nunomaduro/laravel-sluggable/1.x/docs/logo.png)](https://raw.githubusercontent.com/nunomaduro/laravel-sluggable/1.x/docs/logo.png)

 [![GitHub Workflow Status (master)](https://github.com/nunomaduro/laravel-sluggable/actions/workflows/tests.yml/badge.svg)](https://github.com/nunomaduro/laravel-sluggable/actions) [![Total Downloads](https://camo.githubusercontent.com/76549788f9fba874ca8ea0e62262625cc3cb1bb23269a768d88483bec97d0a83/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f6e756e6f6d616475726f2f6c61726176656c2d736c75676761626c65)](https://packagist.org/packages/nunomaduro/laravel-sluggable) [![Latest Version](https://camo.githubusercontent.com/d0202df238f8cdc865fb9aeaa1ee05cae009039d40e9d0e2c7f261ae26e3ca47/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f6e756e6f6d616475726f2f6c61726176656c2d736c75676761626c65)](https://packagist.org/packages/nunomaduro/laravel-sluggable) [![License](https://camo.githubusercontent.com/fc74cdf2c3b598f5f1bf997a6106251cac48a4155b676357f680810400e75ee1/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f6c2f6e756e6f6d616475726f2f6c61726176656c2d736c75676761626c65)](https://packagist.org/packages/nunomaduro/laravel-sluggable) [![YouTube Channel Subscribers](https://camo.githubusercontent.com/a32a48cc6ec1283f38f5beedd4f3aec3eff914f663007ce9295ab1b60e5507e2/68747470733a2f2f696d672e736869656c64732e696f2f796f75747562652f6368616e6e656c2f73756273637269626572732f55434f5f68595a463267625f4379473573413741726c47673f7374796c653d666c6174266c6162656c3d796f757475626526636f6c6f723d627269676874677265656e)](https://youtube.com/@nunomaduro?sub_confirmation=1) [![Laravel Compatibility](https://camo.githubusercontent.com/aeebd409d363ab7814b6bd8c14cbdb83530fa8b3ecd25be94a88ca936be69f9f/68747470733a2f2f62616467652e6c61726176656c2e636c6f75642f62616467652f6e756e6f6d616475726f2f6c61726176656c2d736c75676761626c65)](https://packagist.org/packages/nunomaduro/laravel-sluggable)

---

**Laravel Sluggable** is my opinionated take on **automatic slug generation for Eloquent models** — the exact pattern I've reached for across projects like **Laravel Cloud**, now packaged up. A single `#[Sluggable]` attribute on the model is all you need. No trait, no base class, no extra wiring.

It handles **all of the weirdest edge cases** you can think about; slugs collisions, Unicode and CJK transliteration, domain-aware dot preservation, scoped uniqueness (per-tenant, per-locale), multi-column sources, soft-deleted record collisions, and more.

> **Requires [PHP 8.5+](https://php.net/releases/) and [Laravel 13.5+](https://laravel.com)**

Installation
------------

[](#installation)

```
composer require nunomaduro/laravel-sluggable
```

Getting Started
---------------

[](#getting-started)

To make an existing model sluggable, run the **`make:sluggable`** Artisan command:

```
php artisan make:sluggable Post
```

This command adds the `#[Sluggable]` attribute to your model and generates a migration for the slug column. It introspects the model's table to guess the source column (`name`, `title`, `headline`, or `subject`):

```
+use NunoMaduro\LaravelSluggable\Attributes\Sluggable;

+#[Sluggable(from: 'title')]
class Post extends Model
{
}
```

It also generates a migration under `database/migrations` that adds the slug column:

```
Schema::table('posts', function (Blueprint $table) {
    $table
        ->string('slug')
    //  ->nullable()
        ->unique()
        ->after('id');
});
```

**Review the migration before running it.** The right shape depends on how you configured the attribute and the state of your existing data. For example, on a table with existing rows you typically want to use the `->nullable()`, etc.

Once the migration is in shape, run:

```
php artisan migrate
```

When a `Post` is created, a slug will be automatically generated and stored in the `slug` column:

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

The command also accepts `--from` and `--to` options:

```
php artisan make:sluggable Post --from=headline --to=url_slug
```

Configuration
-------------

[](#configuration)

Every aspect of slug generation can be customized directly on the attribute.

### `from`

[](#from)

By default, the slug is generated from the `name` column. You may customize this with the `from` parameter:

```
#[Sluggable(from: 'title')]
```

Slugs may also be generated from multiple columns by passing an array:

```
#[Sluggable(from: ['first_name', 'last_name'])]
class Author extends Model
{
}

$author = Author::create(['first_name' => 'John', 'last_name' => 'Doe']);
$author->slug; // "john-doe"
```

### `to`

[](#to)

By default, the slug is stored in the `slug` column. You may customize this with the `to` parameter:

```
#[Sluggable(from: 'title', to: 'url_slug')]
```

### `scope`

[](#scope)

When slugs should be unique within a scope (for example, per team or per locale), pass one or more scope columns:

```
#[Sluggable(scope: 'team_id')]
```

Multiple scope columns are also supported:

```
#[Sluggable(scope: ['team_id', 'locale'])]
```

### `onUpdating`, `separator`, `unique`, `maxAttempts`, `maxLength`

[](#onupdating-separator-unique-maxattempts-maxlength)

The attribute accepts several other options to fine-tune slug generation. By default, slugs are generated on creation but not on update, use `-` as the separator, enforce uniqueness with up to 100 attempts, and have no maximum length:

```
#[Sluggable(
    onUpdating: false,
    separator: '-',
    unique: true,
    maxAttempts: 100,
    maxLength: 60,
)]
```

When `onUpdating` is enabled, the slug is regenerated whenever any source column changes. When a slug value is manually provided, it is always preserved — both on creation and on update.

### `errorKey`

[](#errorkey)

When slug generation fails, a `CouldNotGenerateSlugException` is thrown. In HTTP contexts, this exception renders automatically as a `422` validation error response — just like a failed validation rule. The error is attached to the first source column by default:

```
{
    "errors": {
        "name": ["The name cannot be converted into a valid slug."]
    }
}
```

You may customize the error key using the `errorKey` parameter:

```
#[Sluggable(errorKey: 'input_name')]
```

Error messages may be customized by defining `validation.slug_required` and `validation.slug_unique` keys in your application's language files. Both keys receive `:attribute` and `:slug` replacements:

```
// lang/en/validation.php
'slug_required' => 'The :attribute cannot be converted into a valid :slug.',
'slug_unique' => 'Too many :slug entries exist for the given :attribute. Please try a different value.',
```

Because it extends `ValidationException`, the exception is not reported to the application log — consistent with how Laravel handles `ModelNotFoundException` and other Eloquent exceptions.

Slug Generation Pipeline
------------------------

[](#slug-generation-pipeline)

The pipeline has **first-class Unicode and CJK support** — non-Latin scripts transliterate to readable Latin slugs (`如何安装 Laravel` → `ru-he-an-zhuang-laravel`).

It's also **domain-aware**: values like `laravel.com`, `sub.domain.example.com`, `document.final.pdf`, and `über.straße` keep their dots intact — most generators flatten them.

A few examples:

InputSlug`Hello World``hello-world``Café Résumé``cafe-resume``Straße``strasse``如何安装 Laravel``ru-he-an-zhuang-laravel``こんにちは``konnichiha``안녕하세요``annyeonghaseyo``Привет Мир``privet-mir``laravel.com``laravel.com``sub.domain.example.com``sub.domain.example.com``document.final.pdf``document.final.pdf``über.straße``uber.strasse``Example/Path``example-path``Hello — World``hello-world``🎉 Hello 🌟 World 🚀``hello-world``[2024] Annual Report (Final)``2024-annual-report-final`---

**Laravel Sluggable** was created by **[Nuno Maduro](https://x.com/enunomaduro)** under the **[MIT license](https://opensource.org/licenses/MIT)**.

###  Health Score

52

—

FairBetter than 96% of packages

Maintenance93

Actively maintained with recent releases

Popularity36

Limited adoption so far

Community14

Small or concentrated contributor base

Maturity52

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 83.3% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~26 days

Total

2

Last Release

27d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/86cfef5c1f5195df1a9db17a5f8ecb34455e1f0133a725de9acf7f2fb26ac6a1?d=identicon)[nunomaduro](/maintainers/nunomaduro)

---

Top Contributors

[![nunomaduro](https://avatars.githubusercontent.com/u/5457236?v=4)](https://github.com/nunomaduro "nunomaduro (20 commits)")[![lokomilo](https://avatars.githubusercontent.com/u/15527560?v=4)](https://github.com/lokomilo "lokomilo (2 commits)")[![nunolaraveltestaccount](https://avatars.githubusercontent.com/u/106339258?v=4)](https://github.com/nunolaraveltestaccount "nunolaraveltestaccount (1 commits)")[![peterfox](https://avatars.githubusercontent.com/u/1716506?v=4)](https://github.com/peterfox "peterfox (1 commits)")

---

Tags

eloquentlaravelmodelsslugsphpsluglaraveleloquentsluggable

###  Code Quality

TestsPest

Static AnalysisPHPStan, Rector

Code StyleLaravel Pint

Type Coverage Yes

### Embed Badge

![Health badge](/badges/nunomaduro-laravel-sluggable/health.svg)

```
[![Health](https://phpackages.com/badges/nunomaduro-laravel-sluggable/health.svg)](https://phpackages.com/packages/nunomaduro-laravel-sluggable)
```

###  Alternatives

[laravel/ai

The official AI SDK for Laravel.

9782.1M153](/packages/laravel-ai)[prettus/l5-repository

Laravel 8|9|10|11|12|13 - Repositories to the database layer

4.3k11.2M153](/packages/prettus-l5-repository)[yajra/laravel-oci8

Oracle DB driver for Laravel via OCI8

8733.1M23](/packages/yajra-laravel-oci8)[roots/acorn

Framework for Roots WordPress projects built with Laravel components.

9732.3M121](/packages/roots-acorn)[erag/laravel-disposable-email

A Laravel package to detect and block disposable email addresses.

249143.0k](/packages/erag-laravel-disposable-email)[itpathsolutions/dbstan

Database Standardization and Analysis Tool for Laravel

442.1k](/packages/itpathsolutions-dbstan)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
