Week 5

State with useState

Render and commit lifecycle

Event Handling

Forms

Practice

Assignment

Front end Track

Week 5 Assignment: Adding Interactivity to Your Portfolio

Focus: useState, event handling, controlled forms, lifting state up

Description

Your portfolio is a working React app. Right now it's static — the content renders, but nothing responds to user interaction. This week you add the layer that makes it feel alive: a dark mode toggle that actually tracks its own state, a contact form with controlled inputs and validation, and a projects filter that lets visitors narrow down what they see.

By the end of this assignment, your week 6 starting point will be a React portfolio with components for each section, a dark mode toggle using useState, and a contact form with controlled inputs and basic validation — exactly what week 6 builds on.

What you're building on

Your week 4 portfolio should have:

You're not starting over — you're extending what's already there.

Requirements

1. Upgrade the dark mode toggle with useState

In week 4 the toggle changed the DOM directly. Replace that with React state so the button label stays in sync:

export default function Header() {
  const [isDark, setIsDark] = useState(false);

  function handleThemeToggle() {
    setIsDark(!isDark);
    document.body.classList.toggle('dark-mode');
  }

  return (
    <header>
      {/* your name, tagline, image */}
      <button onClick={handleThemeToggle}>
        {isDark ? 'Light mode' : 'Dark mode'}
      </button>
    </header>
  );
}

Requirements:

2. Build a controlled contact form

Replace your week 4 form with a fully controlled version. Every input is driven by React state.

interface FormData {
  name: string;
  email: string;
  message: string;
}

interface FormErrors {
  name?: string;
  email?: string;
  message?: string;
}

export default function Contact() {
  const [formData, setFormData] = useState<FormData>({
    name: '',
    email: '',
    message: '',
  });
  const [errors, setErrors] = useState<FormErrors>({});
  const [submitted, setSubmitted] = useState(false);

  // your handlers here
}

Requirements:

<aside> ⚠️

Do not put validation logic inside the onChange handlers. Validate only on submit — checking on every keystroke makes the form feel hostile before the user has finished typing.

</aside>

3. Add a projects filter

Give visitors a way to filter your project list by technology. This requires lifting state up: the filter lives in the parent that owns both the filter controls and the project list.

const projects: Project[] = [
  {
    id: 1,
    title: 'Portfolio Page',
    description: 'Built with HTML and CSS in weeks 1 and 2.',
    techStack: ['HTML', 'CSS'],
    url: '',
  },
  {
    id: 2,
    title: 'React Portfolio',
    description: 'Rebuilt as a React application in weeks 4 and 5.',
    techStack: ['React', 'TypeScript'],
    url: '',
  },
  // your other projects
];

Requirements:

<aside> 💡

To collect unique tech tags from all projects: const allTags = [...new Set(projects.flatMap((p) => p.techStack))]

</aside>

4. Verify and commit

npm run dev     # no console errors or warnings
npm run build   # production build passes

Confirm all three features work together:

Push to your portfolio GitHub repository. If you're on a branch from week 4, merge it or continue on the same branch.

Hints


The HackYourFuture curriculum is licensed under CC BY-NC-SA 4.0 *https://hackyourfuture.net/*

CC BY-NC-SA 4.0 Icons

Built with ❤️ by the HackYourFuture community · Thank you, contributors

Found a mistake or have a suggestion? Let us know in the feedback form.