React Introduction

React is a free, open-source JavaScript library for building user interfaces, created by Facebook (now Meta) and first released in 2013. Instead of manually finding DOM elements and updating them step by step, you describe what the UI should look like for any given state, and React figures out how to update the actual browser DOM to match. This lesson introduces the core ideas — components, JSX, props, state, and the virtual DOM — that every other lesson in this course builds on.

Overview: What React Is and Why It Exists

Before libraries like React, developers updated web pages imperatively: you’d write code that says “find this element, change its text, add this class, remove that child node.” As applications grew, this became hard to reason about — you had to track every possible sequence of DOM mutations by hand, and bugs crept in when the actual DOM state drifted from what you expected it to be.

React introduced a different model: declarative UI. You write a function that takes the current data (props and state) and returns a description of the UI you want — a tree of elements. React takes that description and handles updating the real DOM for you. You never call methods like appendChild or removeChild yourself; you just describe the end result, and React reconciles it.

Components: The Core Building Block

Everything in a React application is built out of components. A component is just a JavaScript function that returns a description of some UI (written in JSX, explained below). Components can be nested inside other components, reused with different data, and composed together the same way you’d compose regular functions. A large application is really just one big tree of components, starting from a single root component (often called App).

The Virtual DOM and Reconciliation

The real browser DOM is slow to update directly and expensive to query repeatedly. React solves this with a virtual DOM: a lightweight JavaScript object tree that mirrors the structure of the real DOM. Every time a component’s data changes, React calls that component’s function again to produce a new virtual DOM tree, then compares it against the previous virtual DOM tree using an algorithm called reconciliation (often referred to as “diffing”). React calculates the minimal set of real DOM changes needed to make the browser match the new virtual tree, and only applies those changes in a step called the commit phase. This is why React apps can update the screen efficiently even when data changes frequently: React never re-creates the whole page, it patches only what actually changed.

In short, the cycle is: render (call component functions to build a new virtual tree) → reconcile (diff the new tree against the old one) → commit (apply the minimal real DOM updates). You will see this render-reconcile-commit cycle referenced throughout this course, especially in the lessons on state and the render cycle.

Props, State, and Re-renders

Components receive input data through props (short for properties) — read-only values passed down from a parent component, similar to function arguments. Components can also hold their own internal, changeable data using state, managed with the useState hook. Whenever state changes (via its setter function) or a parent passes new props, React schedules a re-render of that component (and its children), running the render-reconcile-commit cycle again. This is the fundamental reason React apps update: not because you “tell the DOM to change,” but because you change data, and React reacts to that change by re-rendering.

Syntax

A minimal React component looks like this:

function ComponentName(props) {
  return <div>Some JSX here</div>;
}
Part Meaning
function ComponentName A component is a normal JavaScript function. Its name must start with an uppercase letter so React can distinguish it from a plain HTML tag.
props A single object argument holding whatever data the parent passed in, e.g. <ComponentName name="Ava" /> passes { name: "Ava" }.
return <div>...</div> JSX — a syntax extension that looks like HTML but compiles to JavaScript function calls (React.createElement(...) under the hood). It must return exactly one root element, or a fragment (<>...</>) wrapping multiple elements.
<ComponentName /> How you render/use the component elsewhere, just like an HTML tag but capitalized.

To actually mount a React app into a web page, you use the React 18+ root API:

import { createRoot } from "react-dom/client";

createRoot(document.getElementById("root")).render(<App />);

This finds a DOM element with id="root" (usually a single empty <div> in your HTML file) and tells React to take ownership of everything inside it, rendering your App component tree there.

Examples

Example 1: A Basic Component

import { createRoot } from "react-dom/client";

function App() {
  return <h1>Hello, React!</h1>;
}

createRoot(document.getElementById("root")).render(<App />);

Renders: a page showing a single heading that reads “Hello, React!”

This is the smallest possible React app. App is a function component that returns one JSX element, an <h1>. createRoot attaches React to the DOM node with id="root" and renders <App /> into it. Nothing here changes over time yet — it’s a static render.

Example 2: Passing Data with Props

function Greeting({ name }) {
  return <p>Welcome, {name}!</p>;
}

function App() {
  return (
    <div>
      <Greeting name="Ava" />
      <Greeting name="Liam" />
    </div>
  );
}

Renders: two paragraphs — “Welcome, Ava!” and “Welcome, Liam!”

Greeting is reused twice with different name props, demonstrating why components matter: you write the UI logic once and reuse it with different data. Note the destructuring { name } directly in the function parameter — this pulls the name field out of the props object. Inside JSX, curly braces {name} let you embed any JavaScript expression as text.

Example 3: Adding Interactivity with State

import { useState } from "react";

function Counter() {
  const [count, setCount] = useState(0);

  function handleClick() {
    setCount(count + 1);
  }

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={handleClick}>Increment</button>
    </div>
  );
}

export default Counter;

Renders: a paragraph reading “Count: 0” and a button labeled “Increment”. Clicking the button updates the text to “Count: 1”, then “Count: 2”, and so on, with no page reload.

This is the first taste of what makes React powerful: the useState hook gives the component a piece of state (count) and a function to update it (setCount). Calling setCount doesn’t mutate count directly — it tells React “here is the new value; please re-render this component.” React then re-runs Counter, gets a new JSX tree with the updated number, diffs it against the previous tree, and patches only the text node that changed. You’ll cover useState in full depth in the dedicated State lesson — this example just shows the shape of the pattern.

How It Works Step by Step (Under the Hood)

  • 1. Initial mount: createRoot(...).render(<App />) tells React to call the App function, which returns JSX. That JSX is really syntactic sugar for calls to a function that builds plain JavaScript objects describing elements — this object tree is the virtual DOM.
  • 2. First commit: Since there’s no previous tree to compare against, React converts the entire virtual DOM tree into real DOM nodes and inserts them into the page inside your root element.
  • 3. State change: When a setter function like setCount is called, React schedules a re-render of that component (and its descendants).
  • 4. Render phase: React calls the component function again with the new state value, producing a new virtual DOM tree.
  • 5. Reconciliation: React compares (diffs) the new virtual tree against the previous one, element by element, to figure out the minimal set of differences.
  • 6. Commit phase: React applies only those specific changes to the real DOM — for example, updating one text node’s content — rather than re-creating the whole subtree.
  • 7. Unmount: If a component is removed from the tree entirely (e.g., conditionally not rendered anymore), React removes its DOM nodes and, where applicable, runs any cleanup registered in effects (covered in the useEffect lesson).

Common Mistakes

Mistake 1: Returning Multiple Root Elements Without Wrapping

function App() {
  return (
    <h1>Title</h1>
    <p>Description</p>
  );
}

This fails because JSX requires a component to return exactly one root element. Two sibling elements with nothing wrapping them is invalid syntax. Fix it with a fragment (<>...</>) or a wrapping element like <div>:

function App() {
  return (
    <>
      <h1>Title</h1>
      <p>Description</p>
    </>
  );
}

Mistake 2: Using HTML Attribute Names Instead of React’s

function App() {
  return <div class="container">Content</div>;
}

JSX is not HTML — it’s JavaScript, and class is a reserved word in JavaScript. React requires className instead (and htmlFor instead of for on labels). Using class won’t crash the app, but it silently fails to apply the styling and React will warn about it in the console. The correct version:

function App() {
  return <div className="container">Content</div>;
}

A closely related mistake beginners make is mutating state directly instead of calling its setter (for example, pushing into a state array with .push() instead of calling setItems([...items, newItem])). Direct mutation doesn’t trigger a re-render because React only knows to update the screen when you call the state setter function — you’ll see this covered in detail once you reach the State and Updating State lessons.

Best Practices

  • Name every component function starting with an uppercase letter (App, Greeting) so React and JSX can tell components apart from ordinary HTML tags.
  • Keep components small and focused on one responsibility; compose larger UIs out of smaller components rather than writing one giant function.
  • Always use className, htmlFor, and camelCase event props (onClick, onChange) in JSX — never the raw HTML attribute names.
  • Treat props as read-only. A component should never modify the props object it receives; if it needs to change data, that data should live in its own state or be lifted to a parent.
  • Use the React 18+ createRoot API to mount your app, not the legacy ReactDOM.render.
  • Wrap multiple sibling elements in a fragment (<>...</>) instead of adding unnecessary extra <div>s just to satisfy the single-root-element rule.

Practice Exercises

  • Exercise 1: Create a component called Profile that accepts name and role props and renders them inside a paragraph, like “Ava is a Developer”. Render two <Profile /> instances with different props inside an App component.
  • Exercise 2: Starting from the Counter example in this lesson, add a second button labeled “Decrement” that decreases count by 1 when clicked. Think about what new function you need and how it should call setCount.
  • Exercise 3: Find and fix the bug in this snippet, then explain in one sentence why it was broken: function App() { return <h1>Hi</h1> <p>Bye</p> }

Summary

  • React is a JavaScript library for building declarative, component-based user interfaces.
  • Components are JavaScript functions that return JSX describing the UI for the current data.
  • React uses a virtual DOM and a render → reconcile → commit cycle to update only what changed in the real DOM, instead of rebuilding the whole page.
  • Props pass read-only data into a component; state (via useState) holds data a component can change itself.
  • Changing state (via its setter function) is what triggers a re-render — direct mutation does not.
  • Modern apps mount with createRoot(...).render(<App />) from react-dom/client.
  • JSX has its own rules: one root element per component, className instead of class, and camelCase event handlers like onClick.