Building a Tip Calculator App with Next.js
Day 9: Tip Calculator — 30 Days of 30 Projects Challenge
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 ornull
if not yet set.tipPercentage
: Stores the tip percentage as a number ornull
if not yet set.tipAmount
: Stores the calculated tip amount as a number, initialized to0
.totalAmount
: Stores the calculated total amount (bill amount plus tip) as a number, initialized to0
.
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 thebillAmount
state with the parsed float value from the input field whenever it changes.handleTipPercentageChange
Function: Updates thetipPercentage
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 thebillAmount
andtipPercentage
state values.- Tip Calculation: Computes the tip by multiplying the
billAmount
by thetipPercentage
divided by 100. - State Update: Updates the
tipAmount
state with the calculated tip and thetotalAmount
state with the sum of thebillAmount
and the calculated tip. - Conditional Execution: Ensures that both
billAmount
andtipPercentage
are notnull
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:
- Start the Development Server: Run
npm run dev
to start the development server. - 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:
- Twitter: @0xAsharib
- LinkedIn: Asharib Ali
- GitHub: AsharibAli
- Website: asharib.xyz
Thanks for reading!