Build a Next.js Interactive Web-Based Tip Calculator

Written by

in

In today’s digital age, we’re constantly interacting with web applications. From ordering food online to managing our finances, these tools have become indispensable. One common task we often perform is calculating tips, whether at a restaurant or when using a service. While it’s easy enough to do in your head or with a calculator app, wouldn’t it be convenient to have a simple, interactive tip calculator right in your web browser? This tutorial will guide you through building precisely that using Next.js, a powerful React framework for building modern web applications. We’ll start from scratch, explaining each step in detail, so even if you’re new to Next.js, you’ll be able to follow along and create your own functional tip calculator.

Why Build a Tip Calculator?

Creating a tip calculator is an excellent project for several reasons:

  • It’s Beginner-Friendly: The core logic is straightforward, making it an ideal project for those learning the ropes of web development and Next.js.
  • Practical Application: It solves a real-world problem, making it a useful tool for everyday use.
  • Learning Opportunity: You’ll learn essential concepts like state management, event handling, and basic styling.
  • Foundation for More Complex Projects: It provides a solid foundation for understanding how to build more complex interactive web applications.

Prerequisites

Before we begin, ensure you have the following installed:

  • Node.js and npm (or yarn): These are essential for running and managing your Next.js project. You can download them from the official Node.js website.
  • A Code Editor: Visual Studio Code (VS Code) is highly recommended, but you can use any editor you prefer.
  • Basic Understanding of HTML, CSS, and JavaScript: Familiarity with these languages will be helpful, but don’t worry if you’re a beginner; we’ll cover the basics as we go.

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 tip-calculator-app

This command will create a new directory named “tip-calculator-app” with all the necessary files for a Next.js project. Navigate into the project directory:

cd tip-calculator-app

Now, start the development server:

npm run dev

This will start the development server, and you can access your application in your browser at http://localhost:3000. You should see the default Next.js welcome page.

Building the UI (User Interface)

Now, let’s build the user interface for our tip calculator. We’ll modify the `pages/index.js` file, which serves as the homepage of our application. Open `pages/index.js` in your code editor and replace the existing content with the following HTML structure:

import { useState } from 'react';

export default function Home() {
  const [billAmount, setBillAmount] = useState('');
  const [tipPercentage, setTipPercentage] = useState(15);
  const [numberOfPeople, setNumberOfPeople] = useState(1);
  const [tipAmount, setTipAmount] = useState(0);
  const [totalAmount, setTotalAmount] = useState(0);

  const calculateTip = () => {
    const bill = parseFloat(billAmount);
    const tip = tipPercentage / 100;
    const people = parseInt(numberOfPeople);

    if (isNaN(bill) || bill  setBillAmount(e.target.value)}
        />
      </div>

      <div className="input-group">
        <label htmlFor="tipPercentage">Tip Percentage:</label>
        <select
          id="tipPercentage"
          value={tipPercentage}
          onChange={(e) => setTipPercentage(parseFloat(e.target.value))}
        >
          <option value="5">5%</option>
          <option value="10">10%</option>
          <option value="15">15%</option>
          <option value="20">20%</option>
          <option value="25">25%</option>
        </select>
      </div>

      <div className="input-group">
        <label htmlFor="numberOfPeople">Number of People:</label>
        <input
          type="number"
          id="numberOfPeople"
          value={numberOfPeople}
          onChange={(e) => setNumberOfPeople(e.target.value)}
        />
      </div>

      <button onClick={calculateTip}>Calculate Tip</button>

      <div className="results">
        <p>Tip Amount per Person: ${tipAmount.toFixed(2)}</p>
        <p>Total Amount per Person: ${totalAmount.toFixed(2)}</p>
      </div>
    </div>
  );
}

Let’s break down the code:

  • Importing `useState`: We import the `useState` hook from React to manage the state of our input fields and calculated values.
  • State Variables: We declare state variables for `billAmount`, `tipPercentage`, `numberOfPeople`, `tipAmount`, and `totalAmount`. These variables will hold the values entered by the user and the calculated results.
  • Input Fields: We create input fields for the bill amount and the number of people and a select dropdown for the tip percentage. Each input field has an `onChange` event handler that updates the corresponding state variable whenever the user types in the input.
  • Calculate Tip Button: We include a button that, when clicked, will trigger the `calculateTip` function.
  • Results Display: We display the tip amount and total amount per person.

Styling the UI with CSS

Now, let’s add some basic styling to make our tip calculator look presentable. Create a new file named `styles/Home.module.css` in your project directory. Add the following CSS rules:


.container {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 20px;
  font-family: sans-serif;
}

h1 {
  margin-bottom: 20px;
}

.input-group {
  margin-bottom: 15px;
  display: flex;
  flex-direction: column;
  width: 300px;
}

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

input[type="number"], select {
  padding: 10px;
  border: 1px solid #ccc;
  border-radius: 4px;
  font-size: 16px;
}

button {
  padding: 10px 20px;
  background-color: #4CAF50;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  font-size: 16px;
  margin-top: 20px;
}

button:hover {
  background-color: #3e8e41;
}

.results {
  margin-top: 20px;
  border: 1px solid #ccc;
  padding: 10px;
  border-radius: 4px;
}

Now, import this CSS file into your `pages/index.js` file. Add the following line at the top of your `pages/index.js` file:

import styles from '../styles/Home.module.css';

And apply the class names to the HTML elements:


<div className={styles.container}>
  <h1>Tip Calculator</h1>

  <div className={styles.input-group}>
    <label htmlFor="billAmount">Bill Amount:</label>
    <input
      type="number"
      id="billAmount"
      value={billAmount}
      onChange={(e) => setBillAmount(e.target.value)}
    />
  </div>

  <div className={styles.input-group}>
    <label htmlFor="tipPercentage">Tip Percentage:</label>
    <select
      id="tipPercentage"
      value={tipPercentage}
      onChange={(e) => setTipPercentage(parseFloat(e.target.value))}
    >
      <option value="5">5%</option>
      <option value="10">10%</option>
      <option value="15">15%</option>
      <option value="20">20%</option>
      <option value="25">25%</option>
    </select>
  </div>

  <div className={styles.input-group}>
    <label htmlFor="numberOfPeople">Number of People:</label>
    <input
      type="number"
      id="numberOfPeople"
      value={numberOfPeople}
      onChange={(e) => setNumberOfPeople(e.target.value)}
    />
  </div>

  <button onClick={calculateTip}>Calculate Tip</button>

  <div className={styles.results}>
    <p>Tip Amount per Person: ${tipAmount.toFixed(2)}</p>
    <p>Total Amount per Person: ${totalAmount.toFixed(2)}</p>
  </div>
</div>

Save the files, and your tip calculator should now have a basic, but functional, user interface with some styling.

Implementing the Tip Calculation Logic

Now, let’s implement the core logic for calculating the tip. We’ve already created the `calculateTip` function, but it’s currently empty. Let’s add the calculation logic inside this function. Modify the `calculateTip` function in `pages/index.js` as follows:


  const calculateTip = () => {
    const bill = parseFloat(billAmount);
    const tip = tipPercentage / 100;
    const people = parseInt(numberOfPeople);

    if (isNaN(bill) || bill <= 0) {
      setTipAmount(0);
      setTotalAmount(0);
      return;
    }

    const tipValue = bill * tip;
    const total = bill + tipValue;
    const tipPerPerson = tipValue / people;
    const totalPerPerson = total / people;

    setTipAmount(tipPerPerson);
    setTotalAmount(totalPerPerson);
  };

Let’s break down the code:

  • Get Input Values: We retrieve the values from the input fields using the state variables (`billAmount`, `tipPercentage`, and `numberOfPeople`).
  • Convert to Numbers: We convert the input values to numbers using `parseFloat()` and `parseInt()`. This is crucial because the input values are initially strings.
  • Handle Invalid Input: We check if the bill amount is a valid number and greater than zero. If not, we reset the tip amount and total amount to zero and return to avoid errors.
  • Calculate Tip and Total: We calculate the tip amount, the total amount, the tip per person, and the total per person using simple mathematical formulas.
  • Update State: We update the `tipAmount` and `totalAmount` state variables with the calculated results.

Now, when the user enters the bill amount, selects a tip percentage, enters the number of people, and clicks the “Calculate Tip” button, the tip amount and total amount per person will be displayed below.

Adding Error Handling and Input Validation

While our tip calculator is functional, it’s essential to consider error handling and input validation to make it more robust. Here’s how we can improve our calculator:

  • Preventing Negative Values: Modify the input fields for bill amount and number of people to only allow positive values.
  • Displaying Error Messages: Display an error message if the bill amount is invalid (e.g., empty or negative).

Let’s modify the `pages/index.js` to include input validation. Update the `calculateTip` function to include an error message if the bill amount is invalid. Also, add the `min` attribute to the input fields to prevent negative values.


  const [errorMessage, setErrorMessage] = useState('');

  const calculateTip = () => {
    const bill = parseFloat(billAmount);
    const tip = tipPercentage / 100;
    const people = parseInt(numberOfPeople);

    if (isNaN(bill) || bill <= 0) {
      setTipAmount(0);
      setTotalAmount(0);
      setErrorMessage('Please enter a valid bill amount.');
      return;
    }

    if (people <= 0 || isNaN(people)) {
      setTipAmount(0);
      setTotalAmount(0);
      setErrorMessage('Please enter a valid number of people.');
      return;
    }

    setErrorMessage(''); // Clear the error message if the input is valid.

    const tipValue = bill * tip;
    const total = bill + tipValue;
    const tipPerPerson = tipValue / people;
    const totalPerPerson = total / people;

    setTipAmount(tipPerPerson);
    setTotalAmount(totalPerPerson);
  };

Now, modify the HTML for the input fields to include the `min` attribute:


 <div className={styles.input-group}>
    <label htmlFor="billAmount">Bill Amount:</label>
    <input
      type="number"
      id="billAmount"
      value={billAmount}
      onChange={(e) => setBillAmount(e.target.value)}
      min="0"
    />
  </div>

  <div className={styles.input-group}>
    <label htmlFor="numberOfPeople">Number of People:</label>
    <input
      type="number"
      id="numberOfPeople"
      value={numberOfPeople}
      onChange={(e) => setNumberOfPeople(e.target.value)}
      min="1"
    />
  </div>

Finally, display the error message in the UI:


<div className={styles.container}>
  <h1>Tip Calculator</h1>

  <div className={styles.input-group}>
    <label htmlFor="billAmount">Bill Amount:</label>
    <input
      type="number"
      id="billAmount"
      value={billAmount}
      onChange={(e) => setBillAmount(e.target.value)}
      min="0"
    />
  </div>

  <div className={styles.input-group}>
    <label htmlFor="tipPercentage">Tip Percentage:</label>
    <select
      id="tipPercentage"
      value={tipPercentage}
      onChange={(e) => setTipPercentage(parseFloat(e.target.value))}
    >
      <option value="5">5%</option>
      <option value="10">10%</option>
      <option value="15">15%</option>
      <option value="20">20%</option>
      <option value="25">25%</option>
    </select>
  </div>

  <div className={styles.input-group}>
    <label htmlFor="numberOfPeople">Number of People:</label>
    <input
      type="number"
      id="numberOfPeople"
      value={numberOfPeople}
      onChange={(e) => setNumberOfPeople(e.target.value)}
      min="1"
    />
  </div>

  {errorMessage && <p className={styles.error}>{errorMessage}</p>}

  <button onClick={calculateTip}>Calculate Tip</button>

  <div className={styles.results}>
    <p>Tip Amount per Person: ${tipAmount.toFixed(2)}</p>
    <p>Total Amount per Person: ${totalAmount.toFixed(2)}</p>
  </div>
</div>

Add the following CSS rule to `styles/Home.module.css` to style the error message:


.error {
  color: red;
  margin-top: 10px;
}

Now, your tip calculator is more robust and user-friendly. It handles invalid input gracefully and provides helpful error messages.

Deploying Your Tip Calculator

Once you’ve built your tip calculator and are satisfied with its functionality, you’ll likely want to deploy it so others can use it. Next.js makes deployment straightforward. Here are a few options:

  • Vercel: Since Next.js is developed by Vercel, it’s the easiest and most recommended way to deploy your application. You can deploy directly from your Git repository (e.g., GitHub, GitLab, Bitbucket). Vercel automatically handles the build process and deployment.
  • Netlify: Netlify is another popular platform for deploying web applications. It also offers automatic builds and deployments from Git repositories.
  • Other Platforms: You can deploy to other platforms like AWS, Google Cloud, or Azure, but these options usually require more configuration.

To deploy to Vercel:

  1. Create a Vercel Account: If you don’t already have one, sign up for a Vercel account at https://vercel.com/.
  2. Connect Your Git Repository: Link your Git repository (where your code is stored) to your Vercel account.
  3. Deploy: Vercel will automatically detect your Next.js project and deploy it. You’ll get a unique URL where your tip calculator will be live.

Common Mistakes and How to Fix Them

As you build your tip calculator, you might encounter some common mistakes. Here’s a list of potential issues and how to resolve them:

  • Incorrect Input Types: One common mistake is forgetting to convert input values from strings to numbers. Always use `parseFloat()` or `parseInt()` to convert the input values before performing calculations.
  • Incorrect State Updates: When updating state variables, make sure you’re using the correct setter functions (e.g., `setBillAmount`, `setTipPercentage`).
  • CSS Issues: Ensure you’ve imported the CSS file correctly and that your CSS class names match the HTML elements. Double-check your CSS rules for any typos.
  • Deployment Errors: If you encounter deployment issues, check the Vercel (or your chosen platform) logs for error messages. These logs often provide valuable clues about what went wrong. Common deployment issues include missing environment variables or incorrect build commands.
  • Missing Dependencies: Ensure you have all the necessary dependencies installed by checking your `package.json` file. If you’re missing a dependency, install it using `npm install [package-name]` or `yarn add [package-name]`.

Key Takeaways

In this tutorial, you’ve learned how to build a functional and interactive tip calculator using Next.js. You’ve covered the following key concepts:

  • Setting up a Next.js project: You learned how to create a new Next.js project using `create-next-app`.
  • Building a user interface: You created the UI using HTML and React components.
  • Managing state: You used the `useState` hook to manage the state of your input fields and calculated values.
  • Implementing the calculation logic: You implemented the core logic for calculating the tip amount and total amount.
  • Adding styling: You added CSS to style your application and make it visually appealing.
  • Error handling and input validation: You improved the robustness of your application by adding error handling and input validation.
  • Deploying your application: You learned how to deploy your Next.js application to a platform like Vercel.

FAQ

Here are some frequently asked questions about building a tip calculator with Next.js:

  1. Can I customize the tip percentages? Yes, you can easily customize the tip percentages by modifying the options in the select dropdown in the HTML.
  2. How can I add more features? You can add features like a dark mode toggle, the ability to split the bill, or save the tip amount to local storage.
  3. Is Next.js the best framework for this project? Yes, Next.js is an excellent choice for this project due to its ease of use, server-side rendering capabilities, and excellent developer experience.
  4. Where can I learn more about Next.js? You can find comprehensive documentation and tutorials on the official Next.js website: https://nextjs.org/.

Building this tip calculator provides a solid foundation for your journey into web development with Next.js. You’ve learned how to structure a basic web application, manage user input, and perform calculations. By understanding these fundamentals, you’re well-equipped to tackle more complex projects and continue expanding your skills in this exciting field. Remember to experiment, explore new features, and most importantly, have fun while coding. The best way to learn is by doing, and with each project you undertake, you’ll gain valuable experience and confidence. Keep practicing, and you’ll be amazed at what you can build!