Build a Simple Next.js Interactive Web-Based Tip Calculator

Written by

in

In today’s digital world, web applications are becoming increasingly essential for everyday tasks. One such task is calculating tips, a common practice in restaurants, bars, and various service industries. While it might seem simple, building a web-based tip calculator provides an excellent opportunity to learn and practice fundamental web development concepts. This tutorial will guide you through building a fully functional, interactive tip calculator using Next.js, a popular React framework for building web applications.

Why Build a Tip Calculator?

Creating a tip calculator is a fantastic way to grasp the core principles of web development. It allows you to:

  • Understand User Input: Learn how to capture and process data entered by users.
  • Practice State Management: Get hands-on experience with managing and updating the application’s state based on user interactions.
  • Explore Basic Calculations: Implement mathematical operations in a web application.
  • Work with UI Components: Build and style user interface elements to create an intuitive user experience.
  • Grasp Next.js Fundamentals: Familiarize yourself with Next.js features such as routing, component structure, and server-side rendering (if applicable).

Furthermore, this project is small enough to be completed relatively quickly, allowing you to see results and build confidence in your development skills. It’s a stepping stone to more complex projects.

Prerequisites

Before we begin, ensure you have the following:

  • Node.js and npm (or yarn): You’ll need Node.js and npm (Node Package Manager) or yarn installed on your system. These are essential for managing project dependencies.
  • Basic Knowledge of HTML, CSS, and JavaScript: Familiarity with these languages is necessary to understand the code and concepts presented in this tutorial.
  • A Code Editor: Choose a code editor like Visual Studio Code, Sublime Text, or Atom.

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

This command will create a new Next.js project named “tip-calculator”. Navigate into the project directory:

cd tip-calculator

Now, start the development server:

npm run dev

Your Next.js application should now be running on http://localhost:3000. Open this address in your web browser, and you should see the default Next.js welcome page.

Project Structure

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

  • pages/index.js: This file will contain the code for our main application page, where the tip calculator will reside.
  • styles/globals.css: This file is where we can add global CSS styles for our application.
  • package.json: This file lists the project dependencies and scripts.

Building the User Interface (UI)

Let’s start by designing the UI for our tip calculator. We’ll need input fields for the bill amount, the desired tip percentage, and the number of people splitting the bill. We’ll also need a display to show the calculated tip amount and the total amount per person.

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

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 [totalPerPerson, setTotalPerPerson] = useState(0);

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

    if (isNaN(bill) || bill  setBillAmount(e.target.value)}
          placeholder="Enter bill amount"
        />
      </div>

      <div className="input-group">
        <label htmlFor="tipPercentage">Tip Percentage:</label>
        <input
          type="number"
          id="tipPercentage"
          value={tipPercentage}
          onChange={(e) => setTipPercentage(e.target.value)}
          placeholder="Enter tip percentage"
        />
      </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)}
          placeholder="Enter number of people"
        />
      </div>

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

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

In this code:

  • We import useState from React to manage the state of our input values and calculated results.
  • We define state variables for billAmount, tipPercentage, numberOfPeople, tipAmount, and totalPerPerson.
  • We create input fields for the user to enter the bill amount, tip percentage, and number of people.
  • We include a button that, when clicked, will trigger the calculateTip function.
  • We display the calculated tip amount and the total amount per person.

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

body {
  font-family: sans-serif;
  background-color: #f4f4f4;
  display: flex;
  justify-content: center;
  align-items: center;
  min-height: 100vh;
  margin: 0;
}

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

h1 {
  text-align: center;
  margin-bottom: 20px;
  color: #333;
}

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

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

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

button {
  background-color: #4caf50;
  color: white;
  padding: 12px 20px;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  font-size: 16px;
  width: 100%;
}

button:hover {
  background-color: #45a049;
}

.results {
  margin-top: 20px;
  padding: 10px;
  border-radius: 4px;
  background-color: #f9f9f9;
  border: 1px solid #eee;
}

With this CSS, we’ve styled the body, the container, the headings, the input groups, the labels, the input fields, the button, and the results section.

Implementing the Calculation Logic

The core of our application is the calculateTip function. This function will take the input values, perform the calculations, and update the state variables to display the results.

Let’s examine the calculateTip function in more detail:

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

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

  const tipValue = bill * tip;
  const total = bill + tipValue;
  const perPerson = total / people;

  setTipAmount(tipValue);
  setTotalPerPerson(perPerson);
};

Here’s a breakdown of the function:

  • Parsing Input: We use parseFloat() and parseInt() to convert the input values (which are strings from the input fields) into numbers.
  • Input Validation: We check if the bill amount is a valid number and greater than zero. If not, we reset the tip amount and total per person to zero and return. This prevents errors if the user enters invalid data.
  • Tip Calculation: We calculate the tip amount by multiplying the bill amount by the tip percentage (divided by 100 to convert it to a decimal).
  • Total Calculation: We calculate the total amount by adding the bill amount and the tip amount.
  • Per Person Calculation: We calculate the amount per person by dividing the total amount by the number of people.
  • Updating State: Finally, we update the tipAmount and totalPerPerson state variables with the calculated values. The toFixed(2) method is used to format the output to two decimal places.

Handling User Input and State Updates

The onChange event handler is crucial for capturing user input and updating the state variables. Each input field has an onChange prop that calls a function to update the corresponding state variable.

For example, in the bill amount input field:

<input
  type="number"
  id="billAmount"
  value={billAmount}
  onChange={(e) => setBillAmount(e.target.value)}
  placeholder="Enter bill amount"
/>

When the user types something in the input field, the onChange event is triggered. The e.target.value contains the new value entered by the user, and the setBillAmount(e.target.value) updates the billAmount state variable. This, in turn, causes the component to re-render, reflecting the updated value in the input field.

The same logic applies to the tip percentage and number of people input fields.

Adding Error Handling

While we’ve included basic input validation, we can enhance it to provide a better user experience. For example, we can display an error message if the user enters an invalid bill amount or an invalid number of people.

Let’s add an error message for invalid bill amounts. Modify the calculateTip function as follows:

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

  if (isNaN(bill) || bill <= 0) {
    errorMessage = 'Please enter a valid bill amount.';
    setTipAmount(0);
    setTotalPerPerson(0);
  } else if (isNaN(people) || people <= 0) {
      errorMessage = 'Please enter a valid number of people.';
      setTipAmount(0);
      setTotalPerPerson(0);
  }
  else {
    const tipValue = bill * tip;
    const total = bill + tipValue;
    const perPerson = total / people;

    setTipAmount(tipValue);
    setTotalPerPerson(perPerson);
  }
  setErrorMessage(errorMessage);
};

Then, add the following line in the return statement, below the button:

<p className="error-message">{errorMessage}</p>

And add the following CSS to styles/globals.css:

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

This will display an error message if the bill amount is invalid. You can extend this to include error handling for other scenarios, such as invalid tip percentages.

Deploying Your Tip Calculator

Once you’ve built and tested your tip calculator locally, you’ll likely want to deploy it so others can use it. Next.js makes deployment relatively easy. Here are a few options:

  • Vercel: Vercel is the platform created by the same team that built Next.js. It’s designed for seamless Next.js deployments. 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 offers easy Git integration and automatic deployments.
  • Other Platforms: You can also deploy your Next.js application to other platforms like AWS, Google Cloud, or Azure. However, these might require more configuration.

For Vercel, the process is straightforward:

  1. Create a Vercel Account: If you don’t already have one, sign up for a free Vercel account.
  2. Connect Your Git Repository: In your Vercel dashboard, connect your Git repository to Vercel.
  3. Deploy: Vercel will automatically detect your Next.js project and deploy it.

After deployment, Vercel will provide you with a URL where your tip calculator will be accessible.

Common Mistakes and Troubleshooting

Here are some common mistakes and how to fix them:

  • Incorrect Input Types: Make sure you are using the correct input types. Use type="number" for numerical inputs.
  • State Updates Not Triggering Re-renders: Ensure your state variables are updated correctly using the useState hook. Incorrect updates won’t trigger the component to re-render.
  • Calculation Errors: Double-check your calculation logic. Use console.log to debug and verify that the values are being calculated correctly.
  • CSS Issues: If your styling isn’t working as expected, inspect your CSS rules and ensure they are being applied correctly. Use your browser’s developer tools to identify any conflicts or errors.
  • Deployment Issues: If you encounter deployment issues, check the platform’s documentation and logs for error messages. Ensure your project’s dependencies are correctly installed.

Key Takeaways

In this tutorial, we’ve covered the following:

  • Setting up a Next.js project: We used create-next-app to quickly scaffold our project.
  • Building a UI: We created input fields and displayed results.
  • Managing State: We used the useState hook to manage user inputs and calculated values.
  • Implementing Calculations: We wrote the logic to calculate the tip amount and total per person.
  • Handling User Input: We used the onChange event to update the state as the user types.
  • Adding Error Handling: We implemented basic input validation to provide a better user experience.
  • Deploying the App: We discussed deployment options.

FAQ

Here are some frequently asked questions:

  1. Can I customize the tip percentages? Yes, you can add buttons or a dropdown to allow users to select from pre-defined tip percentages.
  2. Can I add a dark mode? Absolutely! You can use CSS variables and a state variable to control the theme.
  3. How can I handle different currencies? You can add a currency selection feature and use a library like Intl.NumberFormat to format the output accordingly.
  4. How can I make the app responsive? Use CSS media queries to adjust the layout and styling for different screen sizes.

This tutorial provides a solid foundation for building a web-based tip calculator with Next.js. You can extend this project by adding features like custom tip percentages, a dark mode, currency support, and more. This project is a great starting point for learning React and Next.js, and it can be a valuable addition to your portfolio.