Building a Currency Converter App in React

currency

In this tutorial, we’ll create a currency converter app using React. This app will allow users to convert amounts between different currencies using real-time exchange rates.

Step 1: Setup

Start by setting up a new React project:

npx create-react-app currency-converter
cd currency-converter

Step 2: Create the Converter Form

Create a component called ConverterForm.js. This component will handle user input and display the conversion result.

Key Features:

  • State Management: Use the useState hook to manage amount, currencies, and results.
  • Currency Swap: A function to swap the selected currencies.
  • API Fetch: Fetch exchange rates from an API and update the result.
import { useEffect, useState } from "react";
import CurrencySelect from "./CurrencySelect";

const ConverterForm = () => {
    const [amount, setAmount] = useState(100);
    const [fromCurrency, setFromCurrency] = useState("USD");
    const [toCurrency, setToCurrency] = useState("INR");
    const [result, setResult] = useState("");
    const [isLoading, setIsLoading] = useState(false);

    // Swap the values of fromCurrency and toCurrency
    const handleSwapCurrencies = () => {
        setFromCurrency(toCurrency);
        setToCurrency(fromCurrency);
    }

    // Function to fetch the exchange rate and update the result
    const getExchangeRate = async () => {
        const API_KEY = "PASTE-YOUR-API-HERE";
        const API_URL = `https://v6.exchangerate-api.com/v6/${API_KEY}/pair/${fromCurrency}/${toCurrency}`;

        if (isLoading) return;
        setIsLoading(true);

        try {
            const response = await fetch(API_URL);
            if (!response.ok) throw Error("Something went wrong!");

            const data = await response.json();
            const rate = (data.conversion_rate * amount).toFixed(2);
            setResult(`${amount} ${fromCurrency} = ${rate} ${toCurrency}`);
        } catch (error) {
            setResult("Something went wrong!");
        } finally {
            setIsLoading(false);
        }
    }

    // Handle form submission
    const handleFormSubmit = (e) => {
        e.preventDefault();
        getExchangeRate();
    }

    // Fetch exchange rate on initial render
    // eslint-disable-next-line react-hooks/exhaustive-deps
    useEffect(() => getExchangeRate, []);

    return (
        <form className="converter-form" onSubmit={handleFormSubmit}>
            <div className="form-group">
                <label className="form-label">Enter Amount</label>
                <input
                    type="number"
                    className="form-input"
                    value={amount}
                    onChange={(e) => setAmount(e.target.value)}
                    required
                />
            </div>

            <div className="form-group form-currency-group">
                <div className="form-section">
                    <label className="form-label">From</label>
                    <CurrencySelect
                        selectedCurrency={fromCurrency}
                        handleCurrency={e => setFromCurrency(e.target.value)}
                    />
                </div>

                <div className="swap-icon" onClick={handleSwapCurrencies}>
                    <svg width="16" viewBox="0 0 20 19" xmlns="http://www.w3.org/2000/svg">
                        <path d="M19.13 11.66H.22a.22.22 0 0 0-.22.22v1.62a.22.22 0 0 0 .22.22h16.45l-3.92 4.94a.22.22 0 0 0 .17.35h1.97c.13 0 .25-.06.33-.16l4.59-5.78a.9.9 0 0 0-.7-1.43zM19.78 5.29H3.34L7.26.35A.22.22 0 0 0 7.09 0H5.12a.22.22 0 0 0-.34.16L.19 5.94a.9.9 0 0 0 .68 1.4H19.78a.22.22 0 0 0 .22-.22V5.51a.22.22 0 0 0-.22-.22z"
                            fill="#fff" />
                    </svg>
                </div>

                <div className="form-section">
                    <label className="form-label">To</label>
                    <CurrencySelect
                        selectedCurrency={toCurrency}
                        handleCurrency={e => setToCurrency(e.target.value)}
                    />
                </div>
            </div>

            <button type="submit" className={`${isLoading ? "loading" : ""} submit-button`}>Get Exchange Rate</button>
            <p className="exchange-rate-result">
                {/* Display the conversion result */}
                {isLoading ? "Getting exchange rate..." : result}
            </p>
        </form>
    )
}

export default ConverterForm;
JSX

Step 3: Currency Select Component

Create a CurrencySelect.js component for currency selection:

// Array of currency codes
const currencyCodes = [
    "AED", "AFN", "ALL", "AMD", "ANG", "AOA", "ARS", "AUD", "AWG", "AZN",
    "BAM", "BBD", "BDT", "BGN", "BHD", "BIF", "BMD", "BND", "BOB", "BRL",
    "BSD", "BTN", "BWP", "BYN", "BZD", "CAD", "CDF", "CHF", "CLP", "CNY",
    "COP", "CRC", "CUP", "CVE", "CZK", "DJF", "DKK", "DOP", "DZD", "EGP",
    "ERN", "ETB", "EUR", "FJD", "FKP", "FOK", "GBP", "GEL", "GGP", "GHS",
    "GIP", "GMD", "GNF", "GTQ", "GYD", "HKD", "HNL", "HRK", "HTG", "HUF",
    "IDR", "ILS", "IMP", "INR", "IQD", "IRR", "ISK", "JEP", "JMD", "JOD",
    "JPY", "KES", "KGS", "KHR", "KID", "KMF", "KRW", "KWD", "KYD", "KZT",
    "LAK", "LBP", "LKR", "LRD", "LSL", "LYD", "MAD", "MDL", "MGA", "MKD",
    "MMK", "MNT", "MOP", "MRU", "MUR", "MVR", "MWK", "MXN", "MYR", "MZN",
    "NAD", "NGN", "NIO", "NOK", "NPR", "NZD", "OMR", "PAB", "PEN", "PGK",
    "PHP", "PKR", "PLN", "PYG", "QAR", "RON", "RSD", "RUB", "RWF", "SAR",
    "SBD", "SCR", "SDG", "SEK", "SGD", "SHP", "SLE", "SLL", "SOS", "SRD",
    "SSP", "STN", "SYP", "SZL", "THB", "TJS", "TMT", "TND", "TOP", "TRY",
    "TTD", "TVD", "TWD", "TZS", "UAH", "UGX", "USD", "UYU", "UZS", "VES",
    "VND", "VUV", "WST", "XAF", "XCD", "XOF", "XPF", "YER", "ZAR", "ZMW",
    "ZWL"
];

const CurrencySelect = ({ selectedCurrency, handleCurrency }) => {
    // Extract the country code from the selected currency code
    const countryCode = selectedCurrency.substring(0, 2);

    return (
        <div className="currency-select">
            <img src={`https://flagsapi.com/${countryCode}/flat/64.png`} alt="Flag" />
            <select
                onChange={handleCurrency}
                className="currency-dropdown"
                value={selectedCurrency}
            >
                {currencyCodes.map(currency => (
                    <option key={currency} value={currency}>{currency}</option>
                ))}
            </select>
        </div>
    )
}

export default CurrencySelect;
JSX

This component takes the selected currency and a handler function as props.

Step 4: Fetching Exchange Rates

In the ConverterForm, use the fetch API to retrieve exchange rates:

import ConverterForm from "./components/ConverterForm"

const App = () => {
  return (
    <div className="currency-converter">
      <h2 className="converter-title">Currency Converter</h2>
      <ConverterForm />
    </div>
  )
}

export default App;
JSX

Ensure to handle loading states and errors properly.

Step 5: Styling the App

/* Import Google Font */
@import url('https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,100..900;1,100..900&display=swap');

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
  font-family: "Montserrat", sans-serif;
}

body {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 100vh;
  background: url("bg.png") #030728 no-repeat center;
}

#root {
  width: 100%;
}

.currency-converter {
  max-width: 410px;
  margin: 0 auto;
  padding: 40px 30px 50px;
  border-radius: 8px;
  backdrop-filter: blur(30px);
  background: rgba(2, 7, 40, 0.5);
  border: 1px solid rgba(255, 255, 255, 0.3);
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5);
}

.currency-converter .converter-title {
  color: #fff;
  font-size: 1.65rem;
  font-weight: 600;
  text-align: center;
}

.currency-converter .converter-form {
  margin-top: 45px;
}

.converter-form .form-group {
  display: flex;
  margin-bottom: 30px;
  flex-direction: column;
}

.converter-form .form-group .form-label {
  color: #fff;
  font-weight: 500;
  display: block;
  margin-bottom: 9px;
  font-size: 1rem;
}

.converter-form .form-group .form-input {
  outline: none;
  font-size: 1.1rem;
  padding: 0 15px;
  color: #fff;
  font-weight: 500;
  min-height: 48px;
  border-radius: 6px;
  background: rgba(255, 255, 255, 0.1);
  border: 1px solid rgba(255, 255, 255, 0.5);
}

.converter-form .form-currency-group {
  flex-direction: row;
  align-items: center;
  justify-content: space-between;
}

.form-currency-group .currency-select {
  display: flex;
  padding: 0 10px;
  min-height: 45px;
  align-items: center;
  border-radius: 6px;
  background: rgba(255, 255, 255, 0.1);
  border: 1px solid rgba(255, 255, 255, 0.5);
}

.form-currency-group .currency-select img {
  width: 25px;
}

.form-currency-group .currency-select .currency-dropdown {
  outline: none;
  border: none;
  background: none;
  color: #fff;
  font-size: 1rem;
  font-weight: 500;
  padding: 0 10px 0 5px;
}

.form-currency-group .currency-select .currency-dropdown option {
  color: #000;
  font-weight: 500;
}

.form-currency-group .swap-icon {
  height: 40px;
  width: 40px;
  cursor: pointer;
  display: flex;
  margin-top: 25px;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.1);
  border: 1px solid rgba(255, 255, 255, 0.5);
  transition: 0.2s ease;
}

.form-currency-group .swap-icon:hover {
  background: rgba(255, 255, 255, 0.3);
}

.converter-form .submit-button {
  width: 100%;
  min-height: 52px;
  border-radius: 6px;
  border: none;
  outline: none;
  font-size: 1rem;
  font-weight: 600;
  cursor: pointer;
  margin-top: 5px;
  transition: 0.2s ease;
}

.converter-form .submit-button.loading {
  opacity: 0.7;
  pointer-events: none;
}

.converter-form .submit-button:hover {
  background: rgba(255, 255, 255, 0.7);
}

.converter-form .exchange-rate-result {
  color: #fff;
  font-size: 1.1rem;
  font-weight: 600;
  text-align: center;
  padding: 25px 0;
  margin-top: 25px;
  border-radius: 6px;
  letter-spacing: 0.5px;
  background: rgba(255, 255, 255, 0.15);
}

@media (max-width: 640px) {
  body {
    padding: 0 10px;
  }

  .currency-converter {
    padding: 30px 20px 40px;
  }
}
CSS

Use CSS to enhance the UI, making it user-friendly and visually appealing.

Step 6: Putting It All Together

In App.js, import and use the ConverterForm:

import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.jsx'
import './index.css'

ReactDOM.createRoot(document.getElementById('root')).render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
)
CSS

Final Touches

  • API Key: Make sure to replace "PASTE-YOUR-API-HERE" with your actual API key from ExchangeRate-API.
  • Testing: Test the app thoroughly to ensure accurate conversions and proper error handling.

Conclusion

With these steps, you now have a fully functional currency converter app. It fetches real-time exchange rates and allows easy conversions between currencies. Customize the app further to add features like historical data or graphical representations of exchange rates.

Happy coding!

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply

Your email address will not be published. Required fields are marked *