Build a Next.js Interactive Web-Based Unit Converter

Written by

in

In the digital age, we’re constantly bombarded with data, and often, that data needs to be converted from one unit to another. Whether you’re a student, a professional, or just someone curious about the world, the ability to quickly and accurately convert units is a valuable skill. Imagine trying to bake a cake and realizing the recipe uses grams while your kitchen scale measures in ounces. Or perhaps you’re planning a trip and need to convert kilometers to miles. These are everyday scenarios where a unit converter comes to the rescue. This tutorial will guide you through building a simple, yet functional, web-based unit converter using Next.js, a powerful React framework.

Why Build a Unit Converter?

Creating a unit converter is an excellent project for both beginners and intermediate developers. It allows you to:

  • Learn fundamental concepts: You’ll solidify your understanding of React components, state management, event handling, and basic JavaScript logic.
  • Practice practical skills: You’ll work with user input, perform calculations, and display results dynamically.
  • Build something useful: A unit converter is a practical tool that you can use daily.
  • Enhance your portfolio: Showcasing this project demonstrates your ability to build interactive web applications.

Furthermore, Next.js provides a great development experience with features like server-side rendering, static site generation, and optimized performance, making it an ideal choice for this project.

Prerequisites

Before we begin, make sure you have the following installed on your system:

  • Node.js and npm (or yarn): You’ll need Node.js to run JavaScript on your server and npm (Node Package Manager) or yarn to manage project dependencies. You can download them from nodejs.org.
  • A code editor: Choose your favorite code editor, such as VS Code, Sublime Text, or Atom.
  • Basic knowledge of HTML, CSS, and JavaScript: Familiarity with these languages will make it easier to follow along.

Setting Up Your Next.js Project

Let’s get started by creating a new Next.js project. Open your terminal and run the following command:

npx create-next-app unit-converter

This command will create a new directory called unit-converter and set up a basic Next.js project structure for you. Navigate into the project directory:

cd unit-converter

Now, start the development server:

npm run dev

Open your web browser and go to http://localhost:3000. You should see the default Next.js welcome page.

Project Structure

Before we dive into the code, let’s briefly understand the project structure. The key files and directories we’ll be working with are:

  • pages/index.js: This is the main page of our application. We’ll write the HTML, CSS, and JavaScript for our unit converter here.
  • styles/globals.css: This file contains global CSS styles that apply to the entire application.

Building the User Interface (UI)

Let’s start by designing the UI for our unit converter. We’ll create a simple interface with the following elements:

  • Input field: Where the user enters the value to be converted.
  • Select dropdowns: One for selecting the ‘from’ unit and another for the ‘to’ unit.
  • Output display: Where the converted value will be shown.

Open pages/index.js and replace the existing code with the following:

import { useState } from 'react';

export default function Home() {
  const [inputValue, setInputValue] = useState('');
  const [fromUnit, setFromUnit] = useState('meters');
  const [toUnit, setToUnit] = useState('feet');
  const [result, setResult] = useState('');

  return (
    <div className="container">
      <h1>Unit Converter</h1>
      <div className="input-group">
        <label htmlFor="inputValue">Value:</label>
        <input
          type="number"
          id="inputValue"
          value={inputValue}
          onChange={(e) => setInputValue(e.target.value)}
        />
      </div>

      <div className="select-group">
        <label htmlFor="fromUnit">From:</label>
        <select id="fromUnit" value={fromUnit} onChange={(e) => setFromUnit(e.target.value)}>
          <option value="meters">Meters</option>
          <option value="feet">Feet</option>
          <option value="inches">Inches</option>
        </select>

        <label htmlFor="toUnit">To:</label>
        <select id="toUnit" value={toUnit} onChange={(e) => setToUnit(e.target.value)}>
          <option value="meters">Meters</option>
          <option value="feet">Feet</option>
          <option value="inches">Inches</option>
        </select>
      </div>

      <div className="result-group">
        <p>Result: {result}</p>
      </div>
    </div>
  );
}

In this code:

  • We import the useState hook from React to manage the state of our component.
  • We initialize state variables: inputValue (the value entered by the user), fromUnit (the unit to convert from), toUnit (the unit to convert to), and result (the converted value).
  • We create an input field for the user to enter the value. The onChange event handler updates the inputValue state.
  • We create two select dropdowns for the user to choose the units. The onChange event handlers update the fromUnit and toUnit states.
  • We display the result in a paragraph.

Next, let’s add some basic styling to make the UI look better. Open styles/globals.css and add the following CSS:

body {
  font-family: sans-serif;
  margin: 0;
  padding: 0;
  background-color: #f4f4f4;
}

.container {
  max-width: 600px;
  margin: 20px auto;
  padding: 20px;
  background-color: #fff;
  border-radius: 8px;
  box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}

h1 {
  text-align: center;
  color: #333;
}

.input-group, .select-group, .result-group {
  margin-bottom: 15px;
}

label {
  display: block;
  margin-bottom: 5px;
  font-weight: bold;
}

input[type="number"], select {
  width: 100%;
  padding: 10px;
  border: 1px solid #ccc;
  border-radius: 4px;
  box-sizing: border-box;
  margin-bottom: 10px;
}

.result-group p {
  font-size: 1.2em;
}

This CSS provides basic styling for the container, headings, labels, input fields, and select dropdowns, making the UI more visually appealing.

Now, if you refresh your browser, you should see the basic UI of the unit converter.

Implementing the Conversion Logic

The core of our unit converter is the conversion logic. We need to define the conversion factors between the different units. Let’s create a function to handle the conversion.

Add the following code inside the Home component, just before the return statement:


  const convert = () => {
    if (!inputValue) {
      setResult('');
      return;
    }

    const value = parseFloat(inputValue);

    if (isNaN(value)) {
      setResult('Invalid input');
      return;
    }

    let convertedValue = 0;

    // Conversion factors (meters to...) -  Consider adding more units and conversions
    if (fromUnit === 'meters' && toUnit === 'feet') {
      convertedValue = value * 3.28084;
    } else if (fromUnit === 'meters' && toUnit === 'inches') {
      convertedValue = value * 39.3701;
    } else if (fromUnit === 'feet' && toUnit === 'meters') {
      convertedValue = value / 3.28084;
    } else if (fromUnit === 'feet' && toUnit === 'inches') {
      convertedValue = value * 12;
    } else if (fromUnit === 'inches' && toUnit === 'meters') {
      convertedValue = value / 39.3701;
    } else if (fromUnit === 'inches' && toUnit === 'feet') {
      convertedValue = value / 12;
    }
    else if(fromUnit === toUnit) {
        convertedValue = value;
    }

    setResult(convertedValue.toFixed(2)); // Round to 2 decimal places
  };

Let’s break down this code:

  • The convert function is defined.
  • It first checks if the inputValue is empty. If so, it resets the result and returns.
  • It parses the inputValue to a floating-point number using parseFloat().
  • It checks if the parsed value is a valid number using isNaN(). If not, it sets the result to “Invalid input” and returns.
  • It uses a series of if/else if statements to determine the conversion factor based on the selected units.
  • It performs the conversion calculation.
  • It uses toFixed(2) to round the result to two decimal places for better readability.
  • It updates the result state with the converted value.

Now, we need to call this convert function whenever the input value or the selected units change. Modify the onChange event handlers for the input and select elements to call the convert function:


  <input
    type="number"
    id="inputValue"
    value={inputValue}
    onChange={(e) => {
      setInputValue(e.target.value);
      convert(); // Call convert on input change
    }}
  />

  <select id="fromUnit" value={fromUnit} onChange={(e) => {
      setFromUnit(e.target.value);
      convert(); // Call convert on unit change
    }}>

Your complete pages/index.js file should now look like this:

import { useState } from 'react';

export default function Home() {
  const [inputValue, setInputValue] = useState('');
  const [fromUnit, setFromUnit] = useState('meters');
  const [toUnit, setToUnit] = useState('feet');
  const [result, setResult] = useState('');

  const convert = () => {
    if (!inputValue) {
      setResult('');
      return;
    }

    const value = parseFloat(inputValue);

    if (isNaN(value)) {
      setResult('Invalid input');
      return;
    }

    let convertedValue = 0;

    // Conversion factors (meters to...)
    if (fromUnit === 'meters' && toUnit === 'feet') {
      convertedValue = value * 3.28084;
    } else if (fromUnit === 'meters' && toUnit === 'inches') {
      convertedValue = value * 39.3701;
    } else if (fromUnit === 'feet' && toUnit === 'meters') {
      convertedValue = value / 3.28084;
    } else if (fromUnit === 'feet' && toUnit === 'inches') {
      convertedValue = value * 12;
    } else if (fromUnit === 'inches' && toUnit === 'meters') {
      convertedValue = value / 39.3701;
    } else if (fromUnit === 'inches' && toUnit === 'feet') {
      convertedValue = value / 12;
    }
    else if(fromUnit === toUnit) {
        convertedValue = value;
    }

    setResult(convertedValue.toFixed(2)); // Round to 2 decimal places
  };

  return (
    <div className="container">
      <h1>Unit Converter</h1>
      <div className="input-group">
        <label htmlFor="inputValue">Value:</label>
        <input
          type="number"
          id="inputValue"
          value={inputValue}
          onChange={(e) => {
            setInputValue(e.target.value);
            convert(); // Call convert on input change
          }}
        />
      </div>

      <div className="select-group">
        <label htmlFor="fromUnit">From:</label>
        <select id="fromUnit" value={fromUnit} onChange={(e) => {
            setFromUnit(e.target.value);
            convert(); // Call convert on unit change
          }}>
          <option value="meters">Meters</option>
          <option value="feet">Feet</option>
          <option value="inches">Inches</option>
        </select>

        <label htmlFor="toUnit">To:</label>
        <select id="toUnit" value={toUnit} onChange={(e) => {
            setToUnit(e.target.value);
            convert(); // Call convert on unit change
          }}>
          <option value="meters">Meters</option>
          <option value="feet">Feet</option>
          <option value="inches">Inches</option>
        </select>
      </div>

      <div className="result-group">
        <p>Result: {result}</p>
      </div>
    </div>
  );
}

Now, when you enter a value, select the units, and the result should update dynamically.

Adding More Units and Conversions

Our unit converter currently supports only a few units of length. Let’s extend it to include more units and conversion types. This is where the flexibility of this approach shines.

First, let’s add more units to the select dropdowns. Modify the options in the select elements in pages/index.js to include units like centimeters, kilometers, and miles:

<select id="fromUnit" value={fromUnit} onChange={(e) => {
    setFromUnit(e.target.value);
    convert();
  }}>
  <option value="meters">Meters</option>
  <option value="feet">Feet</option>
  <option value="inches">Inches</option>
  <option value="centimeters">Centimeters</option>
  <option value="kilometers">Kilometers</option>
  <option value="miles">Miles</option>
</select>

Do the same for the toUnit select.

Next, we need to add the corresponding conversion logic within the convert function. This is where you’ll add the else if statements for the new unit combinations. For example:


// Conversion factors (meters to...)
    if (fromUnit === 'meters' && toUnit === 'feet') {
      convertedValue = value * 3.28084;
    } else if (fromUnit === 'meters' && toUnit === 'inches') {
      convertedValue = value * 39.3701;
    } else if (fromUnit === 'feet' && toUnit === 'meters') {
      convertedValue = value / 3.28084;
    } else if (fromUnit === 'feet' && toUnit === 'inches') {
      convertedValue = value * 12;
    } else if (fromUnit === 'inches' && toUnit === 'meters') {
      convertedValue = value / 39.3701;
    } else if (fromUnit === 'inches' && toUnit === 'feet') {
      convertedValue = value / 12;
    } else if (fromUnit === 'meters' && toUnit === 'centimeters') {
      convertedValue = value * 100;
    } else if (fromUnit === 'centimeters' && toUnit === 'meters') {
      convertedValue = value / 100;
    } else if (fromUnit === 'kilometers' && toUnit === 'miles') {
      convertedValue = value * 0.621371;
    } else if (fromUnit === 'miles' && toUnit === 'kilometers') {
      convertedValue = value / 0.621371;
    } else if (fromUnit === toUnit) {
        convertedValue = value;
    }

Remember to research and use accurate conversion factors. You can also add more conversion types, such as:

  • Temperature: Celsius, Fahrenheit, Kelvin
  • Weight: kilograms, pounds, ounces
  • Volume: liters, gallons, quarts

By adding more units and conversion types, you can significantly enhance the functionality of your unit converter.

Handling Errors and Edge Cases

While our unit converter is functional, it’s important to consider error handling and edge cases to make it more robust.

Invalid Input: We’ve already implemented basic input validation to handle non-numeric input. However, you could enhance this by:

  • Preventing non-numeric input: You can use the type="number" attribute on the input field to allow only numbers. You can also add JavaScript to prevent users from entering invalid characters.
  • Displaying more informative error messages: Instead of just “Invalid input,” you could provide more specific feedback, such as “Please enter a valid number.”

Unit Selection Errors: Consider what should happen if a user selects a combination of units that doesn’t make sense (e.g., converting length to temperature). You might:

  • Disable irrelevant options: Dynamically disable units in the “to” dropdown that are not compatible with the selected “from” unit.
  • Display a warning message: If an invalid conversion is attempted, display an error message.

Large Numbers and Precision: For very large or very small numbers, you might want to consider using scientific notation to display the results. You can also adjust the number of decimal places using toFixed() based on the magnitude of the result.

Deployment

Once you’re satisfied with your unit converter, you’ll want to deploy it so others can use it. Next.js makes deployment relatively easy. Here’s a brief overview of common deployment options:

  • Vercel: Vercel is the platform created by the team behind Next.js, and it offers seamless deployment for Next.js applications. It’s often the easiest and fastest way to deploy. Simply push your code to a Git repository (like GitHub or GitLab), and Vercel will automatically build and deploy your application.
  • Netlify: Netlify is another popular platform for deploying web applications. It also provides a streamlined deployment process and integrates well with Git.
  • Other Platforms: You can deploy your Next.js application to other platforms like AWS, Google Cloud, or Azure. However, these often require more configuration.

For Vercel, the steps are typically:

  1. Create a Vercel account.
  2. Connect your Git repository.
  3. Vercel will automatically detect your Next.js project and build and deploy it.
  4. You’ll get a unique URL to access your deployed unit converter.

Common Mistakes and Troubleshooting

Here are some common mistakes and how to fix them:

  • Incorrect Conversion Factors: Double-check your conversion factors. A simple error in a conversion factor can lead to inaccurate results. Use reliable sources for conversion values.
  • Missing or Incorrect Event Handlers: Ensure that your onChange event handlers are correctly attached to the input field and select elements, and that they call the convert function.
  • State Not Updating: Make sure you’re correctly using the useState hook to manage the state of your component. Incorrect state updates can lead to unexpected behavior.
  • CSS Issues: If your UI isn’t displaying correctly, check your CSS styles. Inspect the elements in your browser’s developer tools to identify any styling problems.
  • CORS Errors: If you’re fetching data from an external API (which isn’t necessary for this project, but might be for future enhancements), you might encounter CORS (Cross-Origin Resource Sharing) errors. You’ll need to configure your server to allow requests from your domain, or use a proxy.

Key Takeaways and Summary

In this tutorial, we’ve built a functional web-based unit converter using Next.js. We covered the following key concepts:

  • Setting up a Next.js project: We used create-next-app to create a new project and understood the basic project structure.
  • Creating a user interface: We designed a simple UI with an input field, select dropdowns, and an output display.
  • Implementing conversion logic: We wrote a JavaScript function to perform the unit conversions.
  • Handling state with useState: We used the useState hook to manage the state of our component.
  • Adding more units and conversions: We learned how to extend the functionality of the unit converter.
  • Error handling and deployment: We discussed best practices for handling errors and deploying the application.

FAQ

Q: How can I add more conversion types (e.g., temperature, weight)?

A: You can add more conversion types by:

  1. Adding the new units as options in the select dropdowns.
  2. Adding the corresponding conversion logic within the convert function, using if/else if statements for the new unit combinations.

Q: How do I handle invalid input?

A: You can handle invalid input by:

  1. Using the type="number" attribute on the input field to allow only numbers.
  2. Checking if the input value is a valid number using isNaN() in the convert function.
  3. Displaying informative error messages to the user.

Q: How do I deploy my unit converter?

A: You can deploy your unit converter to platforms like Vercel or Netlify. The basic steps are:

  1. Create an account on the deployment platform.
  2. Connect your Git repository.
  3. The platform will automatically build and deploy your application.
  4. You’ll get a unique URL to access your deployed unit converter.

Q: Can I use this unit converter on my phone?

A: Yes! Because it is a web application, it is responsive and can be used on any device with a web browser, including smartphones and tablets.

Q: How can I improve the UI/UX of this unit converter?

A: You can improve the UI/UX by:

  • Adding more visual elements, such as icons.
  • Improving the layout and making it more responsive for different screen sizes.
  • Adding tooltips or help text to explain the different units.
  • Adding a history feature to store the conversions.

Building a unit converter with Next.js is a rewarding project that combines practical utility with valuable learning opportunities. You’ve not only created a tool that can be used every day, but also gained hands-on experience with React, state management, and the Next.js framework. Remember, the best way to learn is by doing, so don’t hesitate to experiment with the code, add new features, and refine your unit converter to make it even more useful and user-friendly. The skills you’ve acquired in this project will serve you well in your journey as a web developer, opening doors to more complex and exciting projects in the future. Embrace the process of learning and building, and watch your skills grow with each line of code you write.