Reddot UI Library

Docs
Currency Provider

Currency Provider

Multi-currency support with automatic detection and real-time exchange rates

Installation

$npx shadcn@latest add https://reddot.dottools.xyz/r/currency-provider.json

Features

  • 🌍 Automatic Currency Detection - Detects user's currency based on their country
  • 💱 Real-time Exchange Rates - Fetches live rates from multiple API sources
  • 🔄 Fallback Support - Multiple API fallbacks for reliability
  • 💰 Commission Handling - Configurable commission rate for non-EUR currencies
  • 🎨 Flexible Display - Multiple formatting options for prices
  • Server-Side Ready - Works with Next.js SSR

Usage

Basic Setup

Wrap your application with the CurrencyProvider:

import { CurrencyProvider } from '@/components/currency-provider';
import type { PropsWithChildren } from 'react';
 
export default function Layout({ children }: PropsWithChildren) {
  return (
    <CurrencyProvider
      countryCode="US" // Usually from headers or geolocation
      commissionRate={1.03} // 3% commission
    >
      {children}
    </CurrencyProvider>
  );
}

Display Prices

Use the PriceDisplay component to show formatted prices:

import { PriceDisplay } from '@/components/currency-provider';
 
export function ProductCard({ price }: { price: number }) {
  return (
    <div>
      <PriceDisplay
        price={29.99}
        showCombined={true} // Shows "31.19 USD (29.99 €)"
        showPerMonth={true}
        size="xl"
      />
    </div>
  );
}

Use Currency Hook

Access currency data programmatically:

import { useCurrency } from '@/components/currency-provider';
 
export function Checkout() {
  const { userCurrency, exchangeRate, formatPrice } = useCurrency();
 
  const totalPrice = 99.99;
  const formattedTotal = formatPrice({
    initialPrice: totalPrice,
    showCombined: true,
  });
 
  return <div>Total: {formattedTotal}</div>;
}

Price Range Display

Show price ranges with automatic currency conversion:

import { PriceRangeDisplay } from '@/components/currency-provider';
 
export function PricingTiers() {
  return <PriceRangeDisplay minPrice={9.99} maxPrice={99.99} separator=" to " />;
}

Server-Side Usage

Geolocation Setup

The component requires a countryCode prop for currency detection. You'll need to set up geolocation based on your hosting provider:

Vercel:

First, set up geolocation in your middleware:

import { geolocation } from '@vercel/functions';
import createMiddleware from 'next-intl/middleware';
import type { NextRequest } from 'next/server';
 
export default async function middleware(request: NextRequest) {
  const geo = geolocation(request);
 
  // Your existing middleware logic...
  const response = NextResponse.next();
 
  // Set country header for use in components
  response.headers.set('x-country', geo?.country || 'US');
 
  return response;
}

Then use it in your layout:

import { headers } from 'next/headers';
import { CurrencyProvider } from '@/components/currency-provider';
 
export default function Layout({ children }: PropsWithChildren) {
  const headersList = await headers();
  // Country code set by middleware
  const countryCode = headersList.get('x-country') || 'US';
 
  return <CurrencyProvider countryCode={countryCode}>{children}</CurrencyProvider>;
}

Other providers:

  • Netlify: Use x-country header with Netlify Edge Functions
  • Cloudflare: Use cf-ipcountry header with Cloudflare Workers
  • Custom: Implement your own geolocation service or use libraries like geoip-lite
// Example with custom geolocation service
export default function Layout({ children }: PropsWithChildren) {
  const headersList = await headers();
  const ip = headersList.get('x-forwarded-for') || headersList.get('x-real-ip');
  const countryCode = (await getCountryFromIP(ip)) || 'US';
 
  return <CurrencyProvider countryCode={countryCode}>{children}</CurrencyProvider>;
}

API Integration

Built-in API Client

The component uses a built-in API client that directly calls external APIs:

import { fetchCountryCurrency, fetchExchangeRate } from '@/components/currency-provider';
 
// Get currency for a country (uses RestCountries API)
const { currency } = await fetchCountryCurrency('FR'); // Returns 'EUR'
 
// Get exchange rate (uses Frankfurter + UniRate APIs)
const { rate } = await fetchExchangeRate('USD'); // Returns ~1.08

The APIs used are:

  • RestCountries API: https://restcountries.com/v3.1/alpha/{country}?fields=currencies
  • Frankfurter API: https://api.frankfurter.app/latest?from=EUR&symbols={currency}
  • UniRate API (fallback): https://api.unirateapi.com/api/rates (requires API key)

Environment Variables

# Optional: For UniRate API fallback
UNI_RATE_API_KEY=your_api_key_here

Props Reference

CurrencyProvider

PropTypeDefaultDescription
childrenReactNoderequiredChild components
defaultCurrencystring'EUR'Default currency code
commissionRatenumber1.03Commission multiplier (1.03 = 3%)
countryCodestringundefinedISO country code for detection

PriceDisplay

PropTypeDefaultDescription
pricenumberrequiredPrice in base currency (EUR)
fallbackPricestringundefinedFallback price string if needed
showCombinedbooleanfalseShow local + EUR price
showPerMonthbooleanfalseShow "per month" text
perMonthTextstring'per month'Custom text for period
size'sm' | 'md' | 'lg' | 'xl''md'Display size
classNamestring''Additional CSS classes

PriceRangeDisplay

PropTypeDefaultDescription
minPricenumberrequiredMinimum price in base currency
maxPricenumberrequiredMaximum price in base currency
separatorstring' - 'Text between prices
classNamestring''Additional CSS classes

API Sources

The component uses these free APIs:

Examples

Custom Commission Rate

<CurrencyProvider
  countryCode="JP"
  commissionRate={1.05} // 5% commission for Japanese Yen
>
  <PriceDisplay price={1000} />
</CurrencyProvider>

EUR-Only Mode

import { useCurrency } from '@/components/currency-provider';
 
export function AlwaysEurPrice() {
  const { formatPrice } = useCurrency();
 
  return (
    <div>
      {formatPrice({
        initialPrice: 49.99,
        showEurOnly: true, // Always show EUR
      })}
    </div>
  );
}

Loading State

The component handles loading states automatically:

export function PriceWithLoader() {
  const { isLoading } = useCurrency();
 
  if (isLoading) {
    return <div>Loading prices...</div>;
  }
 
  return <PriceDisplay price={99.99} />;
}