Components Reference
Complete API reference for custom form components shipped with Filament Flex Fields (janczakb/filament-flex-fields).
This document covers custom UI components (form fields, table columns, and layout/schema components). Standard Filament fields (TextInput, Select, etc.) are mapped via FieldType and are not described here.
Table of contents
Part I — Shared concepts
- Overview
- Documentation conventions
- Control size
- Inherited Filament field API
- Assets & playground
- Rich card option shape
- Rich select option shape
- Dual listbox option shape
Part II — Components
- FlexTextInput
- FlexTextareaField
- SelectField
- UserSelect
- UserColumn
- DualListboxField
- PriceRangeField
- CreditCardField
- PhoneField
- SignatureField
- MapPickerField
- AddressAutocompleteField
- ChoiceCards
- ChoiceCheckboxCards
- FlexChecklist
- FlexRadiolist
- MatrixChoiceField
- SwitchField
- CurrencyField
- CountryField
- TimezoneField
- Date & time fields
- FlexVerificationCode
- AudioField
- VoiceNoteRecorderField
- VideoField
- FlexFileUpload & FlexImageUpload
- ColorSwatchField
- FlexColorPickerField
- NumberStepper
- FlexSlider
- SegmentTabs
- SegmentControl
- TrackSlider
- TrafficSplit
- RatingField
- RatingColumn
- IconColumn
- CoverCard
- ProgressBar
- ProgressCircle
- ItemCard
- ItemCardGroup
- ItemCardStack
- Layout components — quick comparison
- Form layout patterns
- SlugField & TitleSlugField
- TranslatableFields
Part I — Shared concepts
Overview
Filament Flex Fields provides modern SaaS-inspired form controls with a unified design language:- Shared size scale (
sm,md,lg) - Shared CSS tokens (
--fff-*inresources/css/base.cssand modular bundles underresources/css/) - Filament field wrapper integration (labels, validation errors, helper text)
- Optional playground page for visual QA
Filament\Forms\Components\Field, or extend a native Filament input (TextInput, Textarea, Select) while replacing the view with a styled package template.
Documentation conventions
Each component section documents four layers of API surface:| Layer | Description |
|---|---|
| Chainable config methods | Fluent ->method() calls on the component class. Accept scalars, Closure, or Filament utility injection. |
| Inherited APIs | Methods from parent Filament classes (Field, TextInput, Select, traits) that work unchanged. Listed briefly where relevant. |
| Public helper methods | Non-chainable getters and utilities used by Blade views, tests, or custom extensions. Documented per component when they expose stable behaviour. |
| FlexField schema keys | Snake-case keys in field config arrays passed to FlexFieldFormBuilder. Map to chainable methods. Keys not yet wired in the builder may still be valid when configuring fields manually. |
Control size
Most components accept asize() method.
| Value | Enum constant | Track height |
|---|---|---|
sm | ControlSize::Sm | 32px |
md | ControlSize::Md | 40px (default) |
lg | ControlSize::Lg | 48px |
config/filament-flex-fields.php under the ui key, for example:
| Config key | Affects |
|---|---|
flex_text_input_size | FlexTextInput |
flex_text_input_variant | FlexTextInput |
flex_textarea_size | FlexTextareaField |
flex_textarea_variant | FlexTextareaField |
select_size | SelectField |
select_variant | SelectField |
dual_listbox_size | DualListboxField |
dual_listbox_variant | DualListboxField |
price_range_size | PriceRangeField |
price_range_variant | PriceRangeField |
credit_card_size | CreditCardField |
credit_card_variant | CreditCardField |
number_stepper_size | NumberStepper |
segment_size | SegmentControl |
slider_size | TrackSlider |
switch_size | SwitchField |
rating_size | RatingField |
choice_cards_size and choice_cards_variant when used via FlexFieldFormBuilder (add these keys to config if needed).
Inherited Filament field API
Every component in Part II inherits the standard FilamentField API. Common methods:
| Method | Description |
|---|---|
label() | Field label rendered by the Filament wrapper |
helperText() | Helper text below the field |
hint() | Visible hint text next to the label |
hintIcon() | Icon next to the label with optional tooltip |
hintIconTooltip() | Tooltip text for hintIcon() |
hintAction() | Clickable action next to the label |
placeholder() | Placeholder where applicable |
required() | Marks the field as required |
disabled() | Disables the entire field |
default() | Default state value |
live() | Live / reactive updates |
dehydrated() | Whether the value is saved to form state |
hidden() / visible() | Conditional visibility |
rule() / rules() | Additional validation rules |
afterStateUpdated() | Callback after state changes |
Closure for dynamic values and support Filament utility injection.
Livewire wire:ignore strategy (map & heavy Alpine fields)
Several interactive fields (MapPickerField, AddressAutocompleteField, SelectField, PhoneField, CountryField, and others) wrap third-party or Filament Alpine trees inside wire:ignore so Livewire does not destroy DOM that Alpine manages.
| Concern | Strategy |
|---|---|
| State sync | Pass Livewire state through $wire.$entangle('statePath') in x-data — Alpine owns the UI, Livewire owns persistence. |
| Config changes | Add a wire:key hash over read-only/disabled/config props (token, fields(), storeFormat(), size, variant). When config changes, Livewire remounts the ignored subtree with fresh Alpine boot data. |
| Server updates | Avoid ->set('data.field') on ignored fragments in tests; change upstream props or entangled state instead. |
| Dropdowns / maps | Teleport menus to body, use shared overlay coordinator (fffOverlays) so only one menu stays open. teleported-menu.css raises z-index when a Filament modal is open (:has(.fi-modal.fi-modal-open)). |
MapPickerField / AddressAutocompleteField):
$entangle for state, wire:key for remounts, x-load ES module for JS.
Assets & playground
CSS is split into bundles:| Asset ID | File | Loading |
|---|---|---|
flex-fields-core | resources/dist/css/core.css | Always loaded (tokens, switches, item cards, hold-confirm actions, shared layout) |
flex-fields-playground | resources/dist/css/playground.css | Base playground chrome only |
flex-fields-playground-{slug} | resources/dist/css/playground-{slug}.css | Per-slug playground bundle (e.g. playground-phone-field.css) |
flex-fields-{component} | resources/dist/css/{component}.css | Lazy — injected by form field blades and CoverCard via load-stylesheet → emit-assets |
JavaScript (tiered chunks)
All Alpine components are compiled together in a single esbuild build withsplitting: true. If two fields import the same module from resources/js/core/ or resources/js/support/, the code is split into a shared chunk — loaded once and cached by the browser.
| Output | Role |
|---|---|
resources/dist/components/{component}.js | Thin entry — Alpine x-data factory for the component |
resources/dist/components/flex-fields-{name}-{hash}.js | Shared modules with semantic names (e.g. flex-fields-emoji-*.js) |
resources/dist/components/alpine-manifest.json | Component-to-chunk map + __chunk_modules__ metadata |
Currently shared chunks (auto-generated from build)
| Source modules | Components | Purpose / Example |
|---|---|---|
core/shared-emoji-picker.js | flex-text-input, flex-textarea | Emoji picker (~45 KB) |
core/audio-waveform.js, format-time.js, waveform-bars.js, audio-playback.js | audio-field, voice-note-recorder-field | Waveform, playback, time formatting |
core/dynamic-bars.js | price-range, audio-field, voice-note-recorder-field | Waveform / price histogram bars |
support/mapbox-geocoding.js | map-picker, address-autocomplete | Mapbox geocoding integration |
core/searchable-select-menu.js | country-field, timezone-field, currency-field | Common dropdown overlay shell |
libphonenumber-js (lazy import()) | phone-field | Phone validation (~190 KB, loaded on demand) |
rating-field, dual-listbox) do not share code with other components — their entire logic remains inside their thin entry (which is expected).
Modules used by only a single field (e.g. core/date-time/* used only by flex-date-time-field, nouislider used only by flex-slider) remain inside their entries until another component starts importing them.
Preload & delivery
Every blade template rendering a component stylesheet registers CSS and Alpine chunks in request-scoped queues (FlexFieldStylesheetQueue, FlexFieldAlpineQueue). load-stylesheet immediately emits emit-assets:
- Full page —
@push('styles')into Filament@stack('styles')in<head>. - Livewire partial — inline
<link>/modulepreloadtags plus a hiddendata-fff-asset-batchmarker for the injector.
queued-stylesheets flushes any remaining pending() queues at STYLES_AFTER and BODY_END. At HEAD_END, critical-stylesheet-preloads may emit teleported-menu only when FlexFieldStylesheetQueue has already registered a component that depends on it (e.g. table columns in setUp()). Form fields enqueue during body render via load-stylesheet → emit-assets instead.
HoldConfirmAction preloads its Alpine entry via @push modulepreload in hold-confirm.blade.php when the action renders — not globally in HEAD_END.
Asset injector (SPA / modals)
flex-field-asset-injector.js (registered Filament JS asset at SCRIPTS_AFTER) handles:
- href deduplication (
normalizeAssetUrl, Map indices, in-flight promise cache), - loading missing CSS and Alpine chunks from morph batches,
- modal FOUC prevention (
morph.updating/morph.updated,fff-flex-fields-assets-pending/readyclasses), - protected links (
data-fff-stylesheet,data-fff-alpine-chunk,data-fff-playground-bundle).
rating-field) load their chunks dynamically via ESM import inside x-load — without explicit preloading, since their manifest entry is empty.
After modifying JavaScript (including the injector):
UserColumn, RatingColumn, IconColumn, etc.) are lazy-loaded per column via FlexFieldStylesheetQueue::enqueueFor() in the column setUp() — not via load-stylesheet in table cell blades. The queued-stylesheets partial (render hooks at STYLES_AFTER and BODY_END) flushes pending bundles once; markStylesheetsEmitted() prevents duplicates. Playground slug pages use per-slug bundles with suppressForPlaygroundBundle() so lazy CSS is not injected twice.
Playground CSS uses base playground.css plus per-slug playground-{slug}.css bundles pushed via playground-page-stylesheets.
After changing package CSS or JS:
Rich card option shape
Used by ChoiceCards and ChoiceCheckboxCards viaoptions().
Simple option
Rich option
| Key | Type | Description |
|---|---|---|
label | string | Card title. Falls back to the option key. |
description | string|null | Secondary text under the title. |
price | string|null | Price line. Alias: value. |
price_suffix | string|null | Suffix after price, e.g. /mo. Alias: suffix. |
meta | string|null | Extra footer text (monospace in UI). |
icon | string|null | Heroicon name. Rendered in media and featured layouts. |
badge | string|null | Badge label. Rendered in featured layout. |
badge_color | string | Badge color token. Default: success. |
disabled | bool | Disables this option. Combined with disabledOptions(). |
Rich select option shape
Used by SelectField when options use a rich array shape or whenrichOptions() / optionLayout('grid') is enabled.
Simple option
Rich option
| Key | Type | Description |
|---|---|---|
label | string | Option title. Falls back to the option key. |
description | string|null | Secondary line in list layout. |
icon | string|BackedEnum|Htmlable|null | Heroicon rendered in the option row. |
image | string|null | Image URL for grid layout cards. |
badge | string|null | Badge label in list layout. |
badge_color | string | Filament color token for the badge. Default: primary. |
disabled | bool | Disables this option. |
'Backend' => ['laravel' => 'Laravel', ...].
Dual listbox option shape
Used by DualListboxField viaoptions().
Simple option
Rich option
| Key | Type | Description |
|---|---|---|
label | string | Item label. Falls back to the option key. |
description | string|null | Secondary text under the label. |
disabled | bool | Disables this option. Combined with disabledOptions(). |