
September 07, 2026
11 min read
By Kokil Thapa | Last reviewed: September 2026
Readable URLs are not a cosmetic detail on production Laravel applications—they affect crawlability, click-through rates, and how confidently you can share links in emails or social posts. Laravel URL Slugs with Sluggable Package automation saves you from hand-maintaining kebab-case strings every time an editor renames a blog post, product, or legal guide page. On law-firm portals and eCommerce builds I maintain, slug generation belongs in the model layer, not scattered across controllers or admin forms. If you are shipping on Laravel web development in Nepal or globally, this guide walks through the package most teams reach for first—cviebrock/eloquent-sluggable—with routing, uniqueness, SEO wiring, and Nepali-script edge cases that actually show up after launch.
Sluggable trait to an Eloquent model, defining a source field (usually title), and storing the generated kebab-case string in a unique slug column used for route model binding and canonical URLs.Why does Laravel URL Slugs with Sluggable Package matter for SEO and routing?
A slug is the human-readable segment at the end of a URL: /court-marriage-requirements-nepal instead of /posts/847. Search engines treat descriptive paths as a weak relevance signal, but the bigger win is operational—editors understand URLs, analytics reports become readable, and Laravel SEO setup stays consistent across sitemaps, breadcrumbs, and Open Graph tags.
Manual slug fields in admin panels break down quickly. Someone duplicates a title, pastes uppercase text, or leaves the slug empty. A sluggable package generates the value on create (and optionally on update), applies consistent rules, and can append numeric suffixes when two records share the same title. That pattern appears constantly on content-heavy sites—legal guides on Court Marriage In Nepal, directory listings, and product catalogues on Nepal Gift Card.
Google’s own URL structure guidance recommends simple, descriptive paths with readable words rather than opaque IDs. Laravel gives you explicit route model binding; the sluggable package gives you a stable lookup key. Together they replace brittle where id = ? public URLs without sacrificing database primary keys internally.
How do you install and configure Eloquent Sluggable in Laravel 13?
Start with PHP 8.3 or higher and Laravel 13.x. The widely deployed package is cviebrock/eloquent-sluggable—maintained specifically for Eloquent lifecycle hooks, not a generic string helper. Install via Composer 2.10:
composer require cviebrock/eloquent-sluggable
Migration and model setup
Add an indexed slug column. For high-traffic tables, a unique index prevents race-condition duplicates at the database level:
Schema::create('articles', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->string('slug')->unique();
$table->text('body');
$table->timestamps();
});
Follow the same discipline you would on any production schema—see database migrations and seeding best practices in Laravel for rollback-safe patterns. Wire the trait into your model:
<?php
namespace App\Models;
use Cviebrock\EloquentSluggable\Sluggable;
use Illuminate\Database\Eloquent\Model;
class Article extends Model
{
use Sluggable;
protected $fillable = ['title', 'body'];
public function sluggable(): array
{
return [
'slug' => [
'source' => 'title',
'onUpdate' => false,
'unique' => true,
],
];
}
}
Route model binding by slug
Laravel 13 supports scoped binding directly in the route definition—cleaner than overriding getRouteKeyName() globally:
Route::get('/articles/{article:slug}', [ArticleController::class, 'show'])
->name('articles.show');
Controller type-hinting stays unchanged. Laravel resolves the model by the slug column because you specified the binding key. Official details live in the Laravel route model binding documentation.
- Install
cviebrock/eloquent-sluggablewith Composer. - Add a unique
slugcolumn in your migration. - Apply the
Sluggabletrait and definesluggable()with asourcefield. - Bind routes with
{model:slug}instead of numeric IDs. - Emit canonical URLs and sitemap entries using the slug-based named route.
On greenfield apps I align this with modern Laravel architecture—keep slug logic in the model, validation in Form Requests, and SEO metadata in a dedicated layer such as artesaos/seotools, which I use regularly alongside sluggable on content sites.
How do you handle duplicate slugs and uniqueness in Laravel?
Two articles titled “Divorce Process in Nepal” will collide without a strategy. The package’s unique => true option queries existing rows and appends -1, -2, and so on. That is usually enough for blogs and legal guides on platforms like Notary Nepal.
public function sluggable(): array
{
return [
'slug' => [
'source' => 'title',
'unique' => true,
'uniqueSuffix' => '{%n}',
'separator' => '-',
'maxLength' => 80,
'maxLengthKeepWords' => true,
],
];
}
Scoped uniqueness
Multi-tenant or category-scoped content needs uniqueness within a scope, not globally. Pass a closure:
'unique' => true,
'uniqueSuffix' => '{%n}',
'includeTrashed' => false,
'scope' => fn ($query, $model) => $query->where('category_id', $model->category_id),
That allows identical titles in different categories—useful for directory sites such as Lawyers Pokhara where “Family Law” pages repeat across regions.
Should slugs update when the title changes?
Default behaviour keeps the original slug (onUpdate => false)—the right choice for published SEO URLs. Changing slugs without a 301 redirect breaks inbound links and Search Console history. If editors must rename URLs, store old_slugs in a JSON column or a dedicated redirects table and fire a redirect in middleware. Test edge cases with a regex tester when you validate slug patterns in Form Requests:
'slug' => ['nullable', 'regex:/^[a-z0-9]+(?:-[a-z0-9]+)*$/', 'max:80'],
Reserved slugs—admin, api, login—should be blocked via the package’s reserved array or application-level validation so generated paths never collide with Laravel API routes.
Which Sluggable package should you choose for Laravel—cviebrock or Spatie?
Two packages dominate Laravel projects. Both appear in my regular toolkit alongside packages covered in essential Laravel plugins. They solve the same problem with different ergonomics.
| Criteria | cviebrock/eloquent-sluggable | spatie/laravel-sluggable |
|---|---|---|
| Configuration style | sluggable() method returning array per attribute | HasSlug trait + getSlugOptions() |
| Lifecycle integration | Eloquent creating/updating events | Eloquent creating/updating events |
| Uniqueness suffix | Built-in uniqueSuffix with {%n} | Built-in suffix increment |
| Custom slug logic | method callable or custom engine | usingLanguage, custom generator callbacks |
| Ecosystem fit | Standalone; huge community examples | Natural alongside Spatie Media Library and Permission |
| Typical use case | Blogs, CMS pages, legal guides, directories | Apps already standardised on Spatie packages |
Neither package replaces technical SEO work—they automate string generation. Pick one per project and stay consistent. Switching mid-build means migrating slug columns and redirects. If you already use Spatie Media Library everywhere, Spatie Sluggable keeps mental overhead low. For most content-heavy Laravel 13 apps, cviebrock remains the default recommendation because Stack Overflow answers, tutorials, and legacy migrations reference it directly.
How do you wire slugs into SEO, sitemaps, and Nepali content?
Generating a slug is half the job. Production sites also need canonical tags, XML sitemap entries, breadcrumb schema, and hreflang if you publish multiple languages. On legal-tech portals I build, each guide page exposes one canonical URL derived from the named route—not a hard-coded string in Blade.
// In a controller or view composer
$canonical = route('articles.show', $article->slug);
// artesaos/seotools example
SEOMeta::setCanonical($canonical);
OpenGraph::setUrl($canonical);
Sitemap generation should iterate published models and emit loc elements from the same route helper. Mixed slug and ID URLs are a common indexation problem—pick slug-based public URLs everywhere or nowhere.
Nepali and Unicode titles
Transliterated Nepali titles slug cleanly; raw Devanagari often does not produce readable Latin paths. A pattern I've used on bilingual sites: keep title_ne for display, generate the slug from a Romanized title_en or a dedicated slug_source field. Editors can preview transliteration with a Nepali Unicode converter before publish.
public function sluggable(): array
{
return [
'slug' => [
'source' => 'slug_source',
'method' => function ($string, $separator) {
return Str::slug($string, $separator);
},
],
];
}
For manual slug overrides in admin panels, allow a nullable slug input but validate format server-side. Client-side JavaScript preview is fine; trust the model trait only when the field is empty on create.
Custom multi-field sources
Combine fields when titles alone are ambiguous—common in booking systems like Adventure Third Pole Trek where “Everest Base Camp” repeats across seasons:
'source' => ['title', 'season_year'],
'separator' => '-',
The package concatenates sources, slugifies the result, and still applies uniqueness rules. Keep an eye on max length—long slugs truncate awkwardly in search results.
Queue heavy sitemap rebuilds after bulk imports. On a production Laravel application, importing five hundred legal articles without deferred slug generation can block requests; chunk inserts and let model events generate slugs per row, or temporarily disable events and run an Artisan command that walks the table. Package internals are simple Eloquent hooks—understanding that helps when you debug “slug did not generate on seed” issues, as covered in custom Laravel package development thinking: know the lifecycle before you fight it.
Frontend asset pipelines do not affect slug generation, but keep URL helpers consistent in JavaScript by passing slugs from Blade—avoid reconstructing paths in Vue or Alpine that drift from named routes. See Vite config for Laravel projects for how compiled assets still consume server-rendered route URLs.
Key Takeaways
- Install
cviebrock/eloquent-sluggable, add a uniqueslugcolumn, and bind public routes with{model:slug}on Laravel 13. - Keep
onUpdate => falsefor published content unless you implement301redirects for every slug change. - Use scoped uniqueness when titles repeat across categories, tenants, or regions.
- Generate Latin slugs from Romanized or English source fields when primary titles are in Devanagari script.
- Wire canonical URLs, sitemaps, and schema to the same named route helper—never duplicate URL strings in templates.
- Block reserved paths (
admin,api,login) in validation or package config before deploy.
People Also Ask
Does Laravel 13 include built-in slug generation?
No. Laravel ships Str::slug() for string conversion, but automatic generation on model save, uniqueness suffixes, and scoped duplicate handling come from packages like cviebrock/eloquent-sluggable or spatie/laravel-sluggable. You still own the migration, route binding, and redirect strategy.
What happens to SEO if I change a slug after publishing?
Search engines treat the new URL as a different page unless you return a 301 Moved Permanently from the old slug. Without redirects, you split ranking signals and break inbound links. Default package behaviour keeps slugs stable on update precisely to avoid accidental SEO damage.
Can I use numeric IDs in URLs and slugs only for marketing pages?
Yes—many teams expose slugs on public content while keeping IDs for admin panels and APIs. Mixed strategies work if internal links never leak ID-based URLs into the indexable site; consistency matters more than all-or-nothing adoption.
Is a unique database index on slug required?
Strongly recommended. Package-level uniqueness checks race under concurrent requests; a unique index on MySQL 9.7 or MariaDB 12.3 guarantees integrity at the storage layer and fails fast during duplicate imports.
Ship readable URLs on your next Laravel build
Laravel URL Slugs with Sluggable Package integration takes an afternoon to implement correctly and years to regret if skipped—especially on content libraries, legal guides, and product catalogues where titles change but links must endure. Start with cviebrock on Laravel 13, bind routes by slug, add a unique index, and connect canonical metadata before you launch. If you want slug architecture, SEO wiring, and deployment handled together on a business-critical site, review the portfolio or reach out via contact us for custom software development—readable URLs are baseline infrastructure, not a launch-day afterthought.
Frequently Asked Questions
0 Comments
Leave a comment
Your email is not published. Comments appear once they have been read. Sign in to have your details filled in.

