dcemal/filament-jsvectormap

A FilamentPHP v5 form field and infolist entry for selecting and displaying countries on an interactive jsVectorMap.

Maintainers

Package info

github.com/dcemal/filament-jsvectormap

pkg:composer/dcemal/filament-jsvectormap

Statistics

Installs: 1

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

1.0.0 2026-05-29 21:00 UTC

This package is auto-updated.

Last update: 2026-05-29 21:04:42 UTC


README

Pick countries on a clickable world map, right inside your Filament admin panel.

This is a plugin for FilamentPHP (the admin-panel toolkit for Laravel). It wraps the jsVectorMap JavaScript library (docs) so you get a nice interactive map without writing any JavaScript yourself.

You get two ready-made pieces:

  • MapSelect — a form field. Users click countries on the map (or search a list) to choose them. Selected countries light up.
  • CountriesEntry — a read-only display for your "view" pages. Shows the chosen countries as flag badges, with an optional highlighted map.

What it looks like

Imagine a normal dropdown for picking countries — but above it there's a world map. Click a country on the map and it turns green and gets added to the list. Click it again to remove it. Type in the search box to filter. Pick a whole region (like "MENA" or "Latin America") in one tap. That's the field.

Before you start

You need a working Laravel app with Filament installed. If you don't have that yet, follow Filament's own guide first: https://filamentphp.com/docs/5.x/introduction/installation. Come back here once you can log in to your panel at /admin.

Requirements:

  • PHP 8.2 or newer
  • Filament v5

That's it — you do not need Node.js, npm, or any build step. The map's JavaScript is loaded automatically.

1. Install

Run this in your project folder:

composer require dcemal/filament-jsvectormap

Then publish the plugin's assets (its JavaScript and CSS) so the panel can use them:

php artisan filament:assets

Done. The plugin registers itself — there's nothing to add to a config file.

Tip: Run php artisan filament:assets again any time you update the package.

2. Make somewhere to store the choices

A list of countries needs a column in your database. The map stores countries as two-letter codes (TR, BR, DZ, …).

If you're adding this to an existing table, create a migration:

php artisan make:migration add_target_countries_to_campaigns_table

Inside that migration, add a json column:

$table->json('target_countries')->nullable();

Run it:

php artisan migrate

Then, on your model (e.g. app/Models/Campaign.php), tell Laravel to treat that column as a list. This one line is important — without it the data won't save correctly:

protected $casts = [
    'target_countries' => 'array',
];

(If you only ever let people pick one country, use a plain string column instead and skip the cast.)

3. Add the map to your form

Open your Filament form (in a resource this is usually app/Filament/Resources/.../Schemas/...Form.php, or the form() method of the resource). Add the field:

use Dcemal\FilamentJsVectorMap\Forms\Components\MapSelect;

MapSelect::make('target_countries')
    ->label('Target countries');

Reload the page — you'll see the map. That's the whole basic setup. 🎉

4. (Optional) Show it on a "view" page

If your resource has a read-only view page (an "infolist"), you can display the saved countries there:

use Dcemal\FilamentJsVectorMap\Infolists\Components\CountriesEntry;

CountriesEntry::make('target_countries')
    ->label('Target countries')
    ->showMap();   // or ->hideMap() to show only the flag badges

Common options

All of these are optional — the field works fine with none of them. Chain the ones you want, like ->multiple()->height(400).

What you want Add this
Let people pick many countries (this is the default) ->multiple()
Let people pick only one ->single()
Show the list always open, not in a dropdown ->inline()
Offer "select a whole region" buttons ->regions(['MENA' => ['DZ','EG','SA','AE']])
Only allow certain countries ->only(['BR','MX','AR'])
Hide certain countries ->except(['AQ'])
Show a country but stop it being picked ->disabledCountries(['TR'])
Change the map height ->height(400)
Change the "selected" colour ->selectedColor('#16a34a')
Show the country name when hovering ->tooltips()
Zoom in automatically on the chosen countries ->focusOnSelection()
Show zoom +/- buttons ->zoomButtons()
Country names in another language ->locale('tr')

A small, complete example:

MapSelect::make('target_countries')
    ->label('Target countries')
    ->multiple()
    ->regions([
        'MENA'          => ['DZ','EG','MA','SA','AE','QA'],
        'Latin America' => ['BR','MX','AR','CL','CO'],
    ])
    ->selectedColor('#16a34a')
    ->tooltips()
    ->focusOnSelection();

Translating country names

Country names come from a built-in English list, and fall back to PHP's own translations for any language you set with ->locale(). To edit names or add a language, publish the files and edit the copies:

php artisan vendor:publish --tag="filament-jsvectormap-translations"

This creates files under lang/vendor/filament-jsvectormap/. Add a folder like tr/ for Turkish and edit countries.php / messages.php there.

Using a different map (advanced)

Out of the box, only the world map is available, because that's the only map the jsVectorMap library ships over the internet (CDN). If you need another map (a Mercator world, a single continent, or one country), download it from jsVectorMap's available maps and host it yourself.

Save the map files in your app's public/ folder, then point the field at them:

mkdir -p public/vendor/jsvectormap/maps
# the core library:
curl -o public/vendor/jsvectormap/jsvectormap.min.js  https://cdn.jsdelivr.net/npm/jsvectormap@1.7.0/dist/jsvectormap.min.js
curl -o public/vendor/jsvectormap/jsvectormap.min.css https://cdn.jsdelivr.net/npm/jsvectormap@1.7.0/dist/jsvectormap.min.css
# the world map:
curl -o public/vendor/jsvectormap/maps/world.js       https://cdn.jsdelivr.net/npm/jsvectormap@1.7.0/dist/maps/world.js
MapSelect::make('target_countries')->assetsUrl('/vendor/jsvectormap');

The plugin looks for the map file at <assetsUrl>/maps/<map id>.js, using the exact name you pass to ->map(). One thing to watch: a few maps have an underscore in their id but a hyphen in their filename — for example the Mercator map id is world_merc, but its file is world-merc.js. Save it under the id you'll use:

curl -o public/vendor/jsvectormap/maps/world_merc.js https://cdn.jsdelivr.net/npm/jsvectormap@1.7.0/dist/maps/world-merc.js
MapSelect::make('target_countries')->map('world_merc')->assetsUrl('/vendor/jsvectormap');

Troubleshooting

The map is blank / nothing shows up. First, make sure you ran php artisan filament:assets, then hard-refresh the browser (Ctrl/Cmd + Shift + R). If you changed ->map(...) to something other than world, that map isn't available from the CDN — see Using a different map above.

I see a raw label like filament-jsvectormap::messages.regions instead of words. Run php artisan optimize:clear. If you published the translations earlier, your copy is older than the package — re-publish with --force, or add the missing lines to your file in lang/vendor/filament-jsvectormap/.

The chosen countries don't save. Check two things: the column is a json column, and your model has the 'array' cast (see step 2). For single-select fields, use a string column and no cast.

Nothing changed after updating the package. Run php artisan filament:assets and php artisan optimize:clear, then hard-refresh.

How it stores data

  • Multiple countries → an array of codes, e.g. ["TR","BR","DZ"]. Use a json column + 'array' cast.
  • One country → a single code, e.g. "BR". Use a string column, no cast.

The CountriesEntry display understands both, so you don't have to configure it for either case.

Credits

  • Built on jsVectorMap by Mustafa Omar — the JavaScript library that draws the map.
  • A plugin for FilamentPHP.

License

MIT. Free to use in personal and commercial projects.