React Portfolio Website

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:

  • Navbar sits outside Routes, so it shows on every page.
  • Each Route maps a path to a page component, like /about to About.
  • The path="*" catch-all shows a Not Found page for unknown URLs.
  • This is exactly the routing you learned in the React Router module.

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 style function reads isActive and 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:

  • PROJECTS is the data; in a real site it could come from an API.
  • ProjectCard is a small presentational component that takes props and renders one project.
  • The Projects page maps the data into cards, each with a stable key.
  • 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.
  • NavLink highlights 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 Navbar outside Routes, using NavLink to highlight the active page
  • ✅ Rendered a list of projects with a reusable ProjectCard component 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. 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. 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. 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. 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.

Share & Connect