Build a Next.js Interactive Web-Based Simple Conversion Rate Calculator

Written by

in

In today’s interconnected world, dealing with different currencies is commonplace, whether you’re traveling, shopping online, or managing international finances. Manually converting currencies can be tedious and prone to errors. This tutorial will guide you through building a simple, yet effective, conversion rate calculator using Next.js. This project is perfect for beginners and intermediate developers looking to deepen their understanding of Next.js, API integrations, and front-end development.

Why Build a Conversion Rate Calculator?

A conversion rate calculator is a practical tool. It simplifies currency conversions, saving time and reducing the risk of mistakes. It’s also a great learning experience. You’ll gain hands-on experience with:

  • API Integration: Fetching real-time currency exchange rates.
  • State Management: Managing user input and calculated results.
  • UI Design: Creating a user-friendly interface.
  • Next.js Fundamentals: Understanding the basics of Next.js development.

Prerequisites

Before we begin, make sure you have the following:

  • Node.js and npm (or yarn): Installed on your computer.
  • A code editor: Like VS Code, Sublime Text, or Atom.
  • Basic knowledge of HTML, CSS, and JavaScript: Familiarity with these languages is essential.

Step-by-Step Guide

1. Setting Up Your Next.js Project

First, let’s create a new Next.js project using the following command in your terminal:

npx create-next-app currency-converter
cd currency-converter

This command creates a new Next.js project named “currency-converter” and navigates you into the project directory.

2. Installing Dependencies

We’ll need a library to make API requests. Let’s install `axios`:

npm install axios

3. Project Structure

Your project structure should look something like this:


currency-converter/
├── node_modules/
├── pages/
│   └── index.js
├── public/
├── .gitignore
├── next.config.js
├── package-lock.json
├── package.json
└── README.md

4. Creating the User Interface (UI)

Open `pages/index.js` and replace the existing code with the following. This code sets up the basic UI, including input fields for the amount, source currency, and target currency, and a display area for the converted amount.

import { useState } from 'react';
import axios from 'axios';

export default function Home() {
  const [amount, setAmount] = useState('');
  const [fromCurrency, setFromCurrency] = useState('USD');
  const [toCurrency, setToCurrency] = useState('EUR');
  const [convertedAmount, setConvertedAmount] = useState(null);
  const [exchangeRates, setExchangeRates] = useState({});
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState(null);

  // Function to fetch exchange rates (to be implemented later)
  const fetchExchangeRates = async () => {
    // Implementation will go here
  };

  // useEffect to fetch rates on component mount
  useEffect(() => {
    fetchExchangeRates();
  }, []);

  const handleAmountChange = (e) => {
    setAmount(e.target.value);
  };

  const handleFromCurrencyChange = (e) => {
    setFromCurrency(e.target.value);
  };

  const handleToCurrencyChange = (e) => {
    setToCurrency(e.target.value);
  };

  const handleConvert = async () => {
    if (!amount || isNaN(amount)) {
      setError('Please enter a valid amount.');
      setConvertedAmount(null);
      return;
    }

    if (!exchangeRates[fromCurrency] || !exchangeRates[toCurrency]) {
      setError('Exchange rates are not available. Please try again later.');
      setConvertedAmount(null);
      return;
    }

    try {
      setIsLoading(true);
      setError(null);
      const rate = exchangeRates[toCurrency] / exchangeRates[fromCurrency];
      const result = parseFloat(amount) * rate;
      setConvertedAmount(result.toFixed(2));
    } catch (err) {
      setError('An error occurred during conversion.');
      setConvertedAmount(null);
    } finally {
      setIsLoading(false);
    }
  };

  return (
    <div style={{ fontFamily: 'sans-serif', padding: '20px', maxWidth: '500px', margin: '0 auto' }}>
      <h1 style={{ textAlign: 'center' }}>Currency Converter</h1>
      {error && <p style={{ color: 'red', textAlign: 'center' }}>{error}</p>}
      <div style={{ marginBottom: '10px' }}>
        <label htmlFor="amount" style={{ display: 'block', marginBottom: '5px' }}>Amount:</label>
        <input
          type="number"
          id="amount"
          value={amount}
          onChange={handleAmountChange}
          style={{ width: '100%', padding: '8px', border: '1px solid #ccc', borderRadius: '4px' }}
        />
      </div>
      <div style={{ display: 'flex', marginBottom: '10px', gap: '10px' }}>
        <div style={{ flex: 1 }}>
          <label htmlFor="fromCurrency" style={{ display: 'block', marginBottom: '5px' }}>From:</label>
          <select
            id="fromCurrency"
            value={fromCurrency}
            onChange={handleFromCurrencyChange}
            style={{ width: '100%', padding: '8px', border: '1px solid #ccc', borderRadius: '4px' }}
          >
            <option value="USD">USD</option>
            <option value="EUR">EUR</option>
            <option value="GBP">GBP</option>
            <option value="JPY">JPY</option>
            <option value="CAD">CAD</option>
            <!-- Add more currencies as needed -->
          </select>
        </div>
        <div style={{ flex: 1 }}>
          <label htmlFor="toCurrency" style={{ display: 'block', marginBottom: '5px' }}>To:</label>
          <select
            id="toCurrency"
            value={toCurrency}
            onChange={handleToCurrencyChange}
            style={{ width: '100%', padding: '8px', border: '1px solid #ccc', borderRadius: '4px' }}
          >
            <option value="EUR">EUR</option>
            <option value="USD">USD</option>
            <option value="GBP">GBP</option>
            <option value="JPY">JPY</option>
            <option value="CAD">CAD</option>
            <!-- Add more currencies as needed -->
          </select>
        </div>
      </div>
      <button
        onClick={handleConvert}
        disabled={isLoading}
        style={{
          backgroundColor: '#4CAF50',
          color: 'white',
          padding: '10px 15px',
          border: 'none',
          borderRadius: '4px',
          cursor: 'pointer',
          opacity: isLoading ? 0.6 : 1,
        }}
      >
        {isLoading ? 'Converting...' : 'Convert'}
      </button>
      <div style={{ marginTop: '20px', textAlign: 'center' }}>
        {convertedAmount !== null && !error && (
          <p>Converted Amount: {convertedAmount} {toCurrency}</p>
        )}
      </div>
    </div>
  );
}

This code sets up the basic UI, including input fields for the amount, source currency, and target currency, and a display area for the converted amount. We’ve also included basic styling for a better user experience.

5. Fetching Exchange Rates from an API

To get real-time exchange rates, we’ll use a free API. There are many options available; for this tutorial, we will use a free API that provides exchange rates. You can find many free APIs with a quick search. Note that free APIs often have rate limits. Create a free account or get an API key if required by the API you choose.

Here’s how to modify `pages/index.js` to fetch and display exchange rates. Replace the `fetchExchangeRates` function with the code below. Remember to replace `YOUR_API_KEY` with your actual API key. Also, adjust the API endpoint if your chosen API uses a different one.


import { useState, useEffect } from 'react';
import axios from 'axios';

export default function Home() {
  const [amount, setAmount] = useState('');
  const [fromCurrency, setFromCurrency] = useState('USD');
  const [toCurrency, setToCurrency] = useState('EUR');
  const [convertedAmount, setConvertedAmount] = useState(null);
  const [exchangeRates, setExchangeRates] = useState({});
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState(null);

  const API_KEY = 'YOUR_API_KEY'; // Replace with your actual API key
  const API_URL = `https://api.freecurrencyapi.com/v1/latest?apikey=${API_KEY}`;

  const fetchExchangeRates = async () => {
    try {
      const response = await axios.get(API_URL);
      if (response.data && response.data.data) {
        setExchangeRates(response.data.data);
      } else {
        setError('Failed to fetch exchange rates.');
      }
    } catch (err) {
      setError('Failed to fetch exchange rates. Please check your API key and internet connection.');
    }
  };

  useEffect(() => {
    fetchExchangeRates();
  }, []);

  const handleAmountChange = (e) => {
    setAmount(e.target.value);
  };

  const handleFromCurrencyChange = (e) => {
    setFromCurrency(e.target.value);
  };

  const handleToCurrencyChange = (e) => {
    setToCurrency(e.target.value);
  };

  const handleConvert = async () => {
    if (!amount || isNaN(amount)) {
      setError('Please enter a valid amount.');
      setConvertedAmount(null);
      return;
    }

    if (!exchangeRates[fromCurrency] || !exchangeRates[toCurrency]) {
      setError('Exchange rates are not available. Please try again later.');
      setConvertedAmount(null);
      return;
    }

    try {
      setIsLoading(true);
      setError(null);
      const rate = exchangeRates[toCurrency] / exchangeRates[fromCurrency];
      const result = parseFloat(amount) * rate;
      setConvertedAmount(result.toFixed(2));
    } catch (err) {
      setError('An error occurred during conversion.');
      setConvertedAmount(null);
    } finally {
      setIsLoading(false);
    }
  };

  return (
    <div style={{ fontFamily: 'sans-serif', padding: '20px', maxWidth: '500px', margin: '0 auto' }}>
      <h1 style={{ textAlign: 'center' }}>Currency Converter</h1>
      {error && <p style={{ color: 'red', textAlign: 'center' }}>{error}</p>}
      <div style={{ marginBottom: '10px' }}>
        <label htmlFor="amount" style={{ display: 'block', marginBottom: '5px' }}>Amount:</label>
        <input
          type="number"
          id="amount"
          value={amount}
          onChange={handleAmountChange}
          style={{ width: '100%', padding: '8px', border: '1px solid #ccc', borderRadius: '4px' }}
        />
      </div>
      <div style={{ display: 'flex', marginBottom: '10px', gap: '10px' }}>
        <div style={{ flex: 1 }}>
          <label htmlFor="fromCurrency" style={{ display: 'block', marginBottom: '5px' }}>From:</label>
          <select
            id="fromCurrency"
            value={fromCurrency}
            onChange={handleFromCurrencyChange}
            style={{ width: '100%', padding: '8px', border: '1px solid #ccc', borderRadius: '4px' }}
          >
            <option value="USD">USD</option>
            <option value="EUR">EUR</option>
            <option value="GBP">GBP</option>
            <option value="JPY">JPY</option>
            <option value="CAD">CAD</option>
            <!-- Add more currencies as needed -->
          </select>
        </div>
        <div style={{ flex: 1 }}>
          <label htmlFor="toCurrency" style={{ display: 'block', marginBottom: '5px' }}>To:</label>
          <select
            id="toCurrency"
            value={toCurrency}
            onChange={handleToCurrencyChange}
            style={{ width: '100%', padding: '8px', border: '1px solid #ccc', borderRadius: '4px' }}
          >
            <option value="EUR">EUR</option>
            <option value="USD">USD</option>
            <option value="GBP">GBP</option>
            <option value="JPY">JPY</option>
            <option value="CAD">CAD</option>
            <!-- Add more currencies as needed -->
          </select>
        </div>
      </div>
      <button
        onClick={handleConvert}
        disabled={isLoading}
        style={{
          backgroundColor: '#4CAF50',
          color: 'white',
          padding: '10px 15px',
          border: 'none',
          borderRadius: '4px',
          cursor: 'pointer',
          opacity: isLoading ? 0.6 : 1,
        }}
      >
        {isLoading ? 'Converting...' : 'Convert'}
      </button>
      <div style={{ marginTop: '20px', textAlign: 'center' }}>
        {convertedAmount !== null && !error && (
          <p>Converted Amount: {convertedAmount} {toCurrency}</p>
        )}
      </div>
    </div>
  );
}

This code fetches exchange rates when the component mounts using the `useEffect` hook. It then stores the rates in the `exchangeRates` state variable. It also includes error handling to manage potential API request failures.

6. Implementing the Conversion Logic

Now, let’s implement the `handleConvert` function to perform the currency conversion:


const handleConvert = async () => {
  if (!amount || isNaN(amount)) {
    setError('Please enter a valid amount.');
    setConvertedAmount(null);
    return;
  }

  if (!exchangeRates[fromCurrency] || !exchangeRates[toCurrency]) {
    setError('Exchange rates are not available. Please try again later.');
    setConvertedAmount(null);
    return;
  }

  try {
    setIsLoading(true);
    setError(null);
    const rate = exchangeRates[toCurrency] / exchangeRates[fromCurrency];
    const result = parseFloat(amount) * rate;
    setConvertedAmount(result.toFixed(2));
  } catch (err) {
    setError('An error occurred during conversion.');
    setConvertedAmount(null);
  } finally {
    setIsLoading(false);
  }
};

This function:

  • Validates the input amount.
  • Checks if exchange rates are available.
  • Calculates the converted amount using the formula: `amount * (toCurrencyRate / fromCurrencyRate)`.
  • Updates the `convertedAmount` state.
  • Handles errors and displays appropriate messages.

7. Displaying the Converted Amount

Modify the return statement in `pages/index.js` to display the converted amount:


<div style={{ marginTop: '20px', textAlign: 'center' }}>
  {convertedAmount !== null && !error && (
    <p>Converted Amount: {convertedAmount} {toCurrency}</p>
  )}
</div>

This code conditionally renders the converted amount only if it’s available and there are no errors.

8. Running the Application

Save all your changes. Then, run your Next.js application using the following command in your terminal:

npm run dev

Open your browser and go to `http://localhost:3000` to see your currency converter in action.

Common Mistakes and How to Fix Them

1. API Key Errors

Mistake: Forgetting to replace `YOUR_API_KEY` with your actual API key, or using an incorrect API key.

Fix: Double-check your API key and ensure it’s correctly placed in the code. Also, verify that the API key is valid for the API you are using.

2. CORS (Cross-Origin Resource Sharing) Issues

Mistake: Your browser might block API requests due to CORS restrictions.

Fix: If you encounter CORS errors, you might need to:

  • Use a proxy server to forward your requests.
  • Configure CORS headers on the API server (if you have control over it).
  • Use a browser extension that allows you to bypass CORS restrictions for development purposes (not recommended for production).

3. Incorrect API Endpoint

Mistake: Using an incorrect API endpoint URL.

Fix: Carefully review the API documentation to ensure you are using the correct endpoint URL for fetching exchange rates. Also, check if the API requires any specific parameters in the URL.

4. Rate Limiting

Mistake: Exceeding the API’s rate limits.

Fix: Many free APIs have rate limits. If you exceed the limit, you’ll get an error. Implement error handling to gracefully manage rate limiting. Consider adding delays between requests or using a paid API plan if you need higher limits.

5. Data Parsing Errors

Mistake: Incorrectly parsing the API response data.

Fix: Inspect the API response format (using your browser’s developer tools or `console.log`) to understand the structure of the data. Then, adjust your code to correctly extract the exchange rates from the response. Make sure you handle cases where the data might be missing or in an unexpected format.

SEO Best Practices

To ensure your Next.js currency converter ranks well in search engines, consider these SEO best practices:

  • Keyword Optimization: Use relevant keywords like “currency converter,” “exchange rates,” and currency names in your title, headings, and content naturally.
  • Meta Description: Write a concise and compelling meta description (within 160 characters) that accurately describes your currency converter and includes relevant keywords.
  • Heading Tags: Use heading tags (H1-H4) to structure your content logically and include keywords in your headings.
  • Image Alt Text: If you include images, use descriptive alt text that includes relevant keywords.
  • Mobile-Friendly Design: Ensure your currency converter is responsive and works well on all devices.
  • Site Speed: Optimize your Next.js application for fast loading times. This includes code splitting, image optimization, and using a CDN.
  • Internal Linking: If you have other related content on your blog, link to it from your currency converter tutorial.
  • Content Freshness: Regularly update your content and keep your exchange rates data fresh.

Key Takeaways

In this tutorial, you’ve learned how to build a functional currency converter using Next.js. You’ve gained experience with API integration, state management, and UI design. You’ve also learned how to handle errors and implement essential features for a user-friendly application. This project serves as a solid foundation for more complex web applications and demonstrates the power and flexibility of Next.js.

FAQ

1. Can I use a different API for exchange rates?

Yes, absolutely! There are many free and paid APIs available for fetching exchange rates. Just make sure to adjust the API endpoint and data parsing logic in your code to match the API you choose.

2. How do I add more currencies?

To add more currencies, you need to:

  • Update the currency options in the select dropdowns in your UI (`pages/index.js`).
  • Ensure your chosen API supports the new currencies.
  • Update the `fetchExchangeRates` function to fetch rates for the new currencies.

3. How can I improve the UI/UX of my currency converter?

You can improve the UI/UX by:

  • Adding more visual elements, such as currency symbols.
  • Providing real-time updates as the user types in the amount.
  • Implementing a more visually appealing design using CSS frameworks like Tailwind CSS or Bootstrap.
  • Adding a history log to track previous conversions.

4. How can I deploy my currency converter?

You can deploy your Next.js currency converter to platforms like Vercel, Netlify, or AWS. These platforms provide easy deployment options for Next.js applications.

5. What are the best practices for handling API keys securely?

Never hardcode your API key directly into your client-side code (like in `pages/index.js`). Instead, use environment variables. In Next.js, you can use `.env.local` files to store your API keys and access them using `process.env.YOUR_API_KEY`. Remember to keep your `.env.local` file out of your version control system (e.g., add it to your `.gitignore`). For production, consider using a serverless function or a dedicated backend to handle API requests and securely store your API key.

Building this currency converter is more than just a coding exercise; it’s a step toward mastering practical web development skills. As you continue to build and refine this project, you’ll not only enhance your technical abilities but also gain a deeper understanding of how to create useful and engaging web applications. Embrace the learning process, experiment with new features, and enjoy the journey of becoming a more proficient developer.