Add via modal
- React course
- Intermediate
- Lesson 76/80
- 5 min
Write an add modal with portal and Suspense.
Project outcome
In this project we built a dashboard: layout and routing skeleton, a stats widget, and an add modal. Each piece was added in one lesson, and the previous lesson's solution became the next one's starting code.
You learned nested routes with React Router, child routes via <Outlet>, widgets with StatCard, and form addition with AddStatModal.
At the end you have a modular, real-world app skeleton. In the next level we will grow this structure with context and data fetching.
Final step: combine
Now you combine all pieces in a single file. You write StatCard and AddStatModal definitions together with StatsPage and DashboardLayout components.
StatsPage holds two states: a stats array and an isOpen boolean. The add button opens the modal; the modal's onAdd adds a new StatCard.
This pattern is common in real projects: small pieces are separate components; the main page combines them.
Practice Dashboard final
Combine all pieces: write StatCard, AddStatModal, StatsPage and DashboardLayout. In StatsPage keep a stats array and isOpen in useState; an "Add" button calls setIsOpen(true). The onAdd from AddStatModal adds a new StatCard. DashboardApp shows StatsPage on /stats with BrowserRouter. export default DashboardApp.
Starter code
import { useState, useRef } from "react";
import { BrowserRouter, Routes, Route, NavLink, Outlet } from "react-router-dom";
// StatCard, AddStatModal, StatsPage, DashboardLayout, DashboardApp buraya yazSample solution
import { useState, useRef } from "react";
import { BrowserRouter, Routes, Route, NavLink, Outlet } from "react-router-dom";
function StatCard({ label, value }) {
return (
<article>
<h3>{label}</h3>
<p>{value}</p>
</article>
);
}
function AddStatModal({ isOpen, onAdd, onClose }) {
const inputRef = useRef(null);
if (!isOpen) return null;
function handleSubmit(e) {
e.preventDefault();
onAdd(inputRef.current.value);
onClose();
}
return (
<div>
<form onSubmit={handleSubmit}>
<input ref={inputRef} />
<button type="submit">Add</button>
</form>
</div>
);
}
function StatsPage() {
const [stats, setStats] = useState([{ label: "Users", value: 120 }]);
const [isOpen, setIsOpen] = useState(false);
return (
<section>
{stats.map((s, i) => (
<StatCard key={i} label={s.label} value={s.value} />
))}
<button onClick={() => setIsOpen(true)}>Add</button>
<AddStatModal
isOpen={isOpen}
onAdd={(v) => setStats([...stats, { label: v, value: 0 }])}
onClose={() => setIsOpen(false)}
/>
</section>
);
}
function DashboardLayout() {
return (
<>
<nav><NavLink to="/stats">Stats</NavLink></nav>
<main><Outlet /></main>
</>
);
}
function DashboardApp() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<DashboardLayout />}>
<Route path="stats" element={<StatsPage />} />
</Route>
</Routes>
</BrowserRouter>
);
}
export default DashboardApp;Questions and answers
Review the lesson questions together with their answers. To test yourself, solve the lesson interactively.
1 Which is NOT one of the things you learned in this project?
- useTransition Correct answer
- Add via modal
- StatCard component
- Nested routing
Explanation: useTransition was not covered in this project.
2 Close the modal
function handleClose() {
setIsOpen(____);
}
function handleClose() {
setIsOpen(false);
}
Explanation: Setting isOpen to false closes the modal.
3 Add via modal: match the terms
| Term | Meaning |
|---|---|
BrowserRouter |
Router shell |
Outlet |
Child slot |
NavLink |
Menu link |
StatCard |
Data card |