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

  1. Detect the user's currency from their IP address (no user input needed)
  2. Fetch the live exchange rate for that currency
  3. 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: true if 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