Componentpickers1.29 KB gzipZero dependencies

Country Select

Country select with flag emojis and codes.

Preview

Live Preview

Installation

Tokens guide →

Add to your project via CLI (zero dependencies, code you own):

npx bigbullui add country-select

Or install the full npm package:

npm install bigbullui
import { CountrySelect } from "@/components/ui/country-select";

<CountrySelect />

Usage guidance

Use Country Select when a ticket stub surface needs Country select with flag emojis and codes. Keep props minimal and prefer composition with sibling stubs.

Avoid Country Select for long form flows or dense data grids where a dedicated layout block fits better.

Source code

country-select.tsx

Zero dependencies (only React + utils). Copy and paste directly into your project:

"use client";

import * as React from "react";
import { cn } from "./lib/utils";

export interface CountryOption {
  code: string;
  name: string;
  flag: string;
}

export const COUNTRIES: CountryOption[] = [
  { code: "TR", name: "Turkey", flag: "🇹🇷" },
  { code: "US", name: "ABD", flag: "🇺🇸" },
  { code: "GB", name: "United Kingdom", flag: "🇬🇧" },
  { code: "DE", name: "Almanya", flag: "🇩🇪" },
  { code: "FR", name: "Fransa", flag: "🇫🇷" },
  { code: "JP", name: "Japonya", flag: "🇯🇵" },
  { code: "BR", name: "Brezilya", flag: "🇧🇷" },
  { code: "IN", name: "Hindistan", flag: "🇮🇳" },
];

export interface CountrySelectProps extends Omit<React.HTMLAttributes<HTMLDivElement>, "onChange"> {
  value?: string;
  defaultValue?: string;
  onValueChange?: (code: string) => void;
}

/** Country selector dropdown with national flag icons. */
export function CountrySelect({ value, defaultValue, onValueChange, className, ...props }: CountrySelectProps) {
  const [internal, setInternal] = React.useState(defaultValue ?? "TR");
  const [open, setOpen] = React.useState(false);
  const active = COUNTRIES.find((c) => c.code === (value ?? internal)) ?? COUNTRIES[0];

  return (
    <div className={cn("relative", className)} {...props}>
      <style>{`@keyframes csIn { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: translateY(0); } }`}</style>
      <button
        onClick={() => setOpen((o) => !o)}
        aria-expanded={open}
        aria-haspopup="listbox"
        className={cn(
          "flex w-full items-center gap-2 rounded-md border border-input bg-background px-3 py-2 text-sm",
          "transition-colors motion-reduce:transition-none focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring hover:border-foreground/40"
        )}
      >
        <span className="text-base" aria-hidden="true">{active.flag}</span>
        <span className="flex-1 text-start">{active.name}</span>
        <span className="font-mono text-[10px] text-muted-foreground">{active.code}</span>
      </button>
      {open && (
        <>
          <div className="fixed inset-0 z-20" onClick={() => setOpen(false)} aria-hidden="true" />
          <ul role="listbox" className="absolute z-30 mt-1 max-h-52 w-full overflow-y-auto rounded-md border border-border bg-card p-1 shadow-lg animate-[csIn_0.2s_ease-out_both] motion-reduce:animate-none">
            {COUNTRIES.map((c) => (
              <li key={c.code}>
                <button
                  role="option"
                  aria-selected={c.code === active.code}
                  onClick={() => { setInternal(c.code); onValueChange?.(c.code); setOpen(false); }}
                  className={cn(
                    "flex w-full items-center gap-2 rounded-sm px-2 py-1.5 text-sm transition-colors motion-reduce:transition-none focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring",
                    c.code === active.code ? "bg-accent/10 text-accent" : "hover:bg-secondary"
                  )}
                >
                  <span aria-hidden="true">{c.flag}</span>
                  <span className="flex-1 text-start">{c.name}</span>
                  <span className="font-mono text-[10px] text-muted-foreground">{c.code}</span>
                </button>
              </li>
            ))}
          </ul>
        </>
      )}
    </div>
  );
}

Props

PropTypeDescription
valuestring
defaultValuestring
onValueChange(code: string) => void