React Portfolio Website
Table of Contents + −
In the last lesson, React E-commerce Product Listing, you built a shop page with filtering and a cart. Now for the final project: a portfolio website that ties together routing, components, props, lists, and a form into one real multi-page app.
🎯 What we’re building
Here’s the plan, a real multi-page site:
- Several pages: Home, About, Projects, and Contact, each at its own URL.
- A navigation bar that’s on every page and highlights the active link.
- A Projects page that renders a list of project cards from data.
- A Contact page with a small form.
So it’s a complete site that uses routing for the pages and components for the sections.
🧩 Step 1: set up routing
We wrap the app in BrowserRouter (in main.jsx) and define the routes for each page.
import { Routes, Route } from "react-router-dom";
function App() { return ( <div> <Navbar /> {/* on every page, outside Routes */}
<Routes> <Route path="/" element={<Home />} /> <Route path="/about" element={<About />} /> <Route path="/projects" element={<Projects />} /> <Route path="/contact" element={<Contact />} /> <Route path="*" element={<NotFound />} /> </Routes> </div> );}Read the routing setup:
Navbarsits outsideRoutes, so it shows on every page.- Each
Routemaps a path to a page component, like/abouttoAbout. - The
path="*"catch-all shows a Not Found page for unknown URLs. - This is exactly the routing you learned in the React Router module.
🧭 Step 2: the navigation bar with NavLink
The nav uses NavLink so the current page’s link is highlighted automatically.
import { NavLink } from "react-router-dom";
function Navbar() { const linkStyle = ({ isActive }) => ({ fontWeight: isActive ? "bold" : "normal", });
return ( <nav> <NavLink to="/" style={linkStyle}>Home</NavLink> <NavLink to="/about" style={linkStyle}>About</NavLink> <NavLink to="/projects" style={linkStyle}>Projects</NavLink> <NavLink to="/contact" style={linkStyle}>Contact</NavLink> </nav> );}Walk through the nav:
- We use NavLink, which knows when its page is active.
- The
stylefunction readsisActiveand bolds the current page’s link. - Clicking a link changes the page with no reload, the SPA behavior.
- So the nav shows the user where they are, using the same pattern from the routing module.
🗂️ Step 3: the Projects page with a reusable card
The Projects page renders a list of projects from data, using a reusable ProjectCard component.
const PROJECTS = [ { id: 1, title: "Todo App", description: "A task manager built in React.", link: "#" }, { id: 2, title: "Weather App", description: "Live weather from an API.", link: "#" }, { id: 3, title: "Movie Search", description: "Search movies as you type.", link: "#" },];
function ProjectCard({ title, description, link }) { return ( <div className="card"> <h3>{title}</h3> <p>{description}</p> <a href={link}>View project</a> </div> );}
function Projects() { return ( <div> <h1>My Projects</h1> <div className="grid"> {PROJECTS.map((project) => ( <ProjectCard key={project.id} title={project.title} description={project.description} link={project.link} /> ))} </div> </div> );}See how this reuses your skills:
PROJECTSis the data; in a real site it could come from an API.ProjectCardis a small presentational component that takes props and renders one project.- The
Projectspage maps the data into cards, each with a stablekey. - So this is props, list rendering, and reusable components, all from earlier modules.
📬 Step 4: the Contact page with a form
The Contact page has a controlled form with basic validation, like you built in the Forms module.
import { useState } from "react";
function Contact() { const [form, setForm] = useState({ name: "", message: "" }); const [sent, setSent] = useState(false);
function handleChange(e) { setForm({ ...form, [e.target.name]: e.target.value }); }
function handleSubmit(e) { e.preventDefault(); if (form.name.trim() === "" || form.message.trim() === "") return; // in a real app you'd send this to a server setSent(true); }
if (sent) return <p>Thanks, {form.name}! Your message was sent.</p>;
return ( <form onSubmit={handleSubmit}> <input name="name" value={form.name} onChange={handleChange} placeholder="Your name" /> <textarea name="message" value={form.message} onChange={handleChange} placeholder="Your message" /> <button type="submit">Send</button> </form> );}Walk through the contact form:
- One state object holds the form, updated by a single handler using the input’s
name. - On submit we ignore empty fields, then show a thank-you message.
- So this uses controlled inputs, multiple inputs with one handler, and conditional rendering, all from the Forms module.
🧩 How it all fits together
Step back and notice how this one project uses the whole course:
- React Router for the pages, navigation, and 404.
- Components and props for the nav, cards, and pages.
- List rendering for the projects.
- State, controlled forms, and conditional rendering for the contact page.
- Best practices throughout: small components, stable keys, derived values.
So a portfolio site isn’t one new idea, it’s all your ideas working together. If you can build this, you can build a real React app.
Make it truly yours
This is your portfolio, so fill it with your real projects, your bio, and your links. Add a theme switcher with context, fetch projects from an API, or animate the page transitions. It’s a great piece to show employers, and every upgrade reuses what you’ve learned.
✅ Best Practices used here
A few good habits in this final project:
- Shared layout (the nav) sits outside
Routes, so it’s on every page. NavLinkhighlights the active page automatically.- Pages and cards are small, reusable components fed by props.
- The form is controlled, with one handler and basic validation.
🧩 What You’ve Learned
- ✅ Built a complete multi-page portfolio site with React Router
- ✅ Put a shared
NavbaroutsideRoutes, usingNavLinkto highlight the active page - ✅ Rendered a list of projects with a reusable
ProjectCardcomponent and props - ✅ Built a controlled contact form with one handler and basic validation
- ✅ Added a catch-all
*route for unknown URLs - ✅ Saw how routing, components, props, lists, state, and forms come together in one real app
Check Your Knowledge
Test what you learned. Pick an answer for each question, then click Check.
- 1
Where should the navigation bar go so it appears on every page?
Why: Anything that should appear on every page goes outside Routes. Only the part inside Routes changes when the URL changes.
- 2
Why use NavLink instead of Link for the nav menu?
Why: NavLink knows when its page is active, so you can style the current link (like bolding it). Both navigate without a reload; NavLink adds the active state.
- 3
How does the Projects page render its project cards?
Why: The page maps the data array into ProjectCard components, passing props and giving each a stable key, reusing list rendering and component patterns.
- 4
What course concepts does the Contact form use?
Why: The form uses controlled inputs bound to one state object, a single change handler using the input name, and conditional rendering to show the thank-you message.
🎉 Congratulations, you finished the course!
That’s the final project, and the end of the React course. Look at how far you’ve come. You started with what React is, and you can now build complete, multi-page applications.
Along the way you learned:
- Components, props, and state, the core of every React app.
- Events, conditional rendering, lists, and forms.
- Hooks:
useState,useEffect,useRef,useMemo,useCallback,useContext, and your own custom hooks. - Fetching data from APIs, with loading, error, and empty states handled cleanly.
- Routing with React Router, sharing state with Context, and optimizing performance.
- Advanced patterns, clean project structure, and the best practices that tie it all together.
So keep building. The best way to get better at React is to make things, your own projects, your own ideas. You have everything you need now. Go build something great.