How to Build a Custom Currency Switcher with JavaScript

Table of Contents
Big thanks to our contributors those make our blogs possible.

Our growing community of contributors bring their unique insights from around the world to power our blog. 

Introduction

In today’s global marketplace, offering prices in your customers’ local currency isn’t just a nicety—it’s an expectation. A smooth currency switcher can boost conversions, reduce abandoned carts, and enhance user trust. In this guide, we’ll walk through building a fully custom currency switcher from scratch using plain JavaScript, HTML, and CSS. You’ll learn how to fetch live exchange rates, handle edge cases gracefully, and optimize for performance and accessibility. By the end, you’ll have a reusable component you can drop into any project—no heavy libraries required.

Why You Need a Currency Switcher

  • Global Reach: Let international visitors shop without manual conversions.
  • Higher Conversions: Shoppers are more likely to buy when they see prices in their own currency.
  • Improved UX: Eliminates confusion and builds trust.

Planning Your Switcher

Before writing code, outline your requirements:

  • Supported currencies (e.g., USD, EUR, GBP, JPY)
  • Source for live exchange rates
  • Fallback when API fails
  • Styling to match your design system

Defining the HTML Structure

htmlCopyEdit<div class="currency-switcher">
  <label for="currency-select">Currency:</label>
  <select id="currency-select">
    <option value="USD" selected>USD</option>
    <option value="EUR">EUR</option>
    <option value="GBP">GBP</option>
    <option value="JPY">JPY</option>
  </select>
</div>

<div class="prices">
  <p class="price" data-base-price="49.99">$49.99</p>
  <p class="price" data-base-price="120.00">$120.00</p>
</div>
  • Each .price element stores a data-base-price attribute with your site’s base currency (e.g., USD).
  • The <select> lets the user pick a target currency.

Styling the Switcher

cssCopyEdit.currency-switcher {
  margin-bottom: 1rem;
}
.currency-switcher select {
  padding: 0.5rem;
  border-radius: 4px;
}
.price {
  font-size: 1.25rem;
  margin: 0.5rem 0;
}

Adjust colors and fonts to fit your branding.

Fetching Live Exchange Rates

You’ll need an API—popular free options include ExchangeRate-API, Open Exchange Rates (free tier), or Fixer.io.

Using a Public API

jsCopyEditasync function fetchExchangeRates(base = 'USD') {
  const response = await fetch(
    `https://api.exchangerate-api.com/v4/latest/${base}`
  );
  if (!response.ok) throw new Error('Failed to fetch rates');
  const data = await response.json();
  return data.rates; // { EUR: 0.84, GBP: 0.75, ... }
}

Handling CORS and Errors

  • CORS: Ensure your API supports browser requests or route via your server.
  • Error Fallback: Cache last-known rates in localStorage to use when the network fails.
jsCopyEditfunction saveRatesToCache(rates) {
  localStorage.setItem('exchangeRates', JSON.stringify({
    timestamp: Date.now(),
    rates
  }));
}
function getRatesFromCache(maxAgeMinutes = 60) {
  const cached = JSON.parse(localStorage.getItem('exchangeRates') || '{}');
  if (
    cached.rates &&
    Date.now() - cached.timestamp < maxAgeMinutes * 60 * 1000
  ) {
    return cached.rates;
  }
  return null;
}

JavaScript Implementation

Initializing the Switcher

jsCopyEditconst currencySelect = document.getElementById('currency-select');
const priceElements = document.querySelectorAll('.price');
let exchangeRates = getRatesFromCache() || {};

if (!exchangeRates || !exchangeRates.USD) {
  fetchExchangeRates('USD')
    .then(rates => {
      exchangeRates = rates;
      saveRatesToCache(rates);
    })
    .catch(err => console.error(err));
}

Updating Prices on Change

jsCopyEditcurrencySelect.addEventListener('change', () => {
  const targetCurr = currencySelect.value;
  if (!exchangeRates[targetCurr]) {
    fetchExchangeRates('USD')
      .then(rates => {
        exchangeRates = rates;
        saveRatesToCache(rates);
        updateDisplayedPrices(targetCurr);
      })
      .catch(err => {
        console.error(err);
        updateDisplayedPrices(targetCurr, true);
      });
  } else {
    updateDisplayedPrices(targetCurr);
  }
});

function updateDisplayedPrices(curr, useFallback = false) {
  priceElements.forEach(el => {
    const base = parseFloat(el.dataset.basePrice);
    const rate = useFallback
      ? getRatesFromCache()?.[curr] || 1
      : exchangeRates[curr];
    const converted = (base * rate).toFixed(2);
    el.textContent = `${curr} ${converted}`;
  });
}

Optimizations & Best Practices

  • Debounce Rapid Changes: If your UI has sliders or rapid updates, debounce calls.
  • Lazy-Load Rates: Only fetch rates when the switcher enters the viewport (Intersection Observer).
  • Minimize API Calls: Respect rate limits by using caching and only re-fetching hourly or daily.

Accessibility & Testing

  • ARIA Labels: <select aria-label="Select currency"></select>.
  • Keyboard Navigation: Ensure the switcher is focusable and users can tab through.
  • Unit Tests: Mock fetch in Jest to verify price conversions.
  • Cross-Browser Checks: Test in Chrome, Firefox, Safari, and mobile browsers.

Real-World Analogy

Think of your currency switcher as a multilingual interpreter at a conference:

  • You have speakers (base prices) in one language (USD).
  • The interpreter (exchange API) converts their words into the attendee’s language (EUR, JPY).
  • Caching is like having a printed cheat sheet of common phrases—faster and useful if the interpreter steps out for a moment.

Conclusion

Building a custom currency switcher with JavaScript empowers you to provide a seamless international shopping experience without relying on heavyweight libraries. By structuring your HTML cleanly, fetching and caching exchange rates smartly, and encapsulating your logic in maintainable functions, you create a component that’s both performant and user-friendly. Remember to optimize for accessibility, handle errors gracefully, and test across browsers. Now you’re ready to integrate global pricing into any web project—happy coding!

Let's connect on TikTok

Join our newsletter to stay updated

Sydney Based Software Solutions Professional who is crafting exceptional systems and applications to solve a diverse range of problems for the past 10 years.

Share the Post

Related Posts