Curriculum1 of 11
+ Module 01

Make a real file yours

Read every line of a real shipped file and say what it does and why.

Phase 0 · Foundations · 11 sections · about 9 minutes

+ 01 / 11

What we're doing today

You're about to read a real, shipped React component line by line. Not a toy example. Not pseudocode. A component that's actually running in production somewhere right now, handling real users' clicks, rendering real data.

Your job: make sense of every line. Understand what it does. More importantly, understand why it's there. There's a difference between "I see JSX markup" and "I see JSX markup that handles empty states correctly."

By the end, you'll have a mental model of how a component thinks. You'll be able to pick up unfamiliar code and read it like prose.

+ 02 / 11

The component: a live search input

Here's a real search component from a shipped app. It takes user input, filters a list, and shows results. The component lives in a larger app where product managers and designers use it to find users by name.

+ JSX
1import { useState } from "react";
2 
3export function SearchUsers({ users = [] }) {
4 const [query, setQuery] = useState("");
5 
6 const filtered = users.filter((user) =>
7 user.name.toLowerCase().includes(query.toLowerCase()),
8 );
9 
10 return (
11 <div>
12 <input
13 type="text"
14 placeholder="Search users..."
15 value={query}
16 onChange={(e) => setQuery(e.target.value)}
17 />
18 
19 {filtered.length === 0 ? (
20 <div>No users found</div>
21 ) : (
22 <ul>
23 {filtered.map((user) => (
24 <li key={user.id}>{user.name}</li>
25 ))}
26 </ul>
27 )}
28 </div>
29 );
30}

Read it once now, without worrying about the parts you don't recognise. The rest of this module walks through it line by line until you can say what each one does and why it's there.

+ 03 / 11

Line 1: Where this code lives

+ JSX
1import { useState } from "react";

This line says: "I need the useState tool from React."

useState is a function React gives you for storing a value that is allowed to change while the component is on screen.

Here is why that needs a special tool. Whenever something changes, React runs your component function again from the top to work out what should now be displayed. Each of those runs is called a render. Any ordinary variable you declare inside the component is created fresh at the start of a render and thrown away at the end of it, so it cannot hold anything from one render to the next. A value stored with useState is kept by React outside your function and handed back to you on every render. That is what lets the search box still contain the letters you typed a moment ago.

You'll learn about useState in detail in the next section. For now, know that this import line is how you get access to it.

+ 04 / 11

Line 3: Naming things we choose

+ JSX
1export function SearchUsers({ users = [] }) {

Let's unpack this.

export is a JavaScript keyword. By default every file is sealed: nothing written inside one file can be seen by any other file. Writing export in front of something marks it as available to the rest of the app.

Another file then reaches it by name, like this:

+ JSX
1import { SearchUsers } from "./SearchUsers";

That is the same mechanism as line 1, running in the other direction. React exports useState, and line 1 imports it. Here the component is exported so that some other file can import it and put it on screen. Without export, SearchUsers would exist only inside this file and nothing else could use it.

function SearchUsers says "this is a component, and we call it SearchUsers." That name is ours to choose. You could call it UserLookup or UserFinder or Typeahead. The word function is mandatory, but the name comes from us.

({ users = [] }) is how the component receives data from whatever is putting it on screen. That data arrives under the name users.

The = [] sets a default. If nothing is passed in, users becomes an empty list rather than being missing altogether. That matters because the next line tries to filter users, and filtering something that does not exist stops the component from rendering at all. With the default, the component runs and simply has nothing to show.

+ 05 / 11

Line 4: Remembering the search query

+ JSX
1const [query, setQuery] = useState("");

This is useState at work. Let's break it down.

useState('') says "I'm creating a piece of state that starts as an empty string." An empty string is '' in code, just like a blank text field.

const [query, setQuery] is how you grab that piece of state. You get two things:

  • query: the current value (what the user has typed so far)
  • setQuery: a function to change it

The names query and setQuery are ours to pick. You could call them searchText and setSearchText instead. But the pattern is standard: the second name is always set plus the first name, in camelCase.

Every time the user types a letter, setQuery will be called, and the component will re-render with the new query value. That's how React keeps the display up to date.

+ 06 / 11

Line 6–8: Filtering the list

+ JSX
1const filtered = users.filter((user) =>
2 user.name.toLowerCase().includes(query.toLowerCase()),
3);

This is JavaScript's .filter() method at work. It takes a list and keeps only the items that match a test.

Here, the test is: "Does this user's name include the letters the user typed?" The user.name.toLowerCase() turns the user's name to lowercase, and .includes(query.toLowerCase()) checks if it contains the search query (also lowercased). This way, searching for "john" finds "John" and "JOHN" too.

filter() returns a new list containing only the matches. If the user types "al" and there are users named "Alice", "Albert", and "Bob", the filtered list will have Alice and Albert. Bob gets left out.

The component doesn't modify the original users list. It creates a new one. This is important, and you'll learn why in later modules.

+ 07 / 11

Line 10–12: The search input

+ JSX
1<input
2 type="text"
3 placeholder="Search users..."

This looks like HTML, because it basically is. In React, we can write HTML-like markup called JSX. It compiles to actual HTML and JavaScript.

type="text" makes it a plain text input field.

placeholder="Search users..." is the grey text that shows up when the input is empty, to hint to the user what they should do.

But look at the next line:

+ JSX
1value={query}
2onChange={(e) => setQuery(e.target.value)}

This is where React takes over. value={query} means "make this input show whatever's in the query state." If query is "jo", the input shows "jo". This is called a controlled input. React controls what appears in the input at all times.

onChange={(e) => setQuery(e.target.value)} says "when the user types something, call setQuery with the new text." The e is the browser's event object, and e.target.value is what the user just typed. So when they type "alice", this line calls setQuery("alice"), which updates query, which re-renders the component with the filtered list.

This cycle, type, update state, re-render, show new filtered results, happens instantly. That's React's core trick.

+ 08 / 11

Line 15–19: Handling empty results

+ JSX
1{filtered.length === 0 ? (
2 <div>No users found</div>
3) : (
4 <ul>
5 {filtered.map(user => (

This is a ternary operator. It's short for if-then-else.

filtered.length === 0 ? asks "is the filtered list empty?" If yes, show <div>No users found</div>. If no, show the list of results.

Why this matters: a blank screen is confusing. If the user searches for "xyzzy" and gets nothing, they don't know if it's broken or if there are just no matches. The "No users found" message tells them the component is working, but the search didn't match anyone.

This is a design decision baked into code. The designer and developer agreed: empty states matter. So the code explicitly handles them.

+ 09 / 11

Line 20–23: Looping with .map

+ JSX
1<ul>
2 {filtered.map((user) => (
3 <li key={user.id}>{user.name}</li>
4 ))}
5</ul>

The <ul> is an unordered list in HTML. Inside it, we loop over the filtered array using .map().

.map() is JavaScript's way of saying "for each item in this array, create something." Here, for each user, we create an <li> (list item) showing their name.

{user.name} pulls the name out of the current user object and shows it on screen.

But the crucial line is key={user.id}. This is not for the user to see. It's for React's internal bookkeeping.

When React re-renders and the list changes, React needs to know which items are the same as before and which are new. If you delete a user from the list, React needs to know which <li> to remove. The key prop is like an ID bracelet on each list item. React uses it to match items before and after the render.

Never use the item's position in the array as the key. If the list re-orders, React will get confused. Always use a unique ID like user.id.

+ 10 / 11

What's happening when the user types

Let's trace through what happens when a user finds "alice":

+ Try it5 of 5

One keystroke, six things happen

  • alice
  • bob
  • charlie
  • diana
  • evelyn
What just happened

Type something in the box to trace the cycle.

Type, then walk the six steps. Every one of them runs again on the next keystroke.

  1. 1User clicks the input and types "a".
  2. 2The onChange event fires. setQuery("a") gets called.
  3. 3React re-renders the component with query set to "a".
  4. 4The .filter() runs again, keeping only users whose names include "a".
  5. 5The display updates to show only the matches.
  6. 6User types another letter: "l".
  7. 7Same cycle repeats. query is now "al". The list narrows further.
  8. 8User types "i", then "c", then "e".
  9. 9After each keystroke, the cycle repeats. Each render, .filter() runs fresh, and the results update instantly.
  10. 10The component is now showing only users whose names include "alice".

This cycle, type, update state, re-render, show new results, is the core of how interactive React components work. It's the same pattern whether you're filtering users, searching for products, or sorting posts by date.

+ 11 / 11

Checkpoint

You've just read a real, production component line by line. You know what each line does and why it's there. Let's check.

+ Checkpoint

What does the setQuery function do when the user types in the input?

+ Checkpoint

Why do we need the key={user.id} prop on the list items?

+ Checkpoint

What would happen if we removed the 'No users found' message?

+ Checkpoint

Why does the component have users = [] as a default prop?

+ Up nextHow the web runs