React Handbook
React Handbook
This book aims to introduce you to React, the most popular JavaScript/TypeScript UI library.
It’s a gentle introduction to the basic concepts of React, and assumes no prior React
knowledge.
You’ll learn the basic concepts in a very straightforward way, without too much jargon or
complicated explanations, using an 80/20 approach that give you the mental models and
knowledge to start using React for your projects.
Examples are written using TypeScript. If you need, check out my JavaScript Handbook, and
the TypeScript Handbook.
The book is up to date with React 19 and the latest best practices for client-side React.
Legal
Flavio Copes, 2024. All rights reserved.
No part of this book may be reproduced, distributed, or transmitted in any form or by any
means, including photocopying, recording, or other electronic or mechanical methods,
without the prior written permission of the publisher.
The information in this book is for educational and informational purposes only and is not
intended as legal, financial, or other professional advice. The author and publisher make no
representations as to the accuracy, completeness, suitability, or validity of any information in
this book and will not be liable for any errors, omissions, or delays in this information or any
losses, injuries, or damages arising from its use.
This book is provided free of charge to the newsletter subscribers of Flavio Copes. It is for
personal use only. Redistribution, resale, or any commercial use of this book or any portion
of it is strictly prohibited without the prior written permission of the author.
If you wish to share a portion of this book, please provide proper attribution by crediting
Flavio Copes and including a link to flaviocopes.com.
Introduction to React
The goal of this handbook is to provide a starter guide to learning React.
React is a JavaScript library that aims to simplify the development of highly interactive
interfaces.
Developed at Facebook and released to the world in 2013, it drives some of the most widely
used apps, powering Facebook and Instagram among countless other applications.
Its primary goal is to make it easy to reason about an interface and its state at any point in
time, by dividing the UI into a collection of components.
You might find some initial difficulties learning React, depending on your existing experience.
It’s normal. Once it "clicks", I guarantee it's going to be one of the best experiences you will
have, because React makes many things easier than ever, and its ecosystem is filled with
great libraries and tools.
You don't have to be an expert, but I think you need a good overview of:
Variables
Functions, Arrow functions
Objects and arrays
Work with objects and arrays using rest and spread
Object and array destructuring
Template literals
Callbacks
ES Modules
Those are all good reasons, but one of the reasons I want you to learn React is that it's
powerful.
It allows you to build UIs in ways that you can’t do with plain DOM APIs.
Of course, similar libraries exist, like Vue or Angular or Svelte, but in this book we’ll focus on
React only.
Vite is a modern tool that provides a development server, is very fast, and many people in
the JS community consider it optimal.
💁♂️ Note: Vite can be used as a replacement for `create-react-app`, another tool that’s
popular but also slower. You can use that instead if you prefer, but I found Vite just great and
it can also be used with other libraries, not just React.
To create a project using Vite you first go into the folder where you host all your projects, in
my case a dev folder in my user’s home folder.
Then run npm create vite@latest (you don’t have to install anything, it’s all automatic
thanks to npm create ):
Choose a name for the project. That will also be the project’s folder name. In this case “test”:
Done!
Now go to the newly created project folder:
cd test
and run
npm install
As you can see, Vite created a basic application with some placeholder files:
React Components
You just saw how to create your first React application, using Vite.
function App() {
return /* something */
}
App is a function.
Inside the function we can do something, typically we fetch data, or we inspect the current
state of the application.
Then, we can return something from a function, and that will be rendered by React on the
page.
Like this:
function App() {
return <p>test</p>
}
Basically when the component is rendered, in this case in the browser we’ll see the HTML
code <p>test</p> .
In addition to defining some JSX to return, a component has several other characteristics.
First, it must have a name that starts with a capital letter, like App in our case.
A component can have its own state, which means it encapsulates some variables that
other components can't access unless this component exposes this state to the rest of the
application.
A component can also receive data from other components. In this case, we talk about
props.
Don't worry, we're going to look in detail at all those terms (JSX, State, and Props) right now.
JSX
We can't talk about React without first explaining JSX (technically, TSX as we use
TypeScript).
Let’s look at a real component. You have it in the app you created, in src/App.tsx :
function App() {
const [count, setCount] = useState(0)
return (
<>
<div>
<a href="https://vite.dev" target="_blank">
<img src={viteLogo} className="logo" alt="Vite logo" />
</a>
<a href="https://react.dev" target="_blank">
<img src={reactLogo} className="logo react" alt="React logo"
/>
</a>
</div>
<h1>Vite + React</h1>
<div className="card">
<button onClick={() => setCount((count) => count + 1)}>
count is {count}
</button>
<p>
Edit <code>src/App.tsx</code> and save to test HMR
</p>
</div>
<p className="read-the-docs">
Click on the Vite and React logos to learn more
</p>
</>
)
}
Feel free to ignore the imports, and that weird useState() function call. Let’s look at what
is returned from the component.
<>
<div>
<a href="https://vite.dev" target="_blank">
<img src={viteLogo} className="logo" alt="Vite logo" />
</a>
<a href="https://react.dev" target="_blank">
<img src={reactLogo} className="logo react" alt="React logo" />
</a>
</div>
<h1>Vite + React</h1>
<div className="card">
<button onClick={() => setCount((count) => count + 1)}>
count is {count}
</button>
<p>
Edit <code>src/App.tsx</code> and save to test HMR
</p>
</div>
<p className="read-the-docs">
Click on the Vite and React logos to learn more
</p>
</>
This looks like HTML, but it's not really HTML. It's a little different.
We have className instead of class (plot twist, it’s because class is a reserved word in
JavaScript and can’t be used in code).
We have an empty tag <></> wrapping everything, too. That is basically an empty HTML
tag, if you want, and it’s needed because we can only return a single tag from a component.
Under the hood, React will process the JSX and will transform it into JavaScript that the
browser will be able to interpret.
So we're writing JSX, but in the end, there's a translation step that makes it digestible to a
JavaScript interpreter.
React gives us this interface for one reason: JSX is a fantastic way to compose interfaces.
In the next section, we'll talk about how JSX lets you easily compose a UI, then we'll look at
the differences with "normal HTML" that you need to know.
In particular, in a React component, you can import other React components, you can
embed them, and display them.
A React component is usually created in its own file because that's how we can easily reuse
it (by importing it) in other components.
But a React component can also be created in the same file of another component if you
plan to only use it in that component. There's no "rule" here, you can do what feels best to
you.
I generally use separate files when the number of lines in a file grows too much.
To keep things simple let's create a component in the same App.tsx file.
function WelcomeMessage() {
return <h1>Welcome!</h1>
}
We define a component as a function that returns some JSX, with a capitalized name
See? WelcomeMessage is a simple function that returns a line of JSX that represents an h1
HTML element.
NOTE: as your app grows you typically put each component in its own file, and import it.
But for simple cases, like this, we can define multiple components in a single file.
We can delete all we have in that file, and we can write this code that adds
<WelcomeMessage /> to the App component to show that component in the user interface:
import './App.css'
function WelcomeMessage() {
return <h1>Welcome!</h1>
}
function App() {
return (
<div className='App'>
<WelcomeMessage />
</div>
)
}
We say WelcomeMessage is a child component of App, and App is its parent component.
We're including the <WelcomeMessage /> component as a tag, as if it was part of the HTML
language, like <p> or <input> .
That's the beauty of React components and JSX: we can compose an application interface
and use it like we're writing HTML.
In this section, I want to introduce you to some of the most important things you need to
keep in mind when using JSX.
One of the differences might be quite obvious if you looked at the App component JSX:
there's a strange attribute called className .
In HTML we use the class attribute. It's probably the most widely used attribute, for various
reasons. One of those reasons is CSS. The class attribute allows us to style HTML
elements easily, and CSS frameworks like Tailwind put this attribute at the center of the CSS
user interface design process.
But there's a problem. We are writing this UI code in a JavaScript file, and class in the
JavaScript programming language is a reserved word. This means we can't use this
reserved word as we want. It serves a specific purpose (defining JavaScript classes) and the
React creators had to choose a different name for it.
You need to remember this especially when you're copying/pasting some existing HTML.
React will try its best to make sure things don't break, but it will raise a lot of warnings in the
Developer Tools.
This is not the only HTML feature that suffers from this problem, but it's the most common
one.
Another big difference between JSX and HTML is that HTML is very relaxed, we can say.
Even if you have an error in the syntax, or you close the wrong tag, or you have a mismatch,
the browser will try its best to interpret the HTML without breaking.
It's one of the core features of the Web. It is very forgiving. But JSX is not forgiving. If you
forget to close a tag in JSX, you will have a clear error message.
React usually gives very good and informative error messages that point you in the right
direction to fix the problem.
Another big difference between JSX and HTML is that in JSX we can embed JavaScript.
React lets us use JavaScript in the JSX, by wrapping it into curly brackets.
The first example of this that I will show you comes directly from the App component we
studied so far.
and then in the JSX, we assign this SVG file to the src attribute of an img tag:
Let's do another example. Suppose the App component has a variable called message .
We can print this value in the JSX by adding {message} anywhere in the JSX.
import './App.css'
function App() {
const message = 'Hello!'
return (
<div className='App'>
<h1>{message}</h1>
</div>
)
}
Try it! You should see the Hello! message in the browser. We successfully embedded a
JavaScript variable in the output of the component (the JSX):
Inside the curly brackets { } we can add any JavaScript statement.
For example, this is a common statement you will find in JSX. We have a ternary operator
where we define a condition ( message === 'Hello!' ), and we print one value if the
condition is true, or another value (the content of message in this case) if the condition is
false:
{
message === 'Hello!' ?
'The message was "Hello!"' : message
}
Like this:
import './App.css'
function App() {
const message = 'Hello!'
return (
<div className='App'>
<h1>{message === 'Hello!' ? 'The message was "Hello!"' : message}
</h1>
</div>
)
}
If you change the content of the message variable, JSX will print something else:
import './App.css'
function App() {
const message = 'Test'
return (
<div className='App'>
<h1>{message === 'Hello!' ? 'The message was "Hello!"' : message}
</h1>
</div>
)
}
return (
<ul></ul>
)
Inside this list, we add a JavaScript snippet using curly brackets {} and inside it we
call items.map() to iterate over the items.
We pass to the map() method a callback function that is called for every item of the list.
Inside this function we return a <li> (list item) with the value contained in the array, and
with a key prop that is set to the index of the item in the array. This is needed by React.
return (
<ul>
{items.map((value, index) => {
return <li key={index}>{value}</li>
})}
</ul>
)
You can also use the shorthand form with implicit return:
return (
<ul>
{items.map((value, index) =>
<li key={index}>{value}</li>
)}
</ul>
)
Let's talk about click events, which are pretty simple to digest.
NOTE: if you’re new to DOM events, check out my Browser APIs Handbook.
In the React component’s JSX you can use the onClick attribute on any JSX element:
<button
onClick={(event) => {
/* handle the event */
console.log('click', event)
}}
>
Click here
</button>
When the element is clicked, the function passed to the onClick attribute is fired.
You can also define this function outside of the JSX, which is handy if the function code is
long:
function App() {
return <button onClick={handleClickEvent}>Click here</button>
}
When the click event is fired on the button, React calls the event handler function.
React supports a vast amount of types of events, like onKeyUp , onFocus , onChange ,
onMouseDown , onSubmit , and many more.
What do we mean by state? The state is the set of data that is managed by the
component.
For example if we have a table of addresses we need to display, one state variable could be
an array containing all the rows in our table.
Each individual input element of the form is responsible for managing its state: what is
written inside it.
A button is responsible for knowing if it's being clicked, or not. If it's on focus.
In React, or in any other components-based framework/library, all our applications are based
and make heavy use of components state.
The simplest way manage state is by using the useState utility provided by React.
Calling useState() , you will get back: a new state variable, and a function that we can
call to alter its value.
useState() accepts the initial value of the state item and returns an array containing the
state variable, and the function you call to alter the state.
useState() returns an array. The above construct uses a special syntax called array
destructuring which we’ll use all the time to extract from the array the first value in the
count variable, and the second value in the setCount variable.
This is important: we can't just alter the value of a state variable directly, doing count++ or
count = count + 1 . ****
Otherwise, the React component will not update its UI to reflect the changes in the data.
Calling the modifier is the way we can tell React that the component state has changed.
The syntax is a bit weird, right? Since useState() returns an array we use array
destructuring to access each individual item, like this: const [count, setCount] =
useState(0)
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
</div>
)
}
You can add as many useState() calls as you want, to create as many state variables as
you want, which can hold any value, not just numbers (also objects and arrays are valid):
function WelcomeMessage() {
return <p>Welcome!</p>
}
<WelcomeMessage />
This component does not have any initial value passed to it, because it does not have props.
function WelcomeMessage(props) {
return <p>Welcome!</p>
}
myprop is one of the props contained in the props object, like this: { myprop: 'test'
} . Using this syntax we only extract this prop from the props object.
Now that we have the prop, we can use it inside the component, for example, we can print
its value in the JSX:
Curly brackets here have various meanings. In the case of the function argument, curly
brackets are used as part of the object destructuring syntax.
Then we use them to define the function code block, and finally in the JSX to print the
JavaScript value.
Passing props to components is a great way to pass values around in your application.
A component either holds data (has state) or receives data through its props.
We can also send functions as props, so a child component can call a function in the parent
component.
A special kind of prop is automatically available inside the component with the name
children .
That variable contains the value of anything that is passed between the opening and closing
tags of the component, for example:
In this case, inside WelcomeMessage we could access the value Here is some message
by using the children prop:
If you pass a function to the child component, like we do below with an imaginary
setCount() function, you can also change a state variable defined in the parent
component from a child component:
This is helpful when the parent component is “in charge” of the state variable.
Inside the Counter component, we can now call the setCount prop and call it to update the
count state in the parent component when something happens:
function App() {
const [count, setCount] = useState(0)
return (
<div>
<p>You clicked {count} times</p>
<Counter setCount={setCount} count={count} />
</div>
)
}
There are more advanced ways to manage data, which include the built-in Context API and
libraries like Zustand, MobX or Redux.
You should learn one of them too, later, for more complex scenarios, but those introduce
more complexity, and many times using the useState hook is a good solution.
The useEffect hook allows components to have access to the lifecycle events of a
component and do something when the component is first rendered as it’s mounted in the
DOM, and when it’s re-rendered.
Note that many tutorials use this hook to load data needed by the component. For simple
use cases it’s fine, but in many cases you’ll want to use a state management library, like
TanStack Query, SWR, or others.
React first updates the DOM, then calls any function passed to useEffect() .
Here is an example:
useEffect(() => {
console.log(`You clicked ${count} times`)
})
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
</div>
)
}
We can tell React to skip a re-render, for performance purposes, by adding a second
parameter, an array that contains a list of state variables to watch for.
React will only re-run the function if one of the items in this array changes:
useEffect(() => {
console.log(`Hi ${name} you clicked ${count} times`)
}, [name, count])
Using this syntax you can tell React to only execute the function when the component is first
loaded by passing an empty array:
useEffect(() => {
console.log(`Component mounted`)
}, [])
NOTE: In development mode (not in production mode) the function is called 2 times
when the component is first rendered, to help you solve possible bugs.
If during the effect you perform an action like opening a network connection for example,
you’ll need to close that connection when the component unmounts. To do that, you return a
function from the function passed to useEffect() :
useEffect(() => {
console.log(`connect...`)
return () => {
console.log('cleanup connection')
}
}, [])
Available for both Chrome and Firefox, the React Developer Tools are an essential
instrument you can use to inspect a React application.
They provide an inspector that reveals the React components tree that builds your page,
and for each component, you can go and check the props, the state, hooks, and lots more.
Once you have installed the React Developer Tools, you can open the regular browser dev
tools (in Chrome, right-click on the page, then click Inspect ) and you'll find 2 new panels:
Components and Profiler.
Once you’re in the Components tab, you’ll see the application components list:
Here’s an example of a spreadsheet implemented in React. You can see the Spreadsheet
component, and inside it a series of Cell components, each responsible for a single cell of
the spreadsheet:
Through the devtools you will be able to inspect the component's data:
If you move the mouse over the components, you'll see that on the page, the browser will
select the parts that are rendered by that component.
If you select any component in the tree, the right panel will show you a reference to the
parent component, and the props passed to it.
You can click the eye icon in the Developer Tools toolbar to inspect the DOM element, and
also if you use the first icon, the one with the mouse icon (which conveniently sits under the
similar regular DevTools icon), you can hover an element in the browser UI to directly select
the React component that renders it.
You can use the bug-shaped 🐞 icon at the top right of the panel to log component data to
the console.
Those are the basics.
This is a super powerful tool that will help you debug your React applications going forward.
Search
Contact forms
Shopping carts checkout
Login and registration
and more!
Using React we can make our forms much more interactive and less static.
In the simplest form (for complex needs you can use form libraries made for React like react-
hook-form or TanStack Form, but in this book we focus on “core React”), we build a form by
creating JSX elements.
The difference with React is how we manage the value entered in the form: we associate it
to a state variable defined with useState , and once it’s time to send the form data to the
server, we use this state variable instead of looking up the value from the DOM.
return (
<form>
Username:
<input
type='text'
value={username}
/>
</form>
)
}
When an element state changes in a form field managed by a component, we track it using
the onChange attribute.
return (
<form>
Username:
<input
type='text'
value={username}
**onChange={(event) => {
setUsername(event.target.value)
}}**
/>
</form>
)
}
We use the onSubmit attribute on the form to call the handleSubmit method when the
form is submitted:
}**
return (
<form **onSubmit={handleSubmit}**>
Username:
<input
type='text'
value={username}
onChange={(event) => {
setUsername(event.target.value)
}}
/>
</form>
)
}
event.preventDefault() in the form submit handler is needed so the browser does not
perform the default form submit behavior which is to do a GET request to the same URL the
page is on (ultimately this means just reloading the page).
Validation in a form can be handled in the onChange event handler, where you have access
to the old value of the state and the new one. You can check the new value and if not valid
reject the updated value (and communicate it in some way to the user). And you can also do
validation in the onSubmit event handler.
For example, you can check the fields were filled with data you interpret to be valid.
Once you’re ready to send the data, you can use a fetch request to a POST API endpoint:
return (
<form onSubmit={handleSubmit}>
Username:
<input
type='text'
value={username}
onChange={(event) => {
setUsername(event.target.value)
}}
/>
</form>
)
}
Start building some simple React applications. For example, build a simple counter or
interact with a public API.
Learn more theory about the Virtual DOM, writing declarative code, unidirectional data flow,
immutability, and composition.
Learn how to manage state using the Context API, useContext, Zustand.
Learn an application framework built on top of React, like Next.js or TanStack Start.
The most important part: just use React in your projects, and learn along the way.