parfaitementweb/filament-country-field
A Filament form field for selecting countries. Adds a searchable country dropdown with flags, ISO codes, and localized names to your admin panels, making it quick and consistent to capture country data in Filament resources and forms.
The release that matters here is 2.5.6's Laravel 13 support (PR #46). Everything else in this tag is Dependabot churn on GitHub Actions (actions/checkout, ramsey/composer-install, dependabot/fetch-metadata) and has zero runtime impact. Concretely: the package widened its illuminate/* / laravel/framework constraint to include 13.x. No PHP API changed, no Filament API changed, no deprecations. If you're on Laravel 10–12 today, this release is a no-op for you; if you're planning a Laravel 13 upgrade, this package is no longer a blocker.
First steps:
composer require parfaitementweb/filament-country-field
php artisan filament:assets # or: vendor:publish --tag="filament-country-field-assets"
The second command is the one people forget. The field renders flags as bundled SVGs; without the asset publish step you get a working select with broken/empty flag tiles in production while everything looks fine locally (dev server often resolves the vendor path).
First use case — a single country column:
use Parfaitementweb\FilamentCountryField\Forms\Components\CountryField;
CountryField::make('country')
->searchable()
->required();
The state is the ISO 3166-1 alpha-2 code (BE, FR, US) — not the country name. So your migration should be $table->string('country', 2)->nullable(); and nothing else. Open the field class itself first (src/Forms/Components/CountryField.php) to see the fluent surface; it extends Filament's Select, so everything you know about Select (->live(), ->afterStateUpdated(), ->placeholder(), ->disabled()) applies, plus the country-specific helpers.
Restrict the list rather than validating afterwards. For a Benelux-only signup form:
CountryField::make('country')
->countries(['BE', 'NL', 'LU'])
->searchable()
->required(),
->exclude([...]) is the inverse and reads better when you're removing a handful from the full set. Doing it at the field level means the browser can't submit an out-of-scope code at all — cheaper than a Rule::in() in a FormRequest, though add the rule anyway if the endpoint is reachable outside Filament.
Address forms: pair the field with the phone prefix. ->withPhonePrefix() renders the dialling code alongside the country, which is exactly what you want in a checkout form where the user picks country and the phone input's prefix updates live:
CountryField::make('country')
->withPhonePrefix()
->live()
->afterStateUpdated(fn (Set $set, ?string $state) =>
$set('phone_prefix', Country::phonePrefix($state))
),
Table columns. Keep the raw code in the DB and translate at render time — never store the display name, because names are localized and change (see Eswatini/Türkiye). A TextColumn with ->formatStateUsing() and ->badge() is the low-friction path; use ->withEmojiFlags() in the form and a small code→emoji map in the column so the two views agree.
Enum casting. If you already have a Country enum (or ARCHTECH/-style package), ->castStateUsing() on the field and a casts() entry on the model keep the code out of your domain layer:
protected function casts(): array
{
return ['country' => CountryCode::class];
}
This is the pattern I'd reach for on anything larger than a single form — it makes $order->country->phonePrefix() available everywhere instead of re-deriving from a string.
Livewire reactivity chains. ->live() plus ->afterStateUpdated() is how you drive dependent selects (state/province lists, currency, tax rate, shipping zone). Watch the round-trip count: chaining three live fields in a wizard step fires three requests per keystroke on the search input unless you use ->live(debounce: 500) or move the chain into a ->live(onBlur: true) equivalent.
php artisan filament:assets in your deploy script, after config:cache invalidation, and check that public/ (or your asset disk) actually receives the SVGs. php artisan optimize:clear is the debugging first move.->withEmojiFlags() looks great on macOS/iOS and renders as bare letter pairs on Windows and many Linux Chrome builds. If flag accuracy matters, ship the SVGs.BE) and alpha-3 (BEL) across tables is the classic migration regret; the package gives you alpha-2 from the field, so pick that as the house standard and write it into your schema docs.strtoupper() before comparing — SQLite and MySQL collation differences will bite you in tests but not in production.Parfaitementweb\FilamentCountryField\Forms\Components\CountryField; the class extends Filament\Forms\Components\Select, so a Filament major bump can change inherited behaviour (search debounce, option rendering) without the package's own changelog mentioning it. Pin Filament in composer.json and read the package's composer.json constraints on upgrade day.^2.5.6 rather than ^2.5 with a tight Laravel pin, otherwise Composer resolves to an older tag and you'll spend an hour wondering why the upgrade fails.assertFormSet, fillForm(['country' => 'BE'])) rather than asserting on rendered HTML; the option list is long and the markup includes search chrome that changes between Filament minors. For validation, assert that an out-of-whitelist code like 'ZZ' fails — a test that catches an accidentally-removed ->countries() call.How can I help you explore Laravel packages today?