Section 2 · Components & Props
Thinking in Components
16 min3 min readChoose wisely
Lesson at a glance
Turn a screen mockup into a component tree, build a static version first, and identify the minimal state the Watchlist needs before writing any interactive code.
- 1Draw boxes, name them
- 2When to split, and when not to
- 3Build the static version first
- 4Find the minimal state
- 5Where the data lives
No video here — just the real thing: a written lesson, runnable examples, a quiz and practice.
Here’s the Watchlist you’re building, described as a designer would hand it over: a header with the title and “3 to watch”; a form with a title input and an Add button; a row of filter buttons (All, To watch, Watched); and a list of movies, each with its title, year and a checkbox to mark it watched. When the list is empty, a friendly message replaces it.
Before you write a line of code, decide what the components are. It’s the most important design step in a React app, and it’s done with a pen.
Draw boxes, name them
Take the mockup and draw a box around every piece that does one job. Then name each box as a noun, the way you’d name a function.
Nested boxes become a tree. App owns the whole screen. MovieList renders either many MovieItems or one EmptyState. This tree is the plan for the rest of the course; you’ll build each box as a component.
When to split, and when not to
Split a piece into its own component when at least one of these is true:
- It repeats.
MovieItemappears once per movie. Writing it once is the point. - It has its own job.
AddMovieFormhandles typing and submitting. Nothing else needs to know how. - The parent is getting hard to read. If you scroll to understand a component, extract a named piece.
Don’t split just because you can. A component that only wraps one <span> adds a file, an import and a name to remember, and gives nothing back. Experienced teams often keep a component at 100 lines rather than splitting it into five 20-line ones that are only used together. Start a little bigger and extract when one of the three reasons appears.
Build the static version first
With the tree drawn, build it with hard-coded data and no interactivity. Pass data down from the top so the shape is right from the start:
const MOVIES = [
{ id: 1, title: 'Arrival', year: 2016, watched: true },
{ id: 2, title: 'Past Lives', year: 2023, watched: false },
{ id: 3, title: 'Paddington 2', year: 2017, watched: false },
];
export default function App() {
return (
<main>
<Header movies={MOVIES} />
<AddMovieForm />
<FilterBar />
<MovieList movies={MOVIES} />
</main>
);
}
// …Header, AddMovieForm, FilterBar and MovieList defined belowBuilding the static version is mostly typing, and it gets the structure and markup right while nothing moves. Adding interactivity is mostly thinking. Doing both at once is how components end up tangled.
Find the minimal state
Next, list every piece of data in the app and ask three questions about each. Does it stay the same over time? Is it passed in from a parent? Can you calculate it from other data? If the answer to any of them is yes, it isn’t state.
For the Watchlist:
| Data | State? | Why |
|---|---|---|
| The list of movies | Yes | Changes when you add, remove or mark one |
| The current filter | Yes | Changes when you click a filter button |
| Text typed in the form | Yes | Changes as you type |
| Number left to watch | No | Calculated from the movies |
| Movies currently visible | No | Calculated from movies + filter |
Three pieces of state, two derived values. Keeping state minimal is the single habit that prevents the most bugs in React apps. Every extra piece of state is a second copy of the truth that can disagree with the first.
Where the data lives
The movies are needed by Header (for the count) and MovieList (to show them). The rule: data lives in the closest common parent of every component that needs it, which here is App. It flows down as props. When a child needs to change it, the parent passes down a function to call. You’ll do exactly that in Section 3.
Next, you’ll learn props properly: how to pass data into components, set defaults and keep components reusable.
Key takeaways
- Draw boxes around the mockup and name each box; nested boxes become a component tree.
- Split a component when a piece is reused, has its own job, or makes the parent hard to read, not by default.
- Build a static version from hard-coded data first, then add interactivity.
- Keep the minimal state; anything you can calculate from other data, such as a count or a filtered list, is derived, not state.