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;
JSXStep 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;
JSXThis 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;
JSXEnsure 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;
}
}
CSSUse 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>,
)
CSSFinal 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!