A beginner-friendly React app that displays a list of people and their birthdays. You can view everyone on the list and clear the list with one click. It’s built to teach and practice core React ideas: components, props, state, and styling—with no backend or API. Use it as a reference for learning React or as a starting point for your own list-based UIs.
Live Demo: https://listview-display.vercel.app/
- Project Summary
- Features
- Technologies Used
- Project Structure
- Getting Started
- Environment Variables (.env)
- How the Project Works
- Component Walkthrough
- Data & Styling
- Reusing Components in Other Projects
- Scripts & Commands
- API, Backend & Routes
- Keywords & Concepts
- Conclusion
- License
Birthday Buddy is a single-page React application that shows a list of people with their images, names, and ages (birthday count). The app uses static data from a local JavaScript file—no server, database, or external API. It demonstrates:
- Components: Breaking the UI into
App,List, andPerson. - State: Using
useStateto hold the list and clear it. - Props: Passing data from parent to child (
people→List→Person). - List rendering: Using
array.map()and a stablekeyfor each item.
The project is ideal for learning React fundamentals and for teaching others. You can extend it with filtering, adding items, or connecting to an API later.
- Display list: Renders a list of people with avatar, name, and age.
- Dynamic count: Heading shows how many birthdays (e.g. “5 birthdays today”).
- Clear all: One button clears the entire list by setting state to an empty array.
- Component-based UI: Separate
App,List, andPersoncomponents for clarity and reuse. - Styled with CSS: Global styles in
index.csswith CSS variables, layout, and responsive-friendly design. - Educational: Focused on React basics—no routing, no backend, minimal dependencies.
| Technology | Purpose |
|---|---|
| React 18 | UI components, state, and rendering |
| Vite 4 | Dev server, build, and HMR |
| JavaScript (ES6+) | Logic, modules, and JSX |
| CSS3 | Layout, theming via variables, and visuals |
There is no TypeScript, no backend, and no database in this project.
01-birthday-buddy/
├── public/ # Static assets (e.g. favicon)
│ └── vite.svg # Favicon used in index.html
├── src/
│ ├── App.jsx # Root component: state + layout
│ ├── List.jsx # Renders list of Person items
│ ├── Person.jsx # Single person card (image, name, age)
│ ├── data.js # Static array of people (no API)
│ ├── index.css # Global styles and CSS variables
│ ├── main.jsx # Entry: mounts App into #root
│ └── assets/ # Optional images/assets
├── .env # Optional; not used by default (see below)
├── .eslintrc.cjs # ESLint config for JS/JSX
├── .gitignore
├── index.html # HTML shell and SEO meta tags
├── package.json
├── vite.config.js # Vite + React plugin
└── README.md- Node.js (v16 or newer recommended)
- npm (or yarn/pnpm)
-
Clone the repository
git clone <your-repo-url> cd 01-birthday-buddy
-
Install dependencies
npm install
-
Development:
npm run dev
Then open the URL shown in the terminal (e.g. http://localhost:5173). -
Production build:
npm run build
Output is in thedist/folder. -
Preview production build:
npm run preview
Serves the built app locally. -
Lint:
npm run lint
Runs ESLint onsrc(see Scripts & Commands).
This project does not use environment variables by default. All data comes from src/data.js.
If you later add an API or config (e.g. API base URL), you can use a .env file:
-
Create
.envin the project root (same level aspackage.json).
It is already listed in.gitignore, so it won’t be committed. -
Define variables with the
VITE_prefix (required for Vite to expose them to the client):VITE_API_BASE_URL=https://api.example.com VITE_APP_TITLE=Birthday Buddy
-
Use them in your code:
const apiBase = import.meta.env.VITE_API_BASE_URL; const title = import.meta.env.VITE_APP_TITLE;
-
Optional: Add a
.env.examplefile (without secrets) and commit it, so others know which variables are needed:VITE_API_BASE_URL= VITE_APP_TITLE=Birthday Buddy
For this starter project, no .env file is required to run or build.
- Entry:
index.htmlloadssrc/main.jsx, which renders<App />inside<div id="root">. - State:
App.jsximports the people array fromdata.jsand stores it in state withuseState(data). - Display: The same state is used to:
- Show the count in the heading:
{people.length} birthdays today. - Pass
peopleto<List people={people} />.
- Show the count in the heading:
- List:
List.jsxmaps overpeopleand renders one<Person />per item, passing each person’s fields as props (andkey={person.id}). - Person:
Person.jsxreceivesimage,name, andageand renders an avatar, name, and “X years”. - Clear: The “clear all” button calls
setPeople([]), so the list becomes empty and the UI updates.
Data flow: data.js → App (state) → List (props) → Person (props). No API or backend is involved.
Root component. It holds the list in state and renders the layout.
- Imports
useState,data, andList. const [people, setPeople] = useState(data)— initial list fromdata.js.- Renders a container with:
- Heading:
{people.length} birthdays today <List people={people} />- Button that runs
() => setPeople([])to clear the list.
- Heading:
import { useState } from "react";
import data from "./data";
import List from "./List";
function App() {
const [people, setPeople] = useState(data);
return (
<main>
<section className="container">
<h3>{people.length} birthdays today</h3>
<List people={people} />
<button
type="button"
className="btn btn-block"
onClick={() => setPeople([])}
>
clear all
</button>
</section>
</main>
);
}
export default App;Presentational component. It receives people and renders one Person per item.
- Uses
people.map()withkey={person.id}. - Spreads each
personinto<Person />soPersonreceivesid,name,age,image.
import Person from "./Person";
const List = ({ people }) => {
return (
<section>
{people.map((person) => {
return <Person key={person.id} {...person} />;
})}
</section>
);
};
export default List;Displays a single person: image, name, and age.
- Props:
image,name,age(from parent via spread). - Uses semantic
<article>and classes for styling (person,img).
const Person = ({ image, name, age }) => {
return (
<article className="person">
<img src={image} alt={name} className="img" />
<div>
<h4>{name}</h4>
<p>{age} years</p>
</div>
</article>
);
};
export default Person;Entry point. Mounts the app and applies global CSS.
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import "./index.css";
ReactDOM.createRoot(document.getElementById("root")).render(
<React.StrictMode>
<App />
</React.StrictMode>,
);Exports a single array of person objects. No API calls.
Each item has: id, name, age, image (URL string).
export default [
{ id: 1, name: "Bertie Yates", age: 29, image: "https://..." },
{ id: 2, name: "Hester Hogan", age: 32, image: "https://..." },
// ...
];You can replace or extend this array; the UI will reflect it as long as each object has id, name, age, and image.
- Global reset: Box-sizing, margins, base font size.
- CSS variables: Colors (primary, grey, semantic), spacing, shadows, transitions.
- Layout:
.container(max-width, padding, shadow),.person(grid for avatar + text). - Components:
.btn,.btn-block, typography,.img(round avatar style).
The app uses these classes in App, List, and Person; no CSS-in-JS or extra UI library.
- Person: Copy
Person.jsx(and the.person/.imgstyles fromindex.css). Use it anywhere you need to show a single person/card with image, title, and subtitle. Props:image,name,age(or adapt prop names). - List: Copy
List.jsxandPerson.jsx. Replace the inner component if you need a different card (e.g. product, event). Keep themap+keypattern. - App pattern: The “state in parent + pass down as props” pattern in
App.jsxis reusable for any list CRUD: state holds the array, children receive it and a setter (or callbacks) as needed.
Ensure your data shape matches what Person expects (id, name, age, image), or adjust the component and props accordingly.
| Command | Description |
|---|---|
npm run dev |
Start Vite dev server (e.g. http://localhost:5173) |
npm run build |
Build for production into dist/ |
npm run preview |
Serve the production build locally |
npm run lint |
Run ESLint on .js and .jsx files |
- API: None. Data is static in
src/data.js. - Backend: None. Pure client-side React + Vite.
- Routes: Single page only. No React Router or path-based routing.
To use an API later: fetch in App.jsx (e.g. in useEffect), put the result in state, and pass that state to List the same way. You can move the API base URL into .env as in Environment Variables (.env).
- React, components, JSX
- useState, state management
- Props, prop drilling
- List rendering, key prop, array.map
- Functional components
- Vite, ES modules
- CSS variables, semantic HTML
- No backend, no API, no routing
Birthday Buddy is a small, focused React project for learning components, state, props, and list rendering. The structure is simple enough to follow and to extend (e.g. filters, add/remove items, or an API). Use it as a reference or as a template for other list-based UIs.
This project is licensed under the MIT License. Feel free to use, modify, and distribute the code as per the terms of the license.
This is an open-source project - feel free to use, enhance, and extend this project further!
If you have any questions or want to share your work, reach out via GitHub or my portfolio at https://www.arnobmahmud.com.
Enjoy building and learning! 🚀
Thank you! 😊
