ReactExchange RatesTutorial
How to Localize Prices in React Using Exchange Rates
ApogeoAPI5 min read
Showing prices in a user's local currency increases trust, reduces bounce rates, and improves conversions — especially for international audiences. Here's how to do it automatically in React.
The Goal
- Detect the user's currency from their IP address (no user input needed)
- Fetch the live exchange rate for that currency
- Format the price according to their locale
Step 1: Detect User Currency from IP
// hooks/useCurrency.ts
import { useEffect, useState } from 'react';
export function useCurrency() {
const [currency, setCurrency] = useState('USD');
useEffect(() => {
fetch('https://api.apogeoapi.com/v1/geolocate/auto', {
headers: { 'X-API-Key': process.env.NEXT_PUBLIC_APOGEO_KEY! },
})
.then(r => r.json())
.then(geo => setCurrency(geo.currency ?? 'USD'))
.catch(() => {}); // Fallback to USD on any error
}, []);
return currency;
}
Step 2: Fetch the Exchange Rate
// hooks/useExchangeRate.ts
import { useEffect, useState } from 'react';
export function useExchangeRate(currency: string) {
const [rate, setRate] = useState<number | null>(null);
useEffect(() => {
if (currency === 'USD') { setRate(1); return; }
fetch(`https://api.apogeoapi.com/v1/exchange-rates/${currency}`, {
headers: { 'X-API-Key': process.env.NEXT_PUBLIC_APOGEO_KEY! },
})
.then(r => r.json())
.then(data => setRate(data.usdRate))
.catch(() => setRate(null)); // Fallback: show USD
}, [currency]);
return rate;
}
Step 3: Format the Price
function formatPrice(usdAmount: number, currency: string, usdRate: number): string {
const localAmount = usdAmount / usdRate;
return new Intl.NumberFormat(undefined, {
style: 'currency',
currency,
maximumFractionDigits: 2,
}).format(localAmount);
}
// Examples:
// formatPrice(19, 'EUR', 1.082) → '€17.56'
// formatPrice(19, 'ARS', 0.001) → 'ARS 19,000.00'
// formatPrice(19, 'BRL', 0.19) → 'R$100.00'
Step 4: The LocalizedPrice Component
// components/LocalizedPrice.tsx
'use client';
import { useCurrency } from '@/hooks/useCurrency';
import { useExchangeRate } from '@/hooks/useExchangeRate';
interface Props {
usdAmount: number;
className?: string;
}
export function LocalizedPrice({ usdAmount, className }: Props) {
const currency = useCurrency();
const rate = useExchangeRate(currency);
if (rate === null) {
return <span className={className}>${usdAmount}</span>;
}
const localAmount = usdAmount / rate;
const formatted = new Intl.NumberFormat(undefined, {
style: 'currency',
currency,
maximumFractionDigits: 2,
}).format(localAmount);
return <span className={className}>{formatted}</span>;
}
Edge Cases
- Stale rates: The API returns
stale: trueif rates are older than 6 hours. Show a small "approximate" indicator in that case. - Unknown currency: Always have a USD fallback — the
catch(() => {})blocks above handle this. - Loading state: While fetching, show the USD price — it's better than a blank or flashing layout.
- Server-side rendering: Detect currency server-side in middleware and pass it as a prop or cookie to avoid a layout shift.
Try ApogeoAPI free
1,000 requests/month forever. 14-day full-access trial. No credit card.
Get your free API key