1. Project Setup with Vite
This course uses Vite to scaffold and run React apps — it's faster than older tooling like Create React App (now discontinued) and is what most new React projects reach for today. One command gets you a working TypeScript project:
npm create vite@latest week-01 -- --template react-ts
cd week-01
npm install
npm run dev
npm run dev starts a local dev server (usually at
http://localhost:5173) with hot module replacement — save a file and
the browser updates instantly, without a full page reload or losing component state.
week-01/
├── index.html
├── package.json
├── vite.config.ts
└── src/
├── main.tsx ← mounts the app into index.html
├── App.tsx ← root component
└── App.css
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import App from './App';
createRoot(document.getElementById('root')!).render(
<StrictMode>
<App />
</StrictMode>,
);
createRoot(...).render(<App />) is the one line where React takes
over a DOM node and starts managing everything inside it. Everything else in this
course happens inside that tree.
2. JSX & Elements
JSX — the HTML-like syntax inside your .tsx files — isn't valid
JavaScript on its own. A build tool (Vite, via esbuild) compiles it into plain
function calls before your code ever runs in the browser.
const heading = <h1 className="title">Hello, React</h1>;
const heading = jsx('h1', { className: 'title', children: 'Hello, React' });
That function call returns a plain JavaScript object — a React element — describing what should appear on screen: a type, some props, and children. React elements are cheap, immutable descriptions, not real DOM nodes yet.
function Greeting({ name }: { name: string }) {
const hour = new Date().getHours();
const greeting = hour < 12 ? 'Good morning' : 'Good afternoon';
return (
<div>
<p>{greeting}, {name}!</p>
{name === 'Ada' && <span>⭐ Welcome back</span>}
</div>
);
}
Curly braces {'{ }'} drop back into JavaScript inside JSX — any
expression works, including ternaries for either/or rendering and &&
for conditionally rendering something or nothing.
className, not class?
JSX attributes are JavaScript object properties under the hood, and class is a reserved word in JavaScript. This is also why event handlers are onClick (camelCase), not onclick — JSX props follow JS naming conventions, not HTML's.
3. The Virtual DOM: Why React Re-Renders
Every time a component's state or props change, React calls that component's function again, producing a new tree of elements. Instead of touching the real DOM immediately, React diffs the new element tree against the previous one, and only applies the minimal set of real DOM changes needed.
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
console.log('Counter rendered'); // logs on every click
return (
<button onClick={() => setCount(count + 1)}>
Clicked {count} times
</button>
);
}
Clicking the button calls setCount, which schedules a re-render:
Counter() runs again from the top, producing a new element tree. React
compares it to the previous tree, sees only the text node changed, and updates
just that text node in the real DOM — not the whole button, and definitely
not the whole page.
This is the core mental model you'll build on for the rest of the course: a render is not the same thing as a DOM update. "Rendering" means React calling your component function to figure out what should be on screen; the actual DOM only changes where the result differs from last time.
4. Props vs. State — and One-Way Data Flow
This is the distinction that causes the most confusion in your first week with React,
so it's worth being precise: props are data passed into a
component by its parent — read-only from the component's own point of view.
State is data a component owns and can change itself, via
useState (which you'll go deep on in Week 2).
interface UserCardProps {
name: string; // prop -- the parent decides this, UserCard can't change it
}
function UserCard({ name }: UserCardProps) {
const [expanded, setExpanded] = useState(false); // state -- UserCard owns this
return (
<div>
<p>{name}</p>
<button onClick={() => setExpanded(!expanded)}>
{expanded ? 'Show less' : 'Show more'}
</button>
{expanded && <p>Extra details about {name}...</p>}
</div>
);
}
// The parent controls the prop; UserCard controls its own state
<UserCard name="Ada Lovelace" />
Data flows one way: from parent to child, via props. A child can never directly hand a new value back up into its parent's state — if a child needs to change something the parent owns, the parent passes down a function as a prop, and the child calls it:
function Parent() {
const [selected, setSelected] = useState<string | null>(null);
return <OptionList selected={selected} onSelect={setSelected} />;
}
function OptionList({ selected, onSelect }: {
selected: string | null;
onSelect: (value: string) => void;
}) {
return (
<button onClick={() => onSelect('blue')}>
{selected === 'blue' ? '✓ ' : ''}Blue
</button>
);
}
This pattern — called lifting state up — is how two sibling components end up "sharing" state: the state actually lives in their closest common parent, and both children receive it (and a way to change it) via props.
Lifting state up works fine for a few components. Once you're lifting the same piece of state through five layers just to pass it down, that's the exact pain point Context (Week 10) and global state libraries solve.
5. Composing Components
A React app is a tree of components rendering other components. The children
prop is what lets a component wrap arbitrary content it doesn't need to know the
shape of — the same pattern behind every layout, card, and modal you'll build:
interface CardProps {
title: string;
children: React.ReactNode;
}
function Card({ title, children }: CardProps) {
return (
<div className="card">
<h3>{title}</h3>
<div className="card__body">{children}</div>
</div>
);
}
// Usage -- Card has no idea what's inside it, and doesn't need to
<Card title="Profile">
<UserCard name="Grace Hopper" />
<button>Edit</button>
</Card>
Notice Card is completely reusable — it knows how to render a titled
box, and nothing about what goes inside one. This "wrap arbitrary children" shape
reappears constantly: layout components, modals, list wrappers, and — in Week 11 —
the compound-component pattern built entirely on top of it.
6. Hands-on Exercise
Build a filterable profile list from scratch
Practice the props/state/composition split by building a small, real UI — no external libraries, just what this week covered.
const people = [
{ id: 1, name: 'Ada Lovelace', role: 'Mathematician' },
{ id: 2, name: 'Grace Hopper', role: 'Computer Scientist' },
{ id: 3, name: 'Alan Turing', role: 'Mathematician' },
{ id: 4, name: 'Katherine Johnson', role: 'Physicist' },
];
Requirements:
- Scaffold a new Vite + React + TypeScript project and get it running with
npm run dev. - Build a
PersonCardcomponent that takes a person as props and renders their name and role. - Build a
PersonListcomponent that owns afilterTextpiece of state (viauseState) and a text input bound to it, rendering onePersonCardper person whose name matches the filter (case-insensitive). - Wrap the whole list in a
Cardcomponent (from Section 5) that takes atitleprop and rendersPersonListas itschildren. - Add a "no results" message, rendered conditionally, when the filter matches nobody.
The filter state belongs in PersonList, not PersonCard — PersonCard should stay a "dumb" component that only receives props. If you find yourself wanting state inside PersonCard for this exercise, that's a sign the state lives one level too low.
7. Knowledge Check
Four quick questions. Expand each to check your answer.
Q1
What does a line of JSX like <h1>Hi</h1> actually become before it reaches the browser?
What does a line of JSX like <h1>Hi</h1> actually become before it reaches the browser?
The build tool compiles it into a plain function call (like jsx('h1', { children: 'Hi' })) that returns a JavaScript object describing the element — type, props, and children. It's a description of what should be on screen, not a real DOM node.
Q2
When state changes and a component "re-renders," does React necessarily touch the real DOM?
When state changes and a component "re-renders," does React necessarily touch the real DOM?
Not necessarily, and even when it does, not everywhere. "Re-rendering" means React calls the component function again and gets a new element tree, which it diffs against the previous tree. Only the parts of the real DOM that actually differ get updated — a render and a DOM update are two different things.
Q3
Why can't a child component directly modify a piece of state that lives in its parent?
Why can't a child component directly modify a piece of state that lives in its parent?
React data flow is one-directional: props only travel from parent to child, never back up automatically. A child can only affect a parent's state if the parent explicitly passes down a function as a prop (like onSelect) for the child to call — the parent still owns and controls the actual state update.
Q4
What makes the children prop different from a regular prop like title?
What makes the children prop different from a regular prop like title?
Nothing structurally — it's still just a prop. The difference is convention and syntax: whatever you nest between a component's opening and closing JSX tags gets passed automatically as its children prop, which lets a wrapper component (like Card) render arbitrary, unknown content without needing a dedicated prop for every possible thing you might put inside it.