DEV Community

Cover image for Build A Currency Converter in React JS | Best Beginner React JS Project.
Sudhanshu Gaikwad
Sudhanshu Gaikwad

Posted on • Edited on • Originally published at dev.to

Build A Currency Converter in React JS | Best Beginner React JS Project.

Are you new to React.js and looking for a beginner-friendly project to boost your skills? Creating a currency converter app using React.js and CSS is a perfect choice. This project helps you understand key concepts like components, state management, and working with APIs, while also giving you a useful tool to showcase in your portfolio.

The app will also include additional features:

  • Displaying country flags based on currency codes.
  • A handy "swap" button to switch between the selected currencies.

Why Create a Currency Converter App in React.js?
This project is not only fun but also a great learning experience. Here's what you'll gain by building it:

Master React.js Basics
Learn how to create and manage React components.
Work with hooks like useState and useEffect to handle app logic.

API Integration
Fetch real-time exchange rates from an API.
Understand asynchronous operations and data fetching in React.

CSS for Better UI
Build a clean and responsive user interface with CSS.
Customize your design without relying on libraries like Bootstrap.

Real-Life Usability
Create a practical application that demonstrates your coding skills.
Add this project to your portfolio to impress potential employers or clients.

📂Project Folder Structure

Currency_Converter/
├── src/
   ├── Currency_App/
      ├── component/
         ├── ConverterForm.jsx
         ├── CurrencySelect.jsx
      └── styles/
          └── index.css
   ├── App.js
   └── index.js
├── public/
   └── index.html
├── package.json
└── README.md
Enter fullscreen mode Exit fullscreen mode

index.css

The CSS adds a modern touch with gradients and blur effects.

@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: "Roboto Condensed", sans-serif;
}

body {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 100vh;
background: rgb(131,58,180);
background: linear-gradient(63deg, rgba(131,58,180,1) 0%, rgba(255,0,0,1) 50%, rgba(252,176,69,1) 100%);
}

#root {
  width: 100%;
}

.currency-converter {
  font-family: "Roboto Condensed", sans-serif;
  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);
}

.Last_text {
  color: white;
  text-align: center;
  margin-top: 15px;
}
@media (max-width: 640px) {
  body {
    padding: 0 10px;
  }

  .currency-converter {
    padding: 30px 20px 40px;
  }
}
Enter fullscreen mode Exit fullscreen mode

App.js

This is the main entry point where the ConverterForm component is rendered.

import ConverterForm from "./Currency_App/component/ConverterForm";

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

export default App;
Enter fullscreen mode Exit fullscreen mode

ConverterForm.jsx

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 = "3a2271a8d96fe3a0143bb0d5";
    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`}
      >
         Exchange Rate
      </button>
      <p className="exchange-rate-result">
        {/* Display the conversion result */}
        {isLoading ? "Getting exchange rate..." : result}
      </p>
      <p className="Last_text">Designed By Sudhanshu Gaikwad</p>
    </form>
  );
};

export default ConverterForm;
Enter fullscreen mode Exit fullscreen mode

CurrencySelect.jsx

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;
Enter fullscreen mode Exit fullscreen mode

Looks like Currency Converter Webapp.


I hope you enjoy exploring and learning from this project! In the comments below, let me know your thoughts, suggestions, or improvements.

Happy coding! 😊

Top comments (0)