Een Filament custom field is een PHP-class die Field extendt plus één Blade view. Een custom page is een volledige Livewire-component in je panel. Met vijf bouwstenen kom je ver: custom fields, custom pages, Livewire-componenten, render hooks en een eigen theme. Een plugin is pas zinvol als je...
Een Filament custom field is een PHP-class die Field extendt plus één Blade view. Een custom page is een volledige Livewire-component in je panel. Met vijf bouwstenen kom je ver: custom fields, custom pages, Livewire-componenten, render hooks en een eigen theme. Een plugin is pas zinvol als je het in drie of meer projecten hergebruikt.
Wanneer heb je maatwerk in Filament nodig?
Je hebt maatwerk nodig zodra de standaardcomponenten je proces niet meer volgen. Dat gebeurt later dan de meeste teams denken.
Filament levert tientallen velden, kolommen en layoutcomponenten. Text inputs, selects, repeaters, builders, file uploads, rich editors. In onze uitleg over resources, forms en tables zie je hoe ver je komt met alleen configuratie.
Maar elke backoffice heeft een paar plekken waar het knelt. Concreet:
- Een veld dat data uit een externe API toont terwijl je typt (adres, KvK, voorraad).
- Een scherm dat geen CRUD is, zoals een importwizard of een planbord.
- Een banner die op elke pagina moet staan, bijvoorbeeld "je werkt in de staging-omgeving".
- Een huisstijl die verder gaat dan een primaire kleur.
Onze eigen backoffice draait op Filament. Blog, leads, afspraken, cases en een database met Magento-shops. Die is vrijwel volledig standaard Filament, en dat is precies goed: maatwerk bouw je pas als de standaard echt tekortschiet.
Welke uitbreidingsmogelijkheden heeft Filament?
Filament heeft vijf officiële uitbreidingspunten, elk voor een ander probleem. Kies het lichtste dat werkt.
| Bouwsteen | Wat het is | Gebruik het voor | Artisan-commando |
|---|---|---|---|
| Custom form field | Class + Blade view die state opslaat | Afwijkende invoer: kaart, kleurkiezer, API-lookup | make:filament-form-field |
| Custom schema component | Class + view zonder eigen state | Weergave binnen een form of infolist | make:filament-schema-component |
| View / Livewire in schema | Blade view of Livewire-component in een form | Grafiek, preview, losse interactie | n.v.t. (View::make, Livewire::make) |
| Custom page | Volledige Livewire-pagina in je panel | Wizards, rapportages, instellingen, planborden | make:filament-page |
| Render hook | Injectie van HTML op vaste plekken in de layout | Banners, scripts, extra knoppen in topbar | n.v.t. (registerRenderHook) |
| Custom theme | Eigen Tailwind CSS-build voor je panel | Huisstijl, eigen Tailwind-classes in views | make:filament-theme |
De volgorde in de tabel is ook de volgorde van complexiteit. Een render hook is tien regels. Een custom field met Alpine.js en een externe API is een dag werk, inclusief tests.
Hoe maak je een Filament custom field?
Je genereert een class die Filament\Forms\Components\Field extendt en koppelt die aan een Blade view. De view regelt de binding met Livewire-state.
Het commando:
php artisan make:filament-form-field LocationPicker
Dat levert een class op in App\Filament\Forms\Components:
namespace App\Filament\Forms\Components;
use Filament\Forms\Components\Field;
class LocationPicker extends Field
{
protected string $view = 'filament.forms.components.location-picker';
}
En een view. De wrapper zorgt dat label, helper text en validatiefouten er hetzelfde uitzien als bij elk ander veld:
<x-dynamic-component
:component="$getFieldWrapperView()"
:field="$field"
>
<div x-data="{ state: $wire.{{ $applyStateBindingModifiers("\$entangle('{$getStatePath()}')") }} }">
<input x-model="state" />
</div>
</x-dynamic-component>
Drie functies doen het werk:
$getStatePath()geeft het pad naar de Livewire-property waar de waarde in staat.$applyStateBindingModifiers()respecteert->live()of debounce-instellingen uit je PHP-config.$wire.$entangle()koppelt Alpine.js-state aan Livewire, zodat je client-side kunt werken zonder elke toetsaanslag naar de server te sturen.
Wat maakt een custom field goed?
Een goed custom field gedraagt zich als een native veld. Validatie, ->required(), ->disabled() en ->hidden() moeten gewoon werken. Dat krijg je gratis door Field te extenden en de wrapper te gebruiken.
Wat wij in de praktijk toevoegen:
- Configuratiemethodes in fluent stijl, bijvoorbeeld
->defaultZoom(12). Sla de waarde op alsint | Closureen lees hem uit via$this->evaluate(). Dan werken closures met$geten$recordook. - Geen zware JavaScript inline. Laad externe libraries via Filament's asset-systeem, niet via een
-tag in de view. - Eén test die het veld rendert en een waarde opslaat. Meer daarover in onze gids voor Filament testen met Pest.
Custom field of schema component?
Gebruik een field als de gebruiker een waarde invoert die je opslaat. Gebruik een schema component als je alleen iets toont. Een kaart met de locatie van een order is een component. Een kaart waarop je de locatie aanklikt is een field.
Hoe maak je een Filament custom page?
Een custom page is een full-page Livewire-component met Filament-extra's: navigatie, titel, header actions en widgets. Je maakt hem met één commando.
php artisan make:filament-page Settings
Je krijgt een class in app/Filament/Pages en een Blade view. Filament registreert de page automatisch in de navigatie van je panel.
Omdat het een Livewire-component is, schrijf je er gewone Livewire-code in. Public properties, methodes, wire:click. Daarbovenop krijg je:
$title,$navigationLabel,$slug,$headingen$subheadingom de pagina te configureren.getHeaderActions()voor knoppen rechtsboven, met modals en bevestigingen.getHeaderWidgets()engetFooterWidgets()voor statistieken en grafieken.canAccess()voor autorisatie.
Dat laatste vergeten teams vaak. Een custom page is standaard zichtbaar voor iedereen die in het panel mag:
public static function canAccess(): bool
{
return auth()->user()->canManageSettings();
}
Koppel dit aan je policies of aan Filament Shield, niet aan losse if-statements per pagina.
Wanneer een custom page en geen resource?
Een resource is voor CRUD op één model. Een custom page is voor alles wat daar niet in past. Typische voorbeelden uit backoffices die wij bouwen:
- Een instellingenpagina die naar een settings-tabel of config schrijft.
- Een importwizard met preview en foutrapport.
- Een rapportage die data uit drie modellen combineert.
- Een planbord voor magazijn of afspraken.
Je kunt in een custom page ook gewoon Filament-forms en -tables gebruiken. Dan heb je de kracht van Filament zonder het keurslijf van een resource. Bij dashboards met KPI's zie je hetzelfde patroon met widgets.
Hoe gebruik je Livewire-componenten in een Filament panel?
Je zet een bestaande Livewire-component direct in een form of infolist met Livewire::make(). Dat is de snelste route als je de component al hebt.
use App\Livewire\Chart;
use Filament\Schemas\Components\Livewire;
Livewire::make(Chart::class, ['bar' => 'baz'])
->key('chart-unique-id')
->lazy()
Let op twee dingen:
- Alleen serialiseerbare data. De geneste component rendert apart. Je geeft ID's mee, geen Eloquent-collecties met relaties.
->lazy()voor zware componenten. De rest van het formulier laadt dan eerst. Bij een grafiek met een zware query scheelt dat merkbaar in laadtijd.
Wil je alleen een stuk HTML tonen, zonder eigen interactie? Gebruik dan Filament\Schemas\Components\View met View::make('...')->viewData([...]). In die view heb je toegang tot $get() en $record. Geen extra Livewire-roundtrip nodig.
Sinds v4 zitten forms en infolists samen in het Schemas-package. Daarom staan deze componenten onder Filament\Schemas\Components. Upgrade je van v3? Dan verandert hier je namespace. Zie ons upgradestappenplan van Filament 3 naar 5.
Livewire zelf werkt in een Filament-panel precies zoals daarbuiten. Alles wat we schreven over Livewire voor B2B-dashboards geldt hier ook.
Wat zijn render hooks in Filament?
Render hooks zijn vaste plekken in de layout waar je eigen HTML injecteert. Je raakt geen Filament-views aan, dus je upgrades blijven schoon.
use Filament\Support\Facades\FilamentView;
use Filament\View\PanelsRenderHook;
use Illuminate\Contracts\View\View;
FilamentView::registerRenderHook(
PanelsRenderHook::BODY_START,
fn (): View => view('impersonation-banner'),
);
Je registreert dit in een service provider. Bruikbare hooks:
PanelsRenderHook::BODY_STARTvoor banners bovenaan elke pagina.PanelsRenderHook::GLOBAL_SEARCH_BEFOREvoor iets naast de zoekbalk in de topbar.PanelsRenderHook::USER_MENU_BEFOREvoor een knop vóór het gebruikersmenu.PanelsRenderHook::PAGE_STARTvoor content boven één pagina.
Met de scopes-parameter beperk je een hook tot specifieke pagina's of hele resources. Handig voor een waarschuwing op alleen de edit-pagina van gebruikers.
Wat wij er in de praktijk mee doen: een omgevingsbanner op staging, een impersonation-melding, een link naar de webshop-frontend in de topbar. Kleine dingen. Maar het alternatief is Filament-views publiceren en overschrijven. Dat breekt bij elke major release.
Hoe maak je een custom theme in Filament met Tailwind v4?
Een custom theme is een eigen CSS-build voor je panel. Filament v4 en v5 gebruiken Tailwind CSS v4; v5 vereist v4.1 of hoger.
Waarom heb je het nodig? Filament's standaard-CSS bevat alleen de Tailwind-classes die Filament zelf gebruikt. Schrijf je in een custom field of page een class als grid-cols-7, en gebruikt Filament die nergens? Dan werkt hij niet. Een custom theme lost dat op.
Het commando:
php artisan make:filament-theme admin
Dat maakt resources/css/filament/admin/theme.css aan met de Tailwind-imports en @source-regels. Die @source-regels vertellen Tailwind waar het naar classes moet zoeken:
@source '../../../../app/Filament/**/*';
@source '../../../../resources/views/filament/**/*';
Daarna twee stappen:
- Voeg het bestand toe aan
inputinvite.config.js. - Registreer het in je panel provider met
->viteTheme('resources/css/filament/admin/theme.css').
Bouw je views buiten deze mappen? Voeg dan een extra @source-regel toe. Dit is de meest voorkomende oorzaak van "mijn styling werkt niet".
Voor gerichte aanpassingen gebruik je de CSS hook classes. Filament geeft elementen classes met prefix fi-. Die target je in je theme in plaats van Filament-views te overschrijven. Kleuren en fonts stel je in via de panel-configuratie, daar heb je geen theme voor nodig.
Wie Tailwind kent uit Hyvä, herkent het patroon. In ons artikel over Hyvä Tailwind-theming speelt hetzelfde probleem met content-paden.
Maatwerk of plugin: wat kies je?
Kies een bestaande plugin als die 80% van je behoefte dekt en actief onderhouden wordt. Bouw zelf als het om je kernproces gaat of als je de plugin moet forceren.
| Situatie | Keuze | Waarom |
|---|---|---|
| Generiek probleem (rollen, backups, logs) | Bestaande plugin | Opgelost door anderen, onderhoud gedeeld |
| Plugin dekt 80%, rest via config | Plugin | Minste code |
| Plugin dekt 60% en je moet hem overschrijven | Maatwerk | Je erft de complexiteit zonder controle |
| Kernproces (orderflow, planning, pricing) | Maatwerk | Je wilt dit zelf in handen hebben |
| Eigen component in 3+ projecten | Eigen plugin/package | Hergebruik loont pas vanaf dan |
| Plugin zonder v5-release | Maatwerk of wachten | Blokkeert je upgrades |
Dat laatste punt weegt zwaar. Elke plugin is een afhankelijkheid bij een major upgrade. Filament v5 bracht geen API-wijzigingen ten opzichte van v4, maar plugins moesten wel hun versie-constraints bijwerken. Check vóór je installeert wanneer de laatste release was. Ons overzicht van de beste Filament-plugins helpt daarbij.
De rekenstap. Stel: een custom field kost 6 uur bouwen en 1 uur per jaar onderhoud. Een betaalde plugin kost een licentie per project, maar je moet hem ook updaten en testen. Over drie jaar is het verschil vaak kleiner dan je denkt. De echte vraag is: wie lost het op als het breekt?
Samengevat
- Begin standaard. Het merendeel van een goede backoffice is configuratie, geen maatwerk.
- Custom field als je afwijkende invoer opslaat. Extend
Field, gebruik de wrapper. - Schema component of
View::makeals je alleen iets toont. Livewire::makeals je al een Livewire-component hebt. Lazy laden bij zware queries.- Custom page voor alles wat geen CRUD is. Vergeet
canAccess()niet. - Render hooks in plaats van views overschrijven. Je upgrades blijven schoon.
- Custom theme zodra je eigen Tailwind-classes gebruikt. Controleer je
@source-paden. - Plugin alleen als hij 80% dekt en actief onderhouden wordt.
Meer over het framework zelf lees je op onze Filament-pagina. Bouw je een portaal voor klanten in plaats van een interne backoffice? Kijk dan bij klantportalen op Laravel.
Veelgestelde vragen
Hoe maak ik een custom field in Filament?
Draai php artisan make:filament-form-field NaamVanVeld. Je krijgt een class die Filament\Forms\Components\Field extendt en een Blade view. In de view wikkel je je HTML in $getFieldWrapperView() en bind je de waarde via $getStatePath() of $wire.$entangle().
Wat is het verschil tussen een custom page en een resource in Filament?
Een resource is CRUD voor één Eloquent-model: list, create, edit en view. Een custom page is een vrije Livewire-pagina in je panel. Gebruik een custom page voor instellingen, wizards, rapportages en alles wat niet op één model past.
Waarom werken mijn Tailwind-classes niet in een Filament custom view?
Filament's standaard-CSS bevat alleen de classes die Filament zelf gebruikt. Maak een custom theme met php artisan make:filament-theme en zorg dat je views in een @source-pad staan. Registreer het theme met ->viteTheme() en draai een nieuwe Vite-build.
Kan ik een bestaande Livewire-component in Filament gebruiken?
Ja. Met Filament\Schemas\Components\Livewire::make() zet je hem in een form of infolist. Je kunt parameters meegeven aan mount() en de component lazy laden. Geef alleen serialiseerbare data mee, omdat de component apart rendert.
Moet ik Filament-views overschrijven om de layout aan te passen?
Liever niet. Gebruik render hooks om HTML op vaste plekken te injecteren en CSS hook classes (prefix fi-) voor styling. Overschreven views moet je bij elke major upgrade opnieuw nalopen.
Wanneer bouw ik een eigen Filament-plugin?
Pas als je dezelfde component in drie of meer projecten gebruikt. Daarvoor is een gewone class in je app eenvoudiger te onderhouden. Een plugin voegt versiebeheer, packaging en compatibiliteitswerk toe.
Twijfel je of iets maatwerk vraagt of dat Filament het standaard kan? Neem contact op, dan kijken we er samen naar.

Geschreven door Ruthger Idema
15+ jaar ervaring in e-commerce development. Gespecialiseerd in Magento, Shopify en Laravel maatwerk.
Meer over ons team →