Library
React 18+
react-countries-flags
Controlled React component with forwardRef, typed props,
and country data from the core package.
Try it live
Open the React demo page to try the component.
Styling examples
Go to the React styling section for Tailwind, shadcn/ui, Bootstrap, inline styles, and the slot map.
Installation
pnpm add @drobinetm/react-countries-flags flag-icons Import the CSS in your entry point:
// main.tsx or App.tsx
import 'flag-icons/css/flag-icons.min.css'
import '@drobinetm/react-countries-flags/styles' Basic usage
Use the controlled API with value and onChange.
import { useState } from 'react'
import { CountriesFlags } from '@drobinetm/react-countries-flags'
export function App() {
const [code, setCode] = useState<string | null>(null)
return (
<CountriesFlags
value={code}
onChange={(event) => setCode(event.code)}
placeholder="Select a country"
/>
)
} Filter & limit
Combine filter and max to expose only the countries your flow needs.
<CountriesFlags
value={code}
onChange={(event) => setCode(event.code)}
filter={['es', 'us', 'br', 'ar', 'mx']}
max={10}
/> Access the full Country object
The callback receives both the ISO code and the resolved country object.
import type { CountryChangeEvent } from '@drobinetm/react-countries-flags'
function handleChange(event: CountryChangeEvent) {
console.log(event.code) // 'es'
console.log(event.country) // { code: 'es', name: 'Spain' }
}
<CountriesFlags value={code} onChange={handleChange} /> CSS integrations
In React, you can keep the bundled styles, override specific slots, or
disable them completely with unstyled. The examples below
are valid for React projects only.
Use classNames and styles to map the
component to Tailwind or shadcn/ui.
import { CountriesFlags } from '@drobinetm/react-countries-flags'
<CountriesFlags
value={code}
onChange={(event) => setCode(event.code)}
unstyled
classNames={{
root: 'w-full',
trigger:
'flex h-11 w-full items-center justify-between rounded-xl border border-slate-300 bg-white px-3 text-left text-slate-900 shadow-sm transition focus:outline-none focus:ring-2 focus:ring-sky-500 dark:border-slate-700 dark:bg-slate-950 dark:text-slate-100',
triggerContent: 'flex min-w-0 items-center gap-2',
placeholder: 'text-slate-400 dark:text-slate-500',
label: 'truncate',
chevron: 'text-slate-500',
list:
'mt-2 max-h-64 overflow-auto rounded-xl border border-slate-200 bg-white p-1 shadow-2xl dark:border-slate-800 dark:bg-slate-950',
option: 'flex cursor-pointer items-center gap-2 rounded-lg px-3 py-2 text-sm',
optionActive: 'bg-slate-100 dark:bg-slate-900',
optionSelected: 'bg-sky-600 text-white',
optionName: 'truncate',
}}
styles={{
flag: { borderRadius: '9999px' },
}}
/> You can also map the slots to Bootstrap utility classes.
import { CountriesFlags } from '@drobinetm/react-countries-flags'
<CountriesFlags
value={code}
onChange={(event) => setCode(event.code)}
unstyled
classNames={{
root: 'w-100',
trigger: 'btn btn-outline-secondary w-100 d-flex align-items-center justify-content-between rounded-3 px-3 py-2',
triggerContent: 'd-flex align-items-center gap-2 text-truncate',
placeholder: 'text-secondary',
list: 'mt-2 list-unstyled border rounded-3 bg-body shadow-lg p-1',
option: 'd-flex align-items-center gap-2 rounded-3 px-3 py-2',
optionActive: 'bg-light',
optionSelected: 'bg-primary text-white',
optionName: 'text-truncate',
}}
styles={{
root: { minWidth: '280px' },
flag: { borderRadius: '50%' },
}}
/>
If your styles are generated at runtime, use the
styles prop directly.
import { CountriesFlags } from '@drobinetm/react-countries-flags'
<CountriesFlags
value={code}
onChange={(event) => setCode(event.code)}
unstyled
styles={{
root: { width: '100%', minWidth: 280 },
trigger: {
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
width: '100%',
minHeight: 44,
padding: '0 14px',
border: '1px solid #d4d4d8',
borderRadius: 16,
backgroundColor: '#ffffff',
color: '#111827',
},
triggerContent: { display: 'flex', alignItems: 'center', gap: 8, minWidth: 0 },
placeholder: { color: '#6b7280' },
list: {
marginTop: 8,
padding: 6,
listStyle: 'none',
border: '1px solid #e5e7eb',
borderRadius: 16,
backgroundColor: '#ffffff',
boxShadow: '0 24px 48px rgba(15, 23, 42, 0.18)',
},
option: { display: 'flex', alignItems: 'center', gap: 8, padding: '10px 12px', borderRadius: 12 },
optionActive: { backgroundColor: '#f3f4f6' },
optionSelected: { backgroundColor: '#2563eb', color: '#ffffff' },
flag: { borderRadius: '9999px' },
}}
/> Props
| Prop | Type | Default | Description |
|---|---|---|---|
value | string | null | null | Controlled selected code. |
onChange | (e: CountryChangeEvent) => void | — | Selection callback. |
max | number | 0 | Max countries shown. |
filter | string[] | [] | ISO alpha-2 whitelist. |
placeholder | string | "Select a country" | Placeholder text. |
disabled | boolean | false | Disables the component. |
unstyled | boolean | false | Turns off the bundled UI styles. |
className | string | "" | Additional CSS class names. |
classNames | CountriesFlagsClassNames | {} | Classes by component slot. |
styles | CountriesFlagsStyles | {} | Inline styles by component slot. |