RegionSelect
Server location picker — an interactive dot-matrix world map synced with an accessible select.
RegionSelect turns the region dropdown from project creation into the map itself. Every datacenter is a live marker on a dot-matrix world map: hover lifts a label pill with the city and region id, click selects, and the chosen marker glows primary with a slow ping halo. A floating region card mirrors the selection — name, mono region id, and your measured round-trip when latencies is provided — and the select underneath is the same input — so keyboard and screen-reader users get the full flow without ever touching the map. The land bitmap draws once to a canvas in the current theme’s ink, not thousands of DOM nodes.
Region
Region
AWS Europe Central 1 (Frankfurt)
aws-eu-central-1 · ping: 92 ms
Select the region closest to your application.
Install
npx shadcn@latest add https://ui.neon.com/r/region-select.json
Usage
import { RegionSelect } from "@/components/neon-ui/region-select";
<RegionSelect
regions={[
{
id: "aws-us-east-1",
name: "US East 1 (N. Virginia)",
provider: "AWS",
lat: 38.9,
lng: -77.4,
},
{
id: "aws-eu-central-1",
name: "Europe Central 1 (Frankfurt)",
provider: "AWS",
lat: 50.11,
lng: 8.68,
},
]}
value={region}
onValueChange={setRegion}
/>;
Wiring to Neon
A server component turns the active regions into ServerRegions:
import { createNeonClient } from "@/lib/neon-client";
const neon = createNeonClient(process.env.NEON_API_KEY);
const { data } = await neon.regions.list();
// set orgId on the client to scope this to one organization's regions
const regions = (data ?? [])
.filter((region) => region.geo_lat && region.geo_long)
.map((region) => ({
id: region.region_id,
name: region.name,
provider: region.region_id.split("-")[0].toUpperCase(),
lat: Number(region.geo_lat),
lng: Number(region.geo_long),
}));
Coordinates come back as strings and are empty when Neon does not know them, so filter before mapping or the marker lands at 0,0. default: true marks the region new projects use, which makes a sensible defaultValue. Selection is client state, so wire onValueChange in a client wrapper.
Props
regions?ServerRegion[]
Regions to plot and list; each needs an id, name, and lat/lng. Order drives the select.
ServerRegion[]value?string
Controlled selected region id.
stringdefaultValue?string
Uncontrolled initial region id.
stringonValueChange?(value: string) => void
Fires on marker click or select change.
(value: string) => voidplaceholder?string
Select placeholder before a region is chosen.
string"Select region"hideSelect?boolean
Hide the select under the map; markers become the only input.
booleanhideCard?boolean
Hide the floating region card on the map.
booleancardSuffix?ReactNode
Extra content inside the region card, e.g. a latency badge.
ReactNodelatencies?Record<string, number | null>
Measured round-trips by region id (e.g. from useRegionPing); the selected region's ping folds into the card's mono line.
Record<string, number | null>ServerRegion
id?string
Region id, e.g. "aws-us-east-1". Shown mono in the marker pill.
stringname?string
Human-readable name, e.g. "US East 1 (N. Virginia)".
stringprovider?string
Optional provider prefix for labels, e.g. "AWS".
stringlat?number
Datacenter latitude.
numberlng?number
Datacenter longitude.
numberdisabled?boolean
Unavailable region; marker and option render dimmed.
booleanAccessibility
- Markers are real buttons with
aria-pressedand full labels; the hover pill is presentational. - The select is the same input, so the complete keyboard and screen-reader flow needs no map interaction. Keep it visible unless another accessible input drives the value.
- The ping halo on the selected marker is static under
prefers-reduced-motion; the map canvas is decorative (aria-hidden).
Notes
- The map is Natural Earth 110m land sampled onto a 192×69 equirectangular grid (Antarctica clipped) and shipped as a compact hex bitmap — no map library, no fetch.
- The canvas re-inks itself on theme flips by watching the root element’s class, so the dots always match
text-muted-foreground.