blendbyte/filament-title-with-slug
Filament v5 Title + Slug input for Laravel: auto-generate slugs, live permalink preview, and inline editing. Works with Laravel 11–13. Simple drop-in field to manage titles and clean URLs in your Filament forms.
Title + slug field for Filament v5 with auto-slug generation, live permalink preview, and inline editing.
use Blendbyte\FilamentTitleWithSlug\TitleWithSlugInput;
TitleWithSlugInput::make()
| Package (blendbyte) | Filament | Laravel | Namespace |
|---|---|---|---|
^3.x |
^5.0 |
^11 / ^12 / ^13* |
Blendbyte\FilamentTitleWithSlug |
^2.x ⚠️ (deprecated) |
^4.0 |
^11 / ^12 |
Camya\Filament\Forms\Components |
^1.x ⚠️ (deprecated) |
^3.0 |
^10 / ^11 / ^12 |
Camya\Filament\Forms\Components |
*PHP 8.2 is not tested against Laravel 13.
v1.x and v2.x are not actively maintained. They receive security and severe bug fixes only — no new features. Please upgrade to v3.x. See the Upgrading section for migration steps.
Looking for Filament v2 support? The original package is at camya/filament-title-with-slug.
https://example.com/blog/my-post)All upgrades to v3.x share one breaking change: the namespace changed from Camya\Filament\Forms\Components (used in v1 and v2) to Blendbyte\FilamentTitleWithSlug.
Find every import of the component in your app and update it:
// Before (v1.x and v2.x)
use Camya\Filament\Forms\Components\TitleWithSlugInput;
// After (v3.x)
use Blendbyte\FilamentTitleWithSlug\TitleWithSlugInput;
The TitleWithSlugInput::make() call signature and all parameter names are unchanged.
composer require blendbyte/filament-title-with-slug:^3.0
New in v3: the titleField parameter for passing a custom field (e.g. translatable) as the title input.
composer require blendbyte/filament-title-with-slug:^3.0
camya/filament-title-with-slug (Filament v2)1. Swap the package:
composer remove camya/filament-title-with-slug
composer require blendbyte/filament-title-with-slug:^3.0
2. Update the namespace as shown above.
3. Re-publish config and translations if you had published them previously:
php artisan vendor:publish --tag="filament-title-with-slug-config" --force
php artisan vendor:publish --tag="filament-title-with-slug-translations" --force
4. Remove any manual CSS imports — v3 registers its stylesheet automatically.
5. See the Filament upgrade guide for app-level Filament changes.
composer require blendbyte/filament-title-with-slug
Optionally publish the config file:
php artisan vendor:publish --tag="filament-title-with-slug-config"
Publish the translation files:
php artisan vendor:publish --tag="filament-title-with-slug-translations"
Published translations land at lang/vendor/filament-title-with-slug.
Included languages: English, French, Brazilian Portuguese, German, Dutch, Indonesian, Arabic.
Added a translation? Share it on our GitHub Discussions page.
Add TitleWithSlugInput to any Filament form schema. It binds to title and slug by default:
use Blendbyte\FilamentTitleWithSlug\TitleWithSlugInput;
class PostResource extends Resource
{
public static function form(Form $form): Form
{
return $form->schema([
TitleWithSlugInput::make(),
]);
}
}
Tip: Use
->columnSpan('full')to make the component span the full form width.
Pass any Filament field as the title input via titleField. This is useful when you need a translatable input or another custom field type from a third-party package:
TitleWithSlugInput::make(
titleField: \Spatie\FilamentTranslatable\Forms\Components\TranslatableInput::make('title'),
)
The package automatically wires slug auto-generation onto the provided field. The field name is derived from getName(), so you don't need to pass fieldTitle separately unless you want to override it.
All title-specific parameters (titleLabel, titleRules, titlePlaceholder, etc.) are ignored when titleField is provided — configure those directly on your field before passing it in.
The titleAfterStateUpdated and titleFieldWrapper parameters still work regardless:
TitleWithSlugInput::make(
titleField: MyTranslatableField::make('title')
->label('Post Title')
->required(),
titleAfterStateUpdated: function (Set $set, string $state) {
// runs after every title update
},
)
Default field names are title and slug. Override them:
TitleWithSlugInput::make(
fieldTitle: 'name',
fieldSlug: 'identifier',
)
TitleWithSlugInput::make(
urlPath: '/book/',
urlVisitLinkLabel: 'Visit Book',
titleLabel: 'Title',
titlePlaceholder: 'Insert the title...',
slugLabel: 'Link:',
)
TitleWithSlugInput::make(
urlHostVisible: false,
)
TitleWithSlugInput::make(
urlPath: '/category/',
urlHost: 'https://project.local',
)
Use a named route to generate the Visit link URL:
TitleWithSlugInput::make(
urlPath: '/product/',
urlHost: 'camya.com',
urlVisitLinkRoute: fn(?Model $record) => $record?->slug
? route('product.show', ['slug' => $record->slug])
: null,
)
Because the Visit URL is now route-generated, you can use a short urlHost just for the permalink preview display.
TitleWithSlugInput::make(
urlVisitLinkVisible: false,
)
Pass extra HTML attributes directly to the title <input> element:
TitleWithSlugInput::make(
titleExtraInputAttributes: ['class' => 'italic'],
)
Lock either field, optionally based on context:
TitleWithSlugInput::make(
titleIsReadonly: fn($context) => $context === 'edit',
slugIsReadonly: fn($context) => $context === 'edit',
)
When slugIsReadonly is true the slug row renders as a static permalink display (no edit link, no action buttons).
TitleWithSlugInput::make(
titleRules: [
'required',
'string',
'min:3',
'max:120',
],
)
A unique rule is automatically applied to the slug. To customize it, see Custom unique validation.
Pass an array of named arguments that map to Filament's ->unique() method:
TitleWithSlugInput::make(
slugRuleUniqueParameters: [
'modifyRuleUsing' => fn(Unique $rule) => $rule->where('is_published', 1),
'ignorable' => fn(?Model $record) => $record,
],
)
Available keys: ignorable, modifyRuleUsing, ignoreRecord, table, column.
Override validation messages in your resource class:
protected $messages = [
'data.slug.regex' => 'Invalid slug. Use only a–z, 0–9, and hyphens.',
];
Replace the default Str::slug() with your own closure:
TitleWithSlugInput::make(
slugSlugifier: fn($string) => preg_replace('/[^a-z]/', '', $string),
slugRuleRegex: '/^[a-z]*$/',
)
Remove the slug's required rule, then use / as the slug value to represent the homepage. The / bypasses the auto-regeneration that would trigger on an empty value:
TitleWithSlugInput::make(
slugRules: [],
)
Repeater::make('FAQEntries')
->relationship()
->collapsible()
->schema([
TitleWithSlugInput::make(
fieldTitle: 'title',
fieldSlug: 'slug',
urlPath: '/faq/',
urlHostVisible: false,
titleLabel: 'Title',
titlePlaceholder: 'Insert FAQ title...',
),
]),
Place the slug in the middle of a path, e.g. /books/my-slug/detail/:
TitleWithSlugInput::make(
urlPath: '/books/',
urlVisitLinkRoute: fn(?Model $record) => $record?->slug
? '/books/' . $record->slug . '/detail'
: null,
slugLabelPostfix: '/detail/',
urlVisitLinkLabel: 'Visit Book Details',
),
TitleWithSlugInput::make(
fieldSlug: 'subdomain',
urlPath: '',
urlHostVisible: false,
urlVisitLinkLabel: 'Visit Domain',
urlVisitLinkRoute: fn(?Model $record) => $record?->slug
? 'https://' . $record->slug . '.camya.com'
: null,
slugLabel: 'Domain:',
slugLabelPostfix: '.camya.com',
),
Publish the config to set global defaults:
php artisan vendor:publish --tag="filament-title-with-slug-config"
Published at config/filament-title-with-slug.php:
[
'field_title' => 'title', // Override per-field with fieldTitle:
'field_slug' => 'slug', // Override per-field with fieldSlug:
'url_host' => env('APP_URL'), // Override per-field with urlHost:
]
All parameters are optional and use named argument syntax. Parameters marked (ignored when titleField is set) only apply to the default TextInput.
TitleWithSlugInput::make(
// Model fields
fieldTitle: 'title',
fieldSlug: 'slug',
// Custom title field — replaces the default TextInput
titleField: SomeField::make('title'),
// URL
urlPath: '/blog/',
urlHost: 'https://www.example.com',
urlHostVisible: true,
urlVisitLinkLabel: 'View',
urlVisitLinkRoute: fn(?Model $record) => $record?->slug
? route('post.show', ['slug' => $record->slug])
: null,
urlVisitLinkVisible: true,
// Title — ignored when titleField is provided
titleLabel: 'The Title',
titlePlaceholder: 'Post Title',
titleExtraInputAttributes: ['class' => 'italic'],
titleRules: ['required', 'string'],
titleRuleUniqueParameters: [
'modifyRuleUsing' => fn(Unique $rule) => $rule->where('is_published', 1),
'ignorable' => fn(?Model $record) => $record,
],
titleIsReadonly: fn($context) => $context !== 'create',
titleAutofocus: true,
// Title callbacks — work with both default TextInput and custom titleField
titleAfterStateUpdated: function ($state) {},
titleFieldWrapper: fn($field) => $field,
// Slug
slugLabel: 'The Slug:',
slugRules: ['required', 'string'],
slugRuleUniqueParameters: [
'modifyRuleUsing' => fn(Unique $rule) => $rule->where('is_published', 1),
'ignorable' => fn(?Model $record) => $record,
],
slugIsReadonly: fn($context) => $context !== 'create',
slugSlugifier: fn($string) => Str::slug($string),
slugRuleRegex: '/^[a-z0-9\-\_]*$/',
slugAfterStateUpdated: function ($state) {},
slugLabelPostfix: '/suffix',
)->columnSpan('full'),
Originally created by Andreas Scheibel (camya). Inspired by packages from awcodes and the work of spatie. Tests built with Pest.
Please see the release changelog for version history, and contributing for how to get involved. Security vulnerabilities can be reported via our security policy.
How can I help you explore Laravel packages today?