zvizvi/user-fields
zvizvi/user-fields is a Laravel package for defining custom user profile fields without altering your users table. It provides traits and configuration to attach field values to users, validate input, and manage dynamic user fields with minimal setup.
Install via Composer and let Filament discover the plugin:
composer require zvizvi/user-fields
No plugin registration is typically required for Filament v3+ (the service provider is auto-discovered), but double-check composer.json for the extra.laravel.providers entry if components aren't showing up. If the package ships a Filament plugin class, register it in your panel provider:
->plugins([
\Zvizvi\UserFields\UserFieldsPlugin::make(),
])
Where to look first: the src/ directory — this is a small package, so the component classes (UserSelect, UserColumn, UserFilter) are your real documentation. The README is thin (20 stars), so treat the source as authoritative.
First use case: replace a Select::make('user_id')->relationship('user', 'name') in a form with the package's user-select component so the dropdown renders avatars next to names:
use Zvizvi\UserFields\Components\UserSelect;
UserSelect::make('user_id')
->relationship('user', 'name')
Then do the same in a table column and filter. That upgrade path — Select → Column → Filter — is the natural adoption order.
Form fields. Use the user-select component anywhere you'd use a relationship select tied to a user model. Keep the relationship() call intact; the package decorates the option rendering, it doesn't replace the relationship logic. Pass searchable(), preload(), and multiple() exactly as you would on a native Select — those options pass through.
Table columns. Swap TextColumn::make('user.name') for the package's user column so rows show a small avatar beside the name. This is where the package earns its keep: dense tables where names alone are ambiguous (multiple "John Smith"s) become scannable at a glance.
Filters. The filter component renders user options with avatars in the filter dropdown, which matters on admin dashboards where support staff pick assignees by sight. Wire it the same way as a standard SelectFilter::make('user_id')->relationship(...).
Centralize the "who is a user" decision. Rather than repeating relationship('user', 'name') everywhere, define a small helper or use the package's static configuration (avatar column, name column) once. If your User model stores the avatar on a column other than the Filament default (avatar_url), configure that in one place so all three components inherit it.
Combine with search. Avatars cost render time; always pair the select and filter with ->searchable() so large user tables don't try to paint hundreds of avatars in one request. In tables, rely on pagination — the column renders only the visible page.
Resource-wide adoption. The cleanest integration is to alias the package classes in your own app namespace (e.g. App\Filament\Components\UserSelect) so you can swap the underlying implementation later without touching every resource.
Avatar source. The package almost certainly reads from Filament\Models\Contracts\HasAvatar or a conventional avatar_url attribute. If avatars render blank, check that your User model implements the avatar contract and that the getFilamentAvatarUrl() method returns a real, publicly reachable URL — storage disk misconfiguration (private disk, missing storage:link) is the #1 cause of broken avatars in Filament.
N+1 queries. Rendering avatars in table columns can trigger a query per row if the relationship isn't eager-loaded. Add ->relationship('user', 'name') on the column or call ->modifyQueryUsing(fn ($q) => $q->with('user')) on the table query. Watch query counts in debugbar after switching a column over.
Small-package risk. With ~20 stars and a single maintainer, pin the version in composer.json and be prepared to copy the component classes into your app if maintenance stalls. They're small enough to fork; keeping your own aliases (see above) makes that swap painless.
Filament version coupling. Filament's form/table component APIs shifted between v2 and v3 (and again into v4). Confirm the installed major version matches what the package targets — a component that renders in one panel may throw BadMethodCallException on another. If you run multi-panel apps, test in each.
Debugging empty options. If the dropdown or filter shows no users, the problem is nearly always the relationship() name or the title column on the related model, not the package. Fall back to a plain Filament Select with the same configuration to isolate whether the plugin or the relationship is at fault.
Extension point. The components extend native Filament components, so ->extraAttributes(), ->placeholder(), ->disabled(), and other fluent methods chain through. Prefer extending via these public methods over subclassing — subclassing pins you to internal method signatures the maintainer may change between minor releases.
Performance ceiling. Avatars in filters load all option markup upfront. Above roughly a few hundred users, switch the filter to a searchable-only variant or accept the slower initial paint; there's no lazy-loading of avatar images beyond normal browser behavior.
How can I help you explore Laravel packages today?