Building a Tip Calculator App with Next.js

Day 9: Tip Calculator — 30 Days of 30 Projects Challenge

Asharib Ali
7 min readSep 10, 2024

Hey everyone, I hope you’re all doing well and enjoying your coding journey, Am I right? By the way, today marks the ninth day of the 30-day of 30-projects challenge. Our 9th project will be creating a mini Next.js application — a tip calculator. Please make sure to clap and comment on this blog post. It motivates me to create more amazing content like this, Let’s get started straight away.

Overview of the Mini Next.js Application

Our Color Picker application allows users to:

  • Input for bill amount and tip percentage
  • Calculate and display the tip amount
  • Display the total amount (bill + tip)

Tech-Stack Used:

  • Next.js: A React framework for building full-stack web applications.
  • React: A JavaScript library for building user interfaces.
  • Tailwind CSS: A utility-first CSS framework for styling.
  • Shadcn UI: Beautifully designed tailwindcss components that you can copy and paste into your application.
  • Vercel: For deploying the Nextjs web application.

Initialize a Nextjs project

Start by following this guide to set up the new project.

  • Go to the “components” folder.
  • Create a new file named “tip-calculator.tsx”.
  • This file will manage the entire functionality of the project.

We will go through the code step-by-step to make it easy to understand.

Component Breakdown

Import Statements

"use client";

import { useState, ChangeEvent } from "react";
import {
Card,
CardHeader,
CardTitle,
CardDescription,
CardContent,
CardFooter,
} from "@/components/ui/card";
import { Label } from "@/components/ui/label";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
  • Enables client-side rendering.
  • Imports necessary hooks and custom components.

State Hooks

const [billAmount, setBillAmount] = useState<number | null>(null);
const [tipPercentage, setTipPercentage] = useState<number | null>(null);
const [tipAmount, setTipAmount] = useState<number>(0);
const [totalAmount, setTotalAmount] = useState<number>(0);
  • billAmount: Stores the bill amount as a number or null if not yet set.
  • tipPercentage: Stores the tip percentage as a number or null if not yet set.
  • tipAmount: Stores the calculated tip amount as a number, initialized to 0.
  • totalAmount: Stores the calculated total amount (bill amount plus tip) as a number, initialized to 0.

Purpose: These state hooks manage the values needed to calculate and display the tip and total amounts for a bill.

Event Handlers

const handleBillAmountChange = (e: ChangeEvent<HTMLInputElement>): void => {
setBillAmount(parseFloat(e.target.value));
};

const handleTipPercentageChange = (e: ChangeEvent<HTMLInputElement>): void => {
setTipPercentage(parseFloat(e.target.value));
};
  • handleBillAmountChange Function: Updates the billAmount state with the parsed float value from the input field whenever it changes.
  • handleTipPercentageChange Function: Updates the tipPercentage state with the parsed float value from the input field whenever it changes.

Purpose: These functions ensure that the billAmount and tipPercentage state variables are kept up to date with user input.

Calculation Function

const calculateTip = (): void => {
if (billAmount !== null && tipPercentage !== null) {
const tip = billAmount * (tipPercentage / 100);
setTipAmount(tip);
setTotalAmount(billAmount + tip);
}
};
  • calculateTip Function: Calculates the tip and total amounts based on the billAmount and tipPercentage state values.
  • Tip Calculation: Computes the tip by multiplying the billAmount by the tipPercentage divided by 100.
  • State Update: Updates the tipAmount state with the calculated tip and the totalAmount state with the sum of the billAmount and the calculated tip.
  • Conditional Execution: Ensures that both billAmount and tipPercentage are not null before performing the calculations.

JSX Return Statement

return (
<div className="flex flex-col items-center justify-center min-h-screen bg-gray-100 dark:bg-gray-900">
<Card className="w-full max-w-md p-6 bg-white dark:bg-gray-800 shadow-lg rounded-lg">
<CardHeader>
<CardTitle>Tip Calculator</CardTitle>
<CardDescription>
Enter the bill amount and tip percentage to calculate the tip and
total.
</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="grid gap-2">
<Label htmlFor="bill-amount">Bill Amount</Label>
<Input
id="bill-amount"
type="number"
placeholder="Enter bill amount"
value={billAmount !== null ? billAmount : ""}
onChange={handleBillAmountChange}
/>
</div>
<div className="grid gap-2">
<Label htmlFor="tip-percentage">Tip Percentage</Label>
<Input
id="tip-percentage"
type="number"
placeholder="Enter tip percentage"
value={tipPercentage !== null ? tipPercentage : ""}
onChange={handleTipPercentageChange}
/>
</div>
<Button onClick={calculateTip}>Calculate</Button>
</CardContent>
<CardFooter className="grid gap-2">
<div className="flex items-center justify-between">
<span>Tip Amount:</span>
<span className="font-bold">${tipAmount.toFixed(2)}</span>
</div>
<div className="flex items-center justify-between">
<span>Total Amount:</span>
<span className="font-bold">${totalAmount.toFixed(2)}</span>
</div>
</CardFooter>
</Card>
</div>
);
  • Container Div: Centers the tip calculator UI on the screen using flexbox, with a background that adapts to light and dark themes, ensuring the layout takes up at least the full height of the viewport.
  • Card Component: Encapsulates the calculator content with padding, shadow, and rounded corners, providing a clean and elevated look, and adapts to light and dark themes.
  • Card Header: Displays the title “Tip Calculator” and a description explaining how to use the calculator.
  • Card Content: Contains input fields for the bill amount and tip percentage, each with a label and placeholder, and updates the respective state on input change.
  • Calculate Button: Triggers the calculateTip function to perform the tip and total amount calculations when clicked.
  • Card Footer: Displays the calculated tip amount and total amount in a bold font, ensuring clarity and visibility of the results.

(Bonus just for you): Full Code with Comments

"use client"; // Enables client-side rendering for this component

// Import necessary hooks from React
import { useState, ChangeEvent } from "react";

// Import custom UI components from the UI directory
import {
Card,
CardHeader,
CardTitle,
CardDescription,
CardContent,
CardFooter,
} from "@/components/ui/card";
import { Label } from "@/components/ui/label";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";

// Default export of the TipCalculatorComponent function
export default function TipCalculatorComponent() {
// State hooks for managing the bill amount, tip percentage, tip amount, and total amount
const [billAmount, setBillAmount] = useState<number | null>(null);
const [tipPercentage, setTipPercentage] = useState<number | null>(null);
const [tipAmount, setTipAmount] = useState<number>(0);
const [totalAmount, setTotalAmount] = useState<number>(0);

// Handler for updating bill amount state on input change
const handleBillAmountChange = (e: ChangeEvent<HTMLInputElement>): void => {
setBillAmount(parseFloat(e.target.value));
};

// Handler for updating tip percentage state on input change
const handleTipPercentageChange = (
e: ChangeEvent<HTMLInputElement>
): void => {
setTipPercentage(parseFloat(e.target.value));
};

// Function to calculate the tip and total amounts
const calculateTip = (): void => {
if (billAmount !== null && tipPercentage !== null) {
const tip = billAmount * (tipPercentage / 100); // Calculate the tip amount
setTipAmount(tip); // Set the tip amount state
setTotalAmount(billAmount + tip); // Set the total amount state
}
};

// JSX return statement rendering the tip calculator UI
return (
<div className="flex flex-col items-center justify-center min-h-screen bg-gray-100 dark:bg-gray-900">
{/* Center the tip calculator card within the screen */}
<Card className="w-full max-w-md p-6 bg-white dark:bg-gray-800 shadow-lg rounded-lg">
<CardHeader>
{/* Header with title and description */}
<CardTitle>Tip Calculator</CardTitle>
<CardDescription>
Enter the bill amount and tip percentage to calculate the tip and
total.
</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
{/* Input for bill amount */}
<div className="grid gap-2">
<Label htmlFor="bill-amount">Bill Amount</Label>
<Input
id="bill-amount"
type="number"
placeholder="Enter bill amount"
value={billAmount !== null ? billAmount : ""}
onChange={handleBillAmountChange}
/>
</div>
{/* Input for tip percentage */}
<div className="grid gap-2">
<Label htmlFor="tip-percentage">Tip Percentage</Label>
<Input
id="tip-percentage"
type="number"
placeholder="Enter tip percentage"
value={tipPercentage !== null ? tipPercentage : ""}
onChange={handleTipPercentageChange}
/>
</div>
{/* Button to calculate tip */}
<Button onClick={calculateTip}>Calculate</Button>
</CardContent>
<CardFooter className="grid gap-2">
{/* Display the calculated tip amount */}
<div className="flex items-center justify-between">
<span>Tip Amount:</span>
<span className="font-bold">${tipAmount.toFixed(2)}</span>
</div>
{/* Display the calculated total amount */}
<div className="flex items-center justify-between">
<span>Total Amount:</span>
<span className="font-bold">${totalAmount.toFixed(2)}</span>
</div>
</CardFooter>
</Card>
</div>
);
}

Okay, you’ve completed the main component with functional UI. Now, you need to import this component into the app directory to use it in the app/page.tsx file. Your final code should look like this:

import TipCalculator from "@/components/tip-calculator";

export default function Home() {
return (
<div>
<TipCalculator />
</div>
);
}

Running the Project

To see the weather widget in action, follow these steps:

  1. Start the Development Server: Run npm run dev to start the development server.
  2. Open in Browser: Open http://localhost:3000 in your browser to view the application.

Make sure to test it properly (each and everything) so that we don’t have any errors in the production mode aka when we host on the internet.

Now, we want people to see our application on the internet. All you have to do is create a repository on GitHub and then push your code to it. After that, deploy the Tip Calculator application using Vercel.

Once you’re done with the deployment, please share the application link with me by commenting on this blog post, on Linkedin, and (most importantly, on X a.k.a. Twitter. Tag me there, and I’ll reply and appreciate your efforts) 👀

(Optional): One thing you can do on your own is to add new functionalities, enhance the styling, and improve the overall application. This way, you’ll learn something new by making modifications.

✨ Star Github Repository of the project 👈

Conclusion

In this blog post, we explored the creation of a tip calculator using Next.js. We covered:

  • The purpose and main features of the application.
  • A detailed breakdown of the tip-calculator.tsx component, explaining how each part of the code works together.

See you tomorrow with the latest project. Happy coding!

Stay updated with the latest in cutting-edge technology! Follow me:

Thanks for reading!

--

--

Asharib Ali
Asharib Ali

Written by Asharib Ali

✨ I build & teach about AI and Blockchain stuffs⚡

Responses (1)