Weave 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

Filament Title With Slug Laravel Package

remihin/filament-title-with-slug

A Filament form field that pairs a title input with an auto-generated, editable slug. It keeps the slug in sync as you type, lets you tweak it manually, and helps ensure clean, unique URLs for your Laravel models.

View on GitHub
Deep Wiki
Context7

Getting Started

remihin/filament-title-with-slug is a single Filament form component, not a panel plugin. There is no service provider registration, no panel config entry, and no publish step required to get value out of it — it is a drop-in replacement for the title + slug TextInput pair on a resource form.

  1. Install: composer require remihin/filament-title-with-slug
  2. Check your Filament major. The v2 and v3 lines expose different class names and named arguments. Match the package constraint to the filament/filament version already in your lockfile before you write code — this is the single most common reason a copied README snippet fails.
  3. Confirm the data contract. The model needs real title and slug columns. slug should be indexed (unique where the domain allows), and if you resolve records by slug on the front end, getRouteKeyName() should return 'slug'.
  4. Swap the fields:
use RemiHin\FilamentTitleWithSlug\FilamentTitleWithSlugInput;

public static function form(Form $form): Form
{
    return $form->schema([
        FilamentTitleWithSlugInput::make(
            fieldTitle: 'title',
            fieldSlug: 'slug',
            urlPath: '/posts/',
            urlHost: config('app.url'),
        ),
    ]);
}

Where to look first: the README usage block for the exact named arguments on your Filament version, then the package's Blade view in vendor/ the moment you need to change markup or debug why the slug stopped updating. Publish views and translations before customising so your diff stays reviewable across upgrades.

Best first use case: a low-traffic internal resource — a Pages or Categories model with existing, already-populated slugs. You get the live URL preview and auto-derivation with a trivial blast radius if the preview formatting is wrong.

Release 1.0.2 is a patch on top of 1.0.1 with a changelog containing only a compare link — treat it as a safe upgrade for existing usage, but read the diff yourself if slug normalisation matters to you.

Implementation Patterns

Wrap it in a shared factory. You will use this component on more than one resource. Put the conventions in one place — a method on a base resource class or a trait — so the URL preview format, labels, and column span are consistent:

protected static function titleWithSlug(string $path): FilamentTitleWithSlugInput
{
    return FilamentTitleWithSlugInput::make(
        fieldTitle: 'title',
        fieldSlug: 'slug',
        urlPath: $path,
        urlHost: config('app.url'),
    )->columnSpanFull();
}

Then static::titleWithSlug('/posts/') in each form().

It works anywhere a Form schema works. Resource create/edit pages, custom pages, and modal forms on table actions. The only requirement is that the underlying Livewire component has title and slug as form state — on a custom page that means your $data array keys must match fieldTitle / fieldSlug.

Keep the slug column as an ordinary attribute. Because the component is presentation-only, your table columns, filters, search, and route binding all keep working with plain TextColumn::make('slug'). Don't reach for anything custom on the read side.

Own uniqueness explicitly. The component generates a candidate string; it does not enforce domain rules. Pair it with Rule::unique('posts', 'slug')->ignore($record) plus a database unique index. Concurrent admin saves are rare but they are the failure mode that produces a 500 instead of a form error.

Decide regeneration policy per resource. Existing slugs are preserved so published URLs don't silently break — that's the right default for SEO, but it means renaming a post does not change its URL. If a resource genuinely needs the slug to follow the title (draft-only content, internal tools), make that an explicit, documented choice rather than assuming the component does it.

Multi-locale: one component per locale. Don't try to reuse a single field across translations. Instantiate with distinct field names (slug_en, slug_fr) alongside their title counterparts and keep the uniqueness rule scoped to the locale.

Testing. The auto-derivation is browser-side, so fillForm(['title' => 'My Post']) in a Livewire test will not produce a slug. Set slug explicitly in test and factory data, and reserve your component tests for the rendered markup and the manual-override path.

Gotchas and Tips

  • Version coupling is the main trap. "Class not found" or "unknown named parameter" almost always means you're on the other Filament major from the README snippet. Pin the constraint in composer.json and check the upgrade notes before bumping Filament — a small package typically lags a Filament major release.
  • Generation is client-side (Alpine/Livewire). A field inside a ->hidden() section, a collapsed repeater, or a conditionally-rendered tab may never fire the auto-fill. If the slug stays empty, that's the first thing to check — not the PHP.
  • Typos fail silently. If fieldTitle / fieldSlug don't match your actual form state keys, nothing throws; the slug simply never populates. Inspect the rendered markup for the Alpine bindings and the wire:model names before digging deeper.
  • Don't hang validation on the composite component. Rules applied to the wrapper don't reliably propagate to the inner inputs. Put required, maxLength, and uniqueness on the underlying fields or in a FormRequest, and test the failing path — not just the happy path.
  • urlHost is not inferred from the panel. On staging, multi-tenant installs, or a separate admin domain, the preview will lie until you pass an explicit host. A wrong preview is cosmetic but it erodes editor trust in the field quickly.
  • Slug changes break bookmarks. The package has no opinion here. If editors routinely rename slugs, add an alias/redirect table rather than letting old URLs 404 — that's application work, and it belongs in the same PR as the rollout.
  • Disabled/readonly on edit: verify the existing slug is still dehydrated and submitted. Check the Livewire payload before shipping; a disabled component that drops its value silently nulls slugs on save.
  • Non-Latin titles are the under-tested edge. Transliteration of accented, CJK, and emoji-bearing titles can produce empty or mangled ASCII slugs that still "succeed." Build a small fixture set (Latin, accented, CJK, very long, duplicate) and check the output before you trust it broadly.
  • Don't split authority. Pick one generator: the component or a model observer. Running both means the admin panel and programmatic creation produce different slugs, and you'll spend an afternoon on a bug that looks like caching.
  • Publish early, diff small. Views and translations are upgrade-sensitive. Fork or publish before customising, keep the diff minimal, and re-check after each release. Given the surface area, vendoring the package into your own namespace is genuinely cheap and removes the single-maintainer escalation risk.
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.
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core
nodisrupt/laravel-agent
sierratecnologia/media-manager