Week 6

useEffect - Side Effects, Data Fetching & Async State

Browser DevTools - The Network Tab

Tailwind v4

React Router

Deployment with Vercel

Practice

Assignment

Front end Track

Week 6 Assignment: Portfolio with Routing, Tailwind & Deployment

Focus: React Router, Tailwind CSS v4, Vercel deployment

Description

Over the past five weeks you've built a portfolio page in HTML, styled it with CSS, and rebuilt it as a React application. This week you take the final structural step: adding multiple pages with React Router, restyling with Tailwind v4, and deploying it to a live URL on Vercel.

By the end of this assignment, your portfolio will be publicly accessible on the internet: not running on localhost, but at a real URL you can share with anyone! 🎉

What you're building on

This assignment assumes your portfolio is currently a React application from weeks 4–5, with:

If your week 4–5 project differs from this, adapt the requirements to fit what you have. The core deliverables are the same.

Requirements

1. Add React Router to your project

Install React Router:

npm install react-router

Wrap your application in a BrowserRouter in main.tsx:

import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import { BrowserRouter } from 'react-router';
import App from './App.tsx';
import './index.css';

createRoot(document.getElementById('root')!).render(
  <StrictMode>
    <BrowserRouter>
      <App />
    </BrowserRouter>
  </StrictMode>
);

2. Split your portfolio into multiple pages

Move your existing content into separate page components. The structure should look like this when you're done:

src/
├── components/
│   ├── Navigation.tsx
│   └── ThemeToggle.tsx
├── pages/
│   ├── HomePage.tsx        ← your name, tagline, About section
│   ├── ProjectsPage.tsx    ← your projects
│   ├── ContactPage.tsx     ← your contact form
│   └── NotFoundPage.tsx    ← 404 fallback
└── App.tsx                 ← route definitions

Define your routes in App.tsx:

import { Routes, Route } from 'react-router';
import { Navigation } from './components/Navigation';
import { HomePage } from './pages/HomePage';
import { ProjectsPage } from './pages/ProjectsPage';
import { ContactPage } from './pages/ContactPage';
import { NotFoundPage } from './pages/NotFoundPage';

export default function App() {
  return (
    <>
      <Navigation />
      <main>
        <Routes>
          <Route path="/" element={<HomePage />} />
          <Route path="/projects" element={<ProjectsPage />} />
          <Route path="/contact" element={<ContactPage />} />
          <Route path="*" element={<NotFoundPage />} />
        </Routes>
      </main>
    </>
  );
}

Page requirements:

Navigation:

After form submission:

3. Add Tailwind v4

Install Tailwind and the Vite plugin:

npm install tailwindcss @tailwindcss/vite

Add the plugin to vite.config.ts:

import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import tailwindcss from '@tailwindcss/vite';

export default defineConfig({
  plugins: [
    react(),
    tailwindcss(),
  ],
});

Add the import to src/index.css:

@import "tailwindcss";

Then replace your existing CSS with Tailwind utility classes.

Requirements:

@import "tailwindcss";

@theme {
  --color-brand: #your-chosen-colour;
}

Do not use arbitrary values (text-[17px], mt-[13px]) — use the built-in Tailwind scale.

4. Update the dark mode toggle

Your dark mode toggle from weeks 4–5 still works the same way — it needs no changes to the logic. If you haven't already, move the toggle into your Navigation component so it appears on every page.

import { useState } from 'react';

export function ThemeToggle() {
  const [dark, setDark] = useState(false);

  function toggle() {
    setDark(!dark);
    document.documentElement.classList.toggle('dark');
  }

  return (
    <button
      onClick={toggle}
      className="px-3 py-1 rounded-md text-sm font-medium bg-gray-100 hover:bg-gray-200 dark:bg-gray-800 dark:hover:bg-gray-700"
      aria-label="Toggle dark mode"
    >
      {dark ? 'Light mode' : 'Dark mode'}
    </button>
  );
}

Add dark: variants to your components to make the toggle visually meaningful — at minimum the background and text colour of the page should change.

<aside> ⚠️

Warning

Tailwind's dark: prefix only works if the dark class is on the <html> element. The toggle above handles this via document.documentElement.classList.toggle('dark').

</aside>

5. Run a production build locally

Before deploying, verify the build succeeds on your machine:

npm run build

Fix any TypeScript errors or build failures before continuing. A build that passes locally will almost always pass on Vercel.

6. Deploy to Vercel

  1. Push your project to a GitHub repository
  2. Go to vercel.com, connect your GitHub account, and import the repository
  3. Vercel detects it's a Vite project and pre-fills the build settings correctly — leave them as-is
  4. Click Deploy
  5. Once deployed, open the live URL and verify:

Hints

{
  "rewrites": [{ "source": "/(.*)", "destination": "/index.html" }]
}

This tells Vercel to always serve index.html and let React Router handle the routing client-side.