Field Types
Custom Fields comes with 20+ built-in field types and allows you to create your own custom field types for specialized functionality.
Built-in Field Types
Custom Fields includes 20+ pre-configured field types:
| Field Type | Key | Data Type | Description |
|---|---|---|---|
| Text Input | text | Text | Basic text input with validation |
email | Multi-choice | Email input with validation | |
| Phone | phone | Multi-choice | Phone number input |
| Link | link | Multi-choice | URL validation and formatting |
| Textarea | textarea | Text | Multi-line text input |
| Rich Editor | rich-editor | Text | WYSIWYG editor with formatting |
| Markdown Editor | markdown-editor | Text | Markdown syntax with preview |
| Number | number | Numeric | Numeric input with min/max validation |
| Currency | currency | Float | Currency formatting with locale support |
| Tags Input | tags-input | Multi-choice | Multiple tags with autocomplete |
| Select | select | Single-choice | Single selection dropdown |
| Multi-Select | multi-select | Multi-choice | Multiple selections |
| Radio | radio | Single-choice | Single choice radio buttons |
| Checkbox | checkbox | Boolean | Simple true/false toggle |
| Checkbox List | checkbox-list | Multi-choice | Multiple checkboxes |
| Toggle | toggle | Boolean | Switch-style toggle |
| Toggle Buttons | toggle-buttons | Single-choice | Button group selection |
| Date | date | Date | Date picker |
| Date Time | date-time | DateTime | Date and time picker |
| Color Picker | color-picker | Text | Visual color selection |
| File Upload | file-upload | String | File upload with validation |
| Record | record | Multi-choice | Polymorphic model lookup |
Creating Custom Field Types
You have flexible options for extending Custom Fields:
- Create entirely new field types for specialized functionality
- Extend existing built-in field types to add custom behavior
- Replace built-in field types by creating custom ones with the same key
This allows you to customize any field type behavior while maintaining compatibility with the existing system.
Generate a Field Type
Use the Artisan command to create a new field type:
php artisan make:field-type StarRating
This creates a complete field type class in app/Filament/FieldTypes/.
Field Type Structure
Custom field types extend BaseFieldType and use the FieldSchema system:
<?php
namespace App\Filament\FieldTypes;
use Filament\Forms\Components\Select;
use Filament\Infolists\Components\TextEntry;
use Filament\Tables\Columns\TextColumn;
use Relaticle\CustomFields\FieldTypeSystem\BaseFieldType;
use Relaticle\CustomFields\FieldTypeSystem\FieldSchema;
use Relaticle\CustomFields\Models\CustomField;
use Relaticle\CustomFields\Validation\Capabilities\MaxValueCapability;
use Relaticle\CustomFields\Validation\Capabilities\MinValueCapability;
class StarRatingFieldType extends BaseFieldType
{
public function configure(): FieldSchema
{
return FieldSchema::numeric()
->key('acme-star-rating')
->label('Star Rating')
->icon('heroicon-o-star')
->formComponent(function (CustomField $customField) {
return Select::make($customField->getFieldName())
->options([
1 => '⭐ Poor',
2 => '⭐⭐ Fair',
3 => '⭐⭐⭐ Good',
4 => '⭐⭐⭐⭐ Very Good',
5 => '⭐⭐⭐⭐⭐ Excellent',
])
->native(false);
})
->tableColumn(function (CustomField $customField) {
return TextColumn::make($customField->getFieldName())
->formatStateUsing(function ($state) {
if (!$state) return 'No rating';
return str_repeat('⭐', (int) $state) . " ($state/5)";
});
})
->infolistEntry(function (CustomField $customField) {
return TextEntry::make($customField->getFieldName())
->formatStateUsing(function ($state) {
if (!$state) return 'No rating provided';
$rating = (int) $state;
$labels = [1 => 'Poor', 2 => 'Fair', 3 => 'Good', 4 => 'Very Good', 5 => 'Excellent'];
return str_repeat('⭐', $rating) . " - {$labels[$rating]} ($rating/5)";
});
})
->priority(45)
->withValidationCapabilities(
MinValueCapability::class,
MaxValueCapability::class,
);
}
}
Register Your Field Type
Register your custom field type in your Filament Panel Provider:
<?php
namespace App\Providers\Filament;
use App\Filament\FieldTypes\StarRatingFieldType;
use Filament\Panel;
use Filament\PanelProvider;
use Relaticle\CustomFields\CustomFieldsPlugin;
class AdminPanelProvider extends PanelProvider
{
public function panel(Panel $panel): Panel
{
return $panel
->plugins([
CustomFieldsPlugin::make()
->registerFieldTypes([
StarRatingFieldType::class,
]),
]);
}
}
Extending and Replacing Field Types
You have additional flexibility with custom field types:
- Replace built-in field types: Create a custom field type with the same key as a built-in type (e.g.,
file-upload) to completely override it with your own implementation and behavior
This allows you to customize or completely replace any built-in field type behavior while maintaining system compatibility.
FieldSchema API
The FieldSchema class provides a fluent API for configuring field types. Choose the appropriate factory method based on your field's data type:
Data Type Factory Methods
// Text-based fields
FieldSchema::text() // For TEXT data type (long text)
FieldSchema::string() // For STRING data type (short text)
// Numeric fields
FieldSchema::numeric() // For NUMERIC data type (integers)
FieldSchema::float() // For FLOAT data type (decimals)
// Date/time fields
FieldSchema::date() // For DATE data type
FieldSchema::dateTime() // For DATE_TIME data type
// Boolean fields
FieldSchema::boolean() // For BOOLEAN data type
// Choice fields
FieldSchema::singleChoice() // For SINGLE_CHOICE data type
FieldSchema::multiChoice() // For MULTI_CHOICE data type
Configuration Methods
->key('field-key') // Unique field identifier
->label('Field Label') // Display name
->icon('heroicon-o-star') // Icon for field type
->priority(50) // Sort order (lower = first)
->formComponent($component) // Form field component
->tableColumn($column) // Table column component
->tableFilter($filter) // Table filter component
->infolistEntry($entry) // Read-only display component
->withValidationCapabilities(...) // Validation capabilities users can configure per field
->defaultValidationRules($rules) // Validation rules always applied to this field type
->searchable() // Enable globally-searchable in tables (default: true)
->sortable() // Enable column sorting in tables (default: true)
->filterable() // Enable table filter for this field type (default: false)
->encryptable() // Allow users to enable encryption for this field
filterable() and tableFilter() for filters to appear in tables. filterable() enables the capability; tableFilter() provides the filter component.Settings
Add type-specific configuration options that appear in the field editor when your field type is selected. Settings are stored per custom field instance.
withSettings() takes two arguments:
- A Spatie Laravel Data class defining the settings structure
- An array of Filament form components (or a
Closurereturning them) for the settings UI
use Spatie\LaravelData\Data;
use Filament\Forms\Components\TextInput;
class CurrencySettings extends Data
{
public function __construct(
public string $currencySymbol = '$',
public int $decimalPlaces = 2,
) {}
}
// In your field type's configure() method:
return FieldSchema::float()
->key('acme-currency')
->label('Currency')
->withSettings(CurrencySettings::class, [
TextInput::make('currency_symbol')
->label('Currency Symbol')
->default('$'),
TextInput::make('decimal_places')
->label('Decimal Places')
->numeric()
->default(2),
]);
The settings form components are automatically shown/hidden based on the selected field type. Access stored settings via $customField->settings in your component closures.
Multi-Value & Constraints
Control whether users can store multiple values in a single field and enforce uniqueness.
->supportsMultiValue()
->supportsUniqueConstraint()
->defaultItemValidationRules($rules)
->requiresLookupType()
supportsMultiValue() -- Shows an "Allow Multiple Values" toggle in the field editor. When enabled by the user, the field accepts multiple values (e.g., multiple emails or phone numbers) and a "Max Values" input appears. Used by Email, Phone, Link, and Record field types.
supportsUniqueConstraint() -- Shows a "Unique per Entity Type" toggle in the field editor. When enabled by the user, a UniqueCustomFieldValue validation rule is applied to prevent duplicate values across records of the same entity type. Used by Email, Phone, Link, Text, Textarea, and Number field types.
defaultItemValidationRules(array $rules) -- Validation rules automatically applied to each individual item in a multi-value field. These are not user-configurable -- they are hardcoded per field type. Only available for MULTI_CHOICE data types; throws InvalidArgumentException otherwise.
requiresLookupType() -- Replaces the user-defined options UI with an entity type selector. The field stores references to records of the selected entity type instead of static option values. Import/export treats these as entity references. Currently used by the Record field type.
Example -- the Email field type combines these to support multiple unique emails with per-item validation:
return FieldSchema::multiChoice()
->key('email')
->supportsMultiValue()
->supportsUniqueConstraint()
->withArbitraryValues()
->withoutUserOptions()
->defaultItemValidationRules(['email', 'max:254']);
Import Customization
Control how field values appear in import templates and how raw import data is transformed before storage.
->importExample('99.99')
->importTransformer(function (mixed $state): ?float {
// Transform raw import value into the correct storage format
})
importExample(string $example) -- Displayed as a sample value in the import template UI, helping users understand the expected format for this field type.
importTransformer(Closure $transformer) -- Receives the raw value from the import file and returns the transformed value for storage. Without a transformer, the raw value is stored as-is. The closure signature is function (mixed $state): mixed.
Example -- the Currency field type strips formatting characters on import:
return FieldSchema::float()
->key('currency')
->importExample('99.99')
->importTransformer(function (mixed $state): ?float {
if (blank($state)) {
return null;
}
if (is_string($state)) {
$state = preg_replace('/[^0-9.-]/', '', $state);
}
return round(floatval($state), 2);
});
Component Types
You can define components in two ways:
1. Class References (Simple)
For basic components, reference Filament classes directly:
->formComponent(TextInput::class)
->tableColumn(TextColumn::class)
->infolistEntry(TextEntry::class)
2. Closures (Flexible)
For customized components, use closures that return configured components:
->formComponent(function (CustomField $customField) {
return TextInput::make($customField->getFieldName())
->label($customField->name)
->maxLength(255);
})
Choice Fields and Options
Choice fields (select, radio, checkboxes) can handle options in different ways:
User-Defined Options (Default)
Users define their own options when creating fields:
return FieldSchema::singleChoice()
->key('priority-level')
->formComponent(function (CustomField $customField) {
return Select::make($customField->getFieldName());
// Options automatically applied by the system
});
Built-in Options
Field type provides predefined options:
return FieldSchema::singleChoice()
->key('priority-level')
->withoutUserOptions() // Disable user options
->formComponent(function (CustomField $customField) {
return Select::make($customField->getFieldName())
->options([
1 => 'Low',
2 => 'Medium',
3 => 'High',
4 => 'Critical',
]);
});
Arbitrary Values
Accept both predefined and new user-typed values:
return FieldSchema::multiChoice()
->key('product-tags')
->withArbitraryValues() // Allow new values
->formComponent(function (CustomField $customField) {
return TagsInput::make($customField->getFieldName());
});
Data Types
Custom Fields supports these data types for storage optimization and validation compatibility:
enum FieldDataType: string
{
case STRING = 'string'; // Short text, URLs, identifiers
case TEXT = 'text'; // Long text, rich content, markdown
case NUMERIC = 'numeric'; // Integers, counts
case FLOAT = 'float'; // Decimal numbers, currency
case DATE = 'date'; // Date only
case DATE_TIME = 'date_time'; // Date with time
case BOOLEAN = 'boolean'; // True/false, checkboxes, toggles
case SINGLE_CHOICE = 'single_choice'; // Select, radio buttons
case MULTI_CHOICE = 'multi_choice'; // Multi-select, checkbox lists, tags
}
Best Practices
- Use Existing Filament Components: Build on Filament's components like
Select,TextInput, etc. - Follow Naming Conventions:
- Use
kebab-casefor keys (e.g.,star-rating,country-select) - Use a project prefix for custom types (e.g.,
acme-star-rating) to avoid conflicts with built-in types - Only skip the prefix when intentionally replacing a built-in type
- Use
- Choose the Right Data Type: Select the data type that matches how your field's values should be stored
- Use Closures for Flexibility: For complex components, use closure-based definitions
- Test Your Components: Ensure your field type works in forms, tables, and infolists
- Consider Validation: Only allow validation rules that make sense for your field type
Field Type Priority
Field types are ordered by priority (lower numbers appear first):
- 10-20: Common text fields
- 30-40: Selection fields
- 50-60: Specialized fields
- 70+: Advanced fields
Troubleshooting
Field Type Not Appearing
If your custom field type doesn't appear in the dropdown:
- Ensure your field type class extends
BaseFieldType - Verify the field type is registered in your panel provider
- Clear Laravel's cache:
php artisan cache:clear - Check that the
configure()method returns a validFieldSchema
Components Not Rendering
If your components don't render correctly:
- Verify you're using
$customField->getFieldName()for field names - For closure-based components, ensure closures return valid Filament components
- Test with simple components first before adding complexity
- Check that you're importing all necessary Filament component classes
Your custom field type will now appear in the field type dropdown when creating new custom fields!