Building a Random User Generator App with Next.js
Day 29: Random User Generator — 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 29th day of the 30-day of 30-projects challenge. Our 29th project will be creating a mini Next.js application — a random user generator. 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 Random User Generator application allows users to:
- Fetch random user profiles from an API
- Display user details (name, email, address, UUID, picture)
- Button to fetch a new random user & appreciate
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.
- RandomUser API: The API for generating random user data.
- 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 “
random-user.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, useEffect } from "react";
import {
Card,
CardHeader,
CardContent,
CardTitle,
CardDescription,
} from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { Spinner } from "@/components/ui/spinner";
import Image from "next/image";
import { CSSTransition } from "react-transition-group";
import { MailIcon, MapPinIcon, UserIcon, InfoIcon } from "lucide-react";These import statements include React hooks, various custom components from Shadcn UI, and icons from Lucide React for building the user interface.
Define Types and Component State
interface User {
name: string;
email: string;
address: string;
image: string;
description: string;
}
const RandomUserComponent: React.FC = () => {
const [user, setUser] = useState<User | null>(null);
const [loading, setLoading] = useState<boolean>(false);
const [error, setError] = useState<string | null>(null);
const [appreciationVisible, setAppreciationVisible] = useState<boolean>(false);This component manages state for the fetched user, loading state, error messages, and visibility of the appreciation message using React’s useState hook.
Fetch Random User
const fetchRandomUser = async () => {
setLoading(true);
setError(null);
try {
const response = await fetch("https://randomuser.me/api/");
const data = await response.json();
const fetchedUser = data.results[0];
const newUser: User = {
name: `${fetchedUser.name.first} ${fetchedUser.name.last}`,
email: fetchedUser.email,
address: `${fetchedUser.location.street.number} ${fetchedUser.location.street.name}, ${fetchedUser.location.city}, ${fetchedUser.location.country}`,
image: fetchedUser.picture.large,
description: fetchedUser.login.uuid,
};
setUser(newUser);
} catch (err) {
setError("Failed to fetch user. Please try again.");
} finally {
setLoading(false);
}
};
useEffect(() => {
fetchRandomUser();
}, []);The fetchRandomUser function fetches a random user from the RandomUser API and updates the state with the fetched user data or an error message. This function is called initially when the component mounts using the useEffect hook.
Handle Appreciation Button Click
const handleAppreciate = () => {
setAppreciationVisible(true);
setTimeout(() => setAppreciationVisible(false), 2000);
};This function handles the appreciation button click by showing the appreciation message for 2 seconds.
Render the Random User UI
return (
<div className="flex flex-col items-center justify-center min-h-screen bg-background text-foreground">
<h1 className="text-3xl font-bold mb-4">Random User Generator</h1>
<p className="text-muted-foreground mb-6">
Click the button below to fetch a random user profile.
</p>
<Button onClick={fetchRandomUser} className="mb-6">
Fetch New User
</Button>
{loading && (
<div className="flex items-center justify-center">
<Spinner className="w-6 h-6 mr-2" />
<span>Loading...</span>
</div>
)}
{error && <div className="text-red-500">{error}</div>}
{user && (
<Card className="border-0 shadow-lg rounded-lg overflow-hidden max-w-sm relative">
<CardHeader className="h-32 bg-[#55efc4] relative">
<Image
src={user.image}
alt={user.name}
width={80}
height={80}
className="rounded-full border-4 border-white absolute bottom-0 left-1/2 transform -translate-x-1/2 translate-y-1/2"
/>
</CardHeader>
<CardContent className="p-6 pt-12 text-center">
<CardTitle className="text-xl font-bold flex items-center justify-center">
<UserIcon className="mr-2" /> {user.name}
</CardTitle>
<CardDescription className="text-muted-foreground flex items-center justify-center">
<MailIcon className="mr-2" /> {user.email}
</CardDescription>
<div className="text-sm text-muted-foreground mt-2 flex items-center justify-center">
<MapPinIcon className="mr-2" /> {user.address}
</div>
<div className="text-sm text-muted-foreground mt-2 flex items-center justify-center">
<InfoIcon className="mr-2" /> {user.description}
</div>
<Button
variant="outline"
className="mt-4"
onClick={handleAppreciate}
>
Appreciate
</Button>
</CardContent>
<CSSTransition
in={appreciationVisible}
timeout={300}
classNames="appreciation"
unmountOnExit
>
<div className="absolute top-0 left-0 w-full h-full flex items-center justify-center bg-white bg-opacity-75">
<h2 className="text-2xl font-bold text-black">❤️ Thank you ✨</h2>
</div>
</CSSTransition>
</Card>
)}
</div>
);
};
export default RandomUserComponent;This snippet renders the Random User UI, including a button to fetch a new user, loading and error messages, and user details with an appreciation button.
(Bonus just for you): Full Code with Comments
"use client"; // Enables client-side rendering for this component
import { useState, useEffect } from "react"; // Import useState and useEffect hooks from React
import {
Card,
CardHeader,
CardContent,
CardTitle,
CardDescription,
} from "@/components/ui/card"; // Import custom Card components
import { Button } from "@/components/ui/button"; // Import custom Button component
import ClipLoader from "react-spinners/ClipLoader";
import Image from "next/image"; // Import Next.js Image component
import { CSSTransition } from "react-transition-group"; // Import CSSTransition for animations
import { MailIcon, MapPinIcon, UserIcon, InfoIcon } from "lucide-react"; // Import icons from lucide-react
// Define the User interface
interface User {
name: string;
email: string;
address: string;
image: string;
description: string;
}
const RandomUser: React.FC = () => {
const [user, setUser] = useState<User | null>(null); // State to manage the fetched user
const [loading, setLoading] = useState<boolean>(false); // State to manage the loading state
const [error, setError] = useState<string | null>(null); // State to manage error messages
const [appreciationVisible, setAppreciationVisible] =
useState<boolean>(false); // State to manage appreciation message visibility
// Function to fetch a random user
const fetchRandomUser = async () => {
setLoading(true);
setError(null);
try {
const response = await fetch("https://randomuser.me/api/");
const data = await response.json();
const fetchedUser = data.results[0];
const newUser: User = {
name: `${fetchedUser.name.first} ${fetchedUser.name.last}`,
email: fetchedUser.email,
address: `${fetchedUser.location.street.number} ${fetchedUser.location.street.name}, ${fetchedUser.location.city}, ${fetchedUser.location.country}`,
image: fetchedUser.picture.large,
description: fetchedUser.login.uuid, // Using UUID as a placeholder for description
};
setUser(newUser);
} catch (err) {
setError("Failed to fetch user. Please try again.");
} finally {
setLoading(false);
}
};
// Fetch a random user when the component mounts
useEffect(() => {
fetchRandomUser();
}, []);
// Function to handle appreciation button click
const handleAppreciate = () => {
setAppreciationVisible(true);
setTimeout(() => setAppreciationVisible(false), 2000); // Hide after 2 seconds
};
// JSX return statement rendering the Random User UI
return (
<div className="flex flex-col items-center justify-center min-h-screen bg-background text-foreground">
<h1 className="text-3xl font-bold mb-4">Random User Generator</h1>
<p className="text-muted-foreground mb-6">
Click the button below to fetch a random user profile.
</p>
<Button onClick={fetchRandomUser} className="mb-6">
Fetch New User
</Button>
{loading && (
<div className="flex items-center justify-center">
<ClipLoader className="w-6 h-6 mr-2" />
<span>Loading...</span>
</div>
)}
{error && <div className="text-red-500">{error}</div>}
{user && (
<Card className="border-0 shadow-lg rounded-lg overflow-hidden max-w-sm relative">
<CardHeader className="h-32 bg-[#55efc4] relative">
<Image
src={user.image}
alt={user.name}
width={80}
height={80}
className="rounded-full border-4 border-white absolute bottom-0 left-1/2 transform -translate-x-1/2 translate-y-1/2"
/>
</CardHeader>
<CardContent className="p-6 pt-12 text-center">
<CardTitle className="text-xl font-bold flex items-center justify-center">
<UserIcon className="mr-2" /> {user.name}
</CardTitle>
<CardDescription className="text-muted-foreground flex items-center justify-center">
<MailIcon className="mr-2" /> {user.email}
</CardDescription>
<div className="text-sm text-muted-foreground mt-2 flex items-center justify-center">
<MapPinIcon className="mr-2" /> {user.address}
</div>
<div className="text-sm text-muted-foreground mt-2 flex items-center justify-center">
<InfoIcon className="mr-2" /> {user.description}
</div>
<Button
variant="outline"
className="mt-4"
onClick={handleAppreciate}
>
Appreciate
</Button>
</CardContent>
<CSSTransition
in={appreciationVisible}
timeout={300}
classNames="appreciation"
unmountOnExit
>
<div className="absolute top-0 left-0 w-full h-full flex items-center justify-center bg-white bg-opacity-75">
<h2 className="text-2xl font-bold text-black">❤️ Thank you ✨</h2>
</div>
</CSSTransition>
</Card>
)}
</div>
);
};
export default RandomUser;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 RandomUser from "@/components/random-user";
export default function Home() {
return (
<div>
<RandomUser />
</div>
);
}Running the Project
To see the random user in action, follow these steps:
- Start the Development Server: Run
npm run devto start the development server. - Open in Browser: Open
http://localhost:3000in 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 Random User Generator 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 built a Random User Generator application using Next.js. We covered:
- Setting up the project and using client-side rendering.
- Fetching random user data from an API.
- Displaying user details in a card layout.
- Handling state and user interactions in a React application.
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!
