Command Palette
Search for a command to run...
Loading component...
UniSwap Country Dialog
A modern country selector dialog with real-time search functionality. Features a clean, modern UI with flag display, search capabilities, and seamless integration with React Query for efficient data fetching and caching.
Dependencies
Interaction Type
Click to open country selector | |
Search countries by name or code |
CLI UNAVAILABLE
pro component, try copying the source code from top right corner
Manual setup
npx shadcn-ui@latest add dialog npm i @tanstack/react-query lucide-react
Setup QueryClient
import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; // Create a client const queryClient = new QueryClient(); // Wrap your Layout function Layout({ children }: { children: React.ReactNode }) { return ( <QueryClientProvider client={queryClient}>{children}</QueryClientProvider> ); }
How to use
import { CountrySelectDialog } from "@/components/v1/skiper20"; const Skiper20Demo = () => { const [isLayoutOpen, setIsLayoutOpen] = useState(false); const [selectedCountry, setSelectedCountry] = useState<Country | null>(null); const toggleLayout = () => { setIsLayoutOpen(!isLayoutOpen); }; const handleSelectCountry = (country: Country) => { setSelectedCountry(country); }; return ( <div className="flex h-full w-screen items-center justify-center bg-[#131313] text-white"> <button onClick={toggleLayout} className="flex items-center justify-center gap-1 rounded-full bg-[#2F2F2F] p-1.5 transition-colors duration-200 hover:bg-[#3A3A3A]" > {selectedCountry ? ( <> <div className="size-6 overflow-hidden rounded-full"> <img src={selectedCountry.flag} alt={selectedCountry.name} className="size-full object-cover" /> </div> </> ) : ( <> <div className="size-6 overflow-hidden rounded-full"> <img src="https://flagcdn.com/us.svg" alt="" className="size-full object-cover" /> </div> </> )} <ChevronDown className="size-5" /> </button> <CountrySelectDialog isOpen={isLayoutOpen} onClose={() => setIsLayoutOpen(false)} onSelectCountry={handleSelectCountry} selectedCountry={selectedCountry} /> </div> ); };
Props | Description |
---|---|
isOpen | Controls dialog open/close state |
onClose | Callback when dialog should close |
onSelectCountry | Callback when country is selected |
selectedCountry | Currently selected country or null |
React Query Customization
const { data: countries = [], isLoading, error, isError, } = useQuery({ queryKey: ["countries"], queryFn: fetchCountries, staleTime: 1000 * 60 * 5, // Data considered fresh for 5 minutes gcTime: 1000 * 60 * 10, // Keep in cache for 10 minutes });
Optional: Server-Side Country Detection
In Next.js, the easiest and most reliable way is to get the country server-side from the IP address (or use built-in CDN headers if hosting on Vercel).
1. Using Vercel's built-in country heade
Vercel automatically provides x-vercel-ip-country in the request headers.
- Works only if deployed on Vercel.
- Doesn't require any external API.
//middleware.js import { NextResponse } from "next/server"; export function middleware(req) { const country = req.headers.get("x-vercel-ip-country") || "Unknown"; console.log("User country:", country); const res = NextResponse.next(); res.headers.set("x-user-country", country); // pass to client if needed return res; }
2. Using IP Geolocation API (works anywhere)
// pages/api/get-country.js export default async function handler(req, res) { const ip = req.headers["x-forwarded-for"]?.split(",")[0] || req.socket.remoteAddress; const response = await fetch(`https://ipapi.co/${ip}/json/`); const data = await response.json(); res.status(200).json({ country: data.country_name || "Unknown" }); }
// client side fetch export default function Home() { const [country, setCountry] = React.useState(""); React.useEffect(() => { fetch("/api/get-country") .then((res) => res.json()) .then((data) => setCountry(data.country)); }, []); return <div>User is from: {country}</div>; }
Attribution
Inspired by and adapted from Uniswap
Source code
Keep in mind
Most components here are recreations of the best out there. I don't claim to be the original creator. This is my attempt to reverse-engineer, replicate, and often add a few extra features. I've tried to credit everyone, but if I missed something, let me know.
Contact
License & Usage:
- Free to use and modify in both personal and commercial projects.
- Attribution to Skiper UI is required when using the free version.
- No attribution required with Skiper UI Pro.