If you sell to international customers, showing prices in the visitor's local currency isn't a nice-to-have — it's a conversion-rate lever. But wiring it up usually turns into a small project: IP geolocation, exchange-rate fetching, caching, fallbacks, race conditions when the rate call is slower than render.
react-currency-localizer-realtime compresses all of that into a React hook and a component. Drop it in, pass a price, done.
Under the hood it uses real-time mid-market rates from the AllRatesToday API — 160+ currencies, updated every 60 seconds, sourced from Reuters/Refinitiv.
What you get
- ⚡ Real-time mid-market rates — no retail markup, no hidden spread
- 🌍 Auto-detect the user's currency via IP geolocation (no API key needed for geolocation)
- 💱 160+ currencies — major, minor, exotic, and precious metals
- 🧠 Smart caching — 24h localStorage for geolocation, 1h memory cache for rates
- 🔀 Three usage patterns — hook, component, or batch
- 🎯 Manual override — skip geolocation with an explicit currency
- 🔷 TypeScript-first — full type definitions
- 📦 Zero runtime deps besides React itself
- 🪶 ~4KB gzipped
- 🛡️ Graceful fallbacks — shows the original price if anything fails
Install
npm install react-currency-localizer-realtime
React 17+ is the only peer dependency.
Get a free API key at allratestoday.com/register.
1. The component — the simplest way
import { LocalizedPrice } from 'react-currency-localizer-realtime';
function ProductCard() {
return (
<div>
<h3>Premium Plan</h3>
<LocalizedPrice
basePrice={99.99}
baseCurrency="USD"
apiKey="YOUR_API_KEY"
/>
</div>
);
}
A visitor from Germany sees 92.34 €. A visitor from India sees ₹8,321. A visitor from the US just sees $99.99. No per-country routing, no SSR detection, no currency-switcher UI to build.
2. The hook — full control
Use useCurrencyConverter when you need loading/error states or custom formatting:
import { useCurrencyConverter } from 'react-currency-localizer-realtime';
function ProductPrice({ price }: { price: number }) {
const { convertedPrice, localCurrency, isLoading, error } = useCurrencyConverter({
basePrice: price,
baseCurrency: 'USD',
apiKey: 'YOUR_API_KEY',
});
if (isLoading) return <span>Loading price...</span>;
if (error) return <span>${price}</span>; // graceful fallback
return <span>{convertedPrice} {localCurrency}</span>;
}
3. Manual override
Want to honor a user-picked currency? Pass it explicitly:
<LocalizedPrice
basePrice={99.99}
baseCurrency="USD"
currency="JPY" // override geolocation
apiKey="YOUR_API_KEY"
/>
Useful when you have a currency switcher in the header.
Why not just use Intl.NumberFormat?
Intl.NumberFormat handles formatting, not conversion. If you pass it 99.99 and tell it to format as JPY, it'll print ¥100 — wrong. You still need an actual exchange rate.
react-currency-localizer-realtime handles both: detect the currency, fetch the rate, apply it, format the output.
Performance notes
Two things usually hurt currency localizers on page load:
- Geolocation calls on every render. Fixed here with a 24h localStorage cache.
- Rate fetches on every price. Fixed here with a 1h in-memory cache shared across component instances.
Net effect: first visit triggers two network calls; every subsequent price on the page (and every return visit within 24h) is instant.
If a request fails, the component falls back to showing the original price rather than blanking — your customers never see a broken UI.
Real-world use case
This is the package I reach for whenever a WooCommerce/Shopify/custom storefront needs per-visitor pricing without forking the catalog by region. It's the missing middle between "one-currency site" and "separate regional installs."
Combine it with LocalizedPrice on product cards and a currency switcher in the header, and you've covered 95% of international-storefront UX in an afternoon.
Next steps
- npm:
react-currency-localizer-realtime - Free API key: allratestoday.com/register
- Docs: allratestoday.com
If you ship it on a live store, I'd love to see the before/after conversion numbers — drop them in the comments.
Top comments (0)