In today’s interconnected world, dealing with different currencies is a common experience. Whether you’re traveling, shopping online, or managing international finances, having a quick and reliable way to convert currencies is incredibly useful. This tutorial will guide you through building a simple yet functional currency converter application using Next.js, a popular React framework known for its server-side rendering and excellent performance. This project is perfect for beginners and intermediate developers looking to enhance their skills in web development and learn more about working with APIs.
Why Build a Currency Converter?
Currency converters are practical tools. They solve the everyday problem of needing to understand the value of money across different economies. They are also a great way to learn about fetching data from external APIs, handling user input, and displaying dynamic content. Building this application will give you hands-on experience with these fundamental web development concepts.
What You’ll Learn
By the end of this tutorial, you’ll have built a fully functional currency converter application. You will learn:
- How to set up a Next.js project.
- How to fetch data from a public API.
- How to handle user input.
- How to display dynamic content.
- How to style your application using basic CSS.
Prerequisites
Before starting this tutorial, you should have a basic understanding of:
- HTML, CSS, and JavaScript.
- React fundamentals (components, props, state).
- Node.js and npm (or yarn).
Setting Up Your Next.js Project
Let’s start by creating a new Next.js project. Open your terminal and run the following command:
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. Next.js sets up a basic project structure with everything you need to get started.
Project Structure Overview
Before diving into the code, let’s briefly look at the project structure:
pages/: This directory contains your application’s pages. Each file in this directory represents a route in your application. For instance,pages/index.jswill be the homepage.public/: This directory is for static assets like images, fonts, and other files you want to serve directly.styles/: Here, you’ll find your CSS or styling files. The default setup includes a global stylesheet.package.json: This file lists your project’s dependencies and scripts.
Installing Dependencies
For this project, we’ll need to fetch currency exchange rates from an API. We’ll use the axios library for making HTTP requests. Install it using:
npm install axios
Fetching Currency Data from an API
We’ll use a free currency exchange rate API to get the conversion rates. There are many free APIs available; for this tutorial, we will use a hypothetical API endpoint (replace with a real API later):
const API_URL = 'https://api.example.com/currency/rates'; // Replace with a real API endpoint
Create a new file called utils/api.js in your project directory. This file will handle fetching data from the API. Add the following code:
import axios from 'axios';
const API_URL = 'https://api.example.com/currency/rates'; // Replace with a real API endpoint
export async function getExchangeRates() {
try {
const response = await axios.get(API_URL);
return response.data;
} catch (error) {
console.error('Error fetching exchange rates:', error);
return null;
}
}
This code:
- Imports the
axioslibrary. - Defines the API endpoint. Important: Replace
'https://api.example.com/currency/rates'with a valid API URL from a real currency exchange rate API. You might need to sign up for a free account to get an API key. - Creates an asynchronous function
getExchangeRates()that fetches data from the API. - Uses a
try...catchblock to handle potential errors during the API request.
Building the UI: The Currency Converter Component
Now, let’s build the user interface for our currency converter. We’ll modify the pages/index.js file, which represents our homepage. Replace the existing code with the following:
import { useState, useEffect } from 'react';
import { getExchangeRates } from '../utils/api';
export default function Home() {
const [fromCurrency, setFromCurrency] = useState('USD');
const [toCurrency, setToCurrency] = useState('EUR');
const [amount, setAmount] = useState(1);
const [rates, setRates] = useState(null);
const [convertedAmount, setConvertedAmount] = useState(null);
useEffect(() => {
async function fetchData() {
const data = await getExchangeRates();
setRates(data?.rates); // Assuming the API returns an object with currency rates
}
fetchData();
}, []);
useEffect(() => {
if (rates) {
calculateConversion();
}
}, [rates, fromCurrency, toCurrency, amount]);
const calculateConversion = () => {
if (!rates) return;
const fromRate = rates[fromCurrency];
const toRate = rates[toCurrency];
if (!fromRate || !toRate) {
setConvertedAmount('Invalid currency');
return;
}
const converted = (amount / fromRate) * toRate;
setConvertedAmount(converted.toFixed(2));
};
const handleFromCurrencyChange = (event) => {
setFromCurrency(event.target.value);
};
const handleToCurrencyChange = (event) => {
setToCurrency(event.target.value);
};
const handleAmountChange = (event) => {
const value = parseFloat(event.target.value);
setAmount(isNaN(value) ? 0 : value);
};
return (
<div style={{ fontFamily: 'sans-serif', padding: '20px' }}>
<h1 style={{ textAlign: 'center' }}>Currency Converter</h1>
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', maxWidth: '400px', margin: '0 auto' }}>
<label htmlFor="amount" style={{ marginBottom: '5px' }}>Amount:</label>
<input
type="number"
id="amount"
value={amount}
onChange={handleAmountChange}
style={{ marginBottom: '10px', padding: '8px', borderRadius: '4px', border: '1px solid #ccc', width: '100%' }}
/>
<div style={{ display: 'flex', justifyContent: 'space-between', width: '100%', marginBottom: '10px' }}>
<div style={{ width: '48%' }}>
<label htmlFor="fromCurrency" style={{ marginBottom: '5px', display: 'block' }}>From:</label>
<select
id="fromCurrency"
value={fromCurrency}
onChange={handleFromCurrencyChange}
style={{ padding: '8px', borderRadius: '4px', border: '1px solid #ccc', width: '100%' }}
>
<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={{ width: '48%' }}>
<label htmlFor="toCurrency" style={{ marginBottom: '5px', display: 'block' }}>To:</label>
<select
id="toCurrency"
value={toCurrency}
onChange={handleToCurrencyChange}
style={{ padding: '8px', borderRadius: '4px', border: '1px solid #ccc', width: '100%' }}
>
<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>
<p style={{ fontSize: '1.2em', fontWeight: 'bold' }}>Converted Amount: {convertedAmount ? convertedAmount : '0.00'}</p>
</div>
</div>
);
}
Let’s break down this code:
- Import Statements: We import
useStateanduseEffectfrom React, andgetExchangeRatesfrom ourutils/api.jsfile. - State Variables:
fromCurrency: The currency to convert from (e.g., USD).toCurrency: The currency to convert to (e.g., EUR).amount: The amount to convert.rates: An object containing the exchange rates fetched from the API (e.g., { USD: 1, EUR: 0.92, GBP: 0.78, … }).convertedAmount: The result of the conversion.
useEffectHook (Fetch Rates):- This hook runs once when the component mounts (the empty dependency array
[]). - It calls
getExchangeRates()to fetch the exchange rates from the API. - It updates the
ratesstate with the fetched data. The?.ratespart handles potential errors if the API returns a different structure.
- This hook runs once when the component mounts (the empty dependency array
useEffectHook (Calculate Conversion):- This hook runs whenever
rates,fromCurrency,toCurrency, oramountchange. - It calls
calculateConversion()to perform the currency conversion.
- This hook runs whenever
calculateConversionFunction:- Checks if the
ratesare available. - Retrieves the exchange rates for the selected currencies.
- Performs the conversion:
(amount / fromRate) * toRate. - Formats the result to two decimal places using
toFixed(2). - Updates the
convertedAmountstate.
- Checks if the
- Event Handlers:
handleFromCurrencyChange: Updates thefromCurrencystate when the user selects a different “From” currency.handleToCurrencyChange: Updates thetoCurrencystate when the user selects a different “To” currency.handleAmountChange: Updates theamountstate when the user enters a new amount. It also usesparseFloat()to convert the input to a number andisNaN()to handle invalid input.
- JSX (User Interface):
- A simple layout with a heading, an input field for the amount, two dropdowns for selecting currencies, and a paragraph to display the converted amount.
- Uses inline styles for basic styling. Consider using CSS or a CSS-in-JS solution (like styled-components or Emotion) for more complex styling in a real-world project.
- The dropdowns (
<select>elements) are populated with a few example currencies. You can add more as needed. - The
valueandonChangeprops are used to bind the input fields and dropdowns to the component’s state, making them interactive.
Running Your Application
To run your application, open your terminal, navigate to your project directory (currency-converter), and run the following command:
npm run dev
This will start the development server. Open your web browser and go to http://localhost:3000 to see your currency converter in action. You should see the input fields, dropdowns, and the converted amount updated in real-time as you change the inputs.
Styling Your Application
The current styling is very basic. Let’s add some basic CSS to improve the appearance. You can either add styles directly to the JSX using inline styles (as we’ve done), or you can create a separate CSS file. For larger projects, a separate CSS file is generally preferred for better organization and maintainability.
Here’s an example of how you can add a separate CSS file. Create a file named styles/Home.module.css in your project directory (or any other name you prefer). Add the following CSS rules:
.container {
display: flex;
flex-direction: column;
align-items: center;
max-width: 400px;
margin: 0 auto;
padding: 20px;
font-family: sans-serif;
}
h1 {
text-align: center;
}
.input {
margin-bottom: 10px;
padding: 8px;
border-radius: 4px;
border: 1px solid #ccc;
width: 100%;
}
.currency-select {
padding: 8px;
border-radius: 4px;
border: 1px solid #ccc;
width: 100%;
}
.currency-container {
display: flex;
justify-content: space-between;
width: 100%;
margin-bottom: 10px;
}
.currency-input {
width: 48%;
}
p {
font-size: 1.2em;
font-weight: bold;
}
Then, import this CSS file into your pages/index.js file and apply the classes to your elements:
import { useState, useEffect } from 'react';
import { getExchangeRates } from '../utils/api';
import styles from '../styles/Home.module.css'; // Import the CSS module
export default function Home() {
// ... (rest of the component code)
return (
<div className={styles.container}>
<h1>Currency Converter</h1>
<div>
<label htmlFor="amount">Amount:</label>
<input
type="number"
id="amount"
value={amount}
onChange={handleAmountChange}
className={styles.input}
/>
<div className={styles.currency-container}>
<div className={styles.currency-input}>
<label htmlFor="fromCurrency" style={{ display: 'block' }}>From:</label>
<select
id="fromCurrency"
value={fromCurrency}
onChange={handleFromCurrencyChange}
className={styles.currency-select}
>
<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 className={styles.currency-input}>
<label htmlFor="toCurrency" style={{ display: 'block' }}>To:</label>
<select
id="toCurrency"
value={toCurrency}
onChange={handleToCurrencyChange}
className={styles.currency-select}
>
<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>
<p>Converted Amount: {convertedAmount ? convertedAmount : '0.00'}</p>
</div>
</div>
);
}
This approach uses CSS modules, which automatically scope your CSS to the component, preventing style conflicts with other parts of your application. You can explore other styling options like Tailwind CSS or styled-components for more advanced styling capabilities.
Error Handling
Our code includes basic error handling for API requests (in the getExchangeRates function) and for invalid currency selections in the calculateConversion function. However, you can enhance error handling further:
- API Errors: Display user-friendly error messages if the API request fails (e.g., “Failed to fetch exchange rates. Please try again later.”). Use the
useStatehook to manage an error state. - Invalid Input: Provide more informative messages if the user enters invalid input (e.g., non-numeric values for the amount).
- Currency Not Found: If the API returns a currency rate that is not available, handle it gracefully by displaying an appropriate message (e.g., “Currency not supported.”).
Common Mistakes and How to Fix Them
Here are some common mistakes and how to avoid them:
- Incorrect API Endpoint: Make sure you are using a valid API endpoint and that the API key (if required) is correctly configured. Double-check the API documentation.
- CORS Issues: If you are getting CORS (Cross-Origin Resource Sharing) errors, it means the API you are trying to access has restrictions on which domains can access it. You might need to use a proxy server or configure CORS settings on the API server (if you have control over it). For local development, you might be able to use a browser extension to disable CORS temporarily. In production, you’ll need a proper solution.
- Incorrect Data Parsing: The API might return data in a different format than you expect. Inspect the API response using your browser’s developer tools (Network tab) to understand the data structure and adjust your code accordingly. Use
console.log()to inspect the response data. - State Updates: Be careful when updating state variables. Ensure you are using the correct state update functions (e.g.,
setAmount,setFromCurrency) and that you are not accidentally overwriting state. - Performance: If you are fetching data frequently, consider implementing techniques like caching or debouncing to optimize performance and reduce the number of API requests.
Key Takeaways
In this tutorial, we’ve successfully built a basic currency converter application using Next.js. We’ve covered the following key concepts:
- Setting up a Next.js project.
- Fetching data from an external API using
axios. - Managing component state with
useStateanduseEffect. - Handling user input.
- Displaying dynamic content based on API data and user input.
- Basic styling using CSS modules.
This project provides a solid foundation for understanding how to build interactive web applications that interact with external data sources. Remember to always validate and sanitize user input to prevent security vulnerabilities.
FAQ
- Q: Where can I find a free currency exchange rate API?
A: There are many free APIs available. Some popular options include ExchangeRate-API, CurrencyLayer (offers a free tier), and FreeCurrencyConverterAPI. Be sure to check the API’s terms of service and usage limits.
- Q: How can I add more currencies to the dropdowns?
A: You’ll need to update the options in the
<select>elements with the currency codes supported by your chosen API. You might also need to modify thegetExchangeRatesfunction to fetch rates for those currencies from the API. Consider fetching the available currency codes from the API and dynamically generating the<option>elements for a more flexible solution. - Q: How do I deploy this application?
A: Next.js applications can be deployed to various platforms, including Vercel (recommended, as it’s built by the Next.js team), Netlify, and other hosting providers. You typically need to push your code to a Git repository (like GitHub or GitLab) and configure your hosting provider to deploy from that repository. Vercel makes this process very easy.
- Q: How can I improve the user experience?
A: Consider adding features like:
- Currency symbols next to the amount and converted amount.
- A visual representation of the conversion rates (e.g., a chart).
- Saving the user’s preferred currencies.
- Error handling for network issues.
Building this currency converter is just the beginning. The skills you’ve acquired can be applied to a wide range of web development projects. Experiment with different APIs, explore more advanced styling techniques, and add more features to the application. The more you practice and experiment, the more proficient you’ll become in building dynamic and engaging web applications. Keep learning, keep building, and enjoy the process!
