React Mastery
0%A complete, self-contained interactive learning module. Master modern React with guided concepts, mental models, and hands-on mini projects.
What is React?
Definition
React is a declarative, component-based JavaScript library for building user interfaces. It abstracts away direct DOM manipulation, allowing engineers to build complex, scalable applications by composing encapsulated UI components.
Real-Life Problem vs Solution
Direct DOM manipulation (imperative programming) forces the browser to expensively recalculate layouts and repaint the entire tree for even minor state changes, causing performance bottlenecks.
Full Layout Recalculation
React introduces a declarative approach using an in-memory Virtual DOM. State changes trigger an efficient diffing algorithm that patches only the precise DOM nodes that mutated, optimizing the render cycle.
Targeted DOM Patch
Why This Exists
Before React, developers manually updated the browser DOM (Document Object Model) using vanilla JavaScript or jQuery. This was extremely slow and led to messy, unmaintainable 'spaghetti code' as applications grew. React was invented to solve this scaling problem.
How Companies Use It
Facebook created React to manage their increasingly complex UI, specifically the chat feature which kept breaking. Today, companies like Netflix, Airbnb, and Uber use React to deliver lightning-fast, app-like experiences on the web.
Common Mistakes
A common beginner mistake is trying to manipulate the DOM directly (e.g., using document.getElementById) instead of letting React handle it. In React, you update the data (state), and React updates the DOM.
Performance & Security
React's Virtual DOM naturally protects against basic Cross-Site Scripting (XSS) attacks by automatically escaping strings before rendering them. However, rendering large lists without optimization can still cause performance drops.
How it works
Under the hood, React 16+ uses the 'Fiber' architecture. It splits rendering into two phases: the 'Render Phase' (asynchronous, interruptible generation of the Virtual DOM tree) and the 'Commit Phase' (synchronous, blocking mutation of the actual DOM via the Reconciliation algorithm).
Code Example
// A basic React Component
function WelcomeMessage() {
// We return what looks like HTML, but it's actually JSX
return (
<div className="p-4 border rounded shadow">
<h1 className="font-bold text-xl">Hello, World!</h1>
<p className="text-gray-600">Welcome to React.</p>
</div>
);
}Interview Questions
8 questions to test your knowledge
Summary
React is a UI library that uses a declarative, component-based approach and a Virtual DOM to build fast, scalable web applications.
Up Next
Now that you know what React is, let's learn how to set up a modern React project on your local machine.
Setting up a React project
Definition
To build a modern React app, you need a build tool. Tools like Vite or Next.js configure everything you need (bundling, hot-reloading, server setup) out of the box. Think of it like buying a fully-furnished house instead of buying a plot of land and having to install the plumbing and electricity yourself.
Why This Exists
Setting up a modern frontend app requires bundling JavaScript, compiling JSX, managing CSS, and spinning up a local server. Doing this from scratch with Webpack is notoriously difficult. Build tools like Vite automate all of this instantly.
How Companies Use It
Enterprise teams use Vite or Next.js to start their projects. For example, a startup building a SaaS dashboard will use Vite for lightning-fast development, ensuring engineers don't waste hours waiting for the app to compile after every save.
Common Mistakes
Using outdated tools like Create React App (CRA). CRA is officially deprecated and extremely slow. Always use Vite, Next.js, or Remix for new projects in 2026.
Performance & Security
Never commit your node_modules folder or .env files to version control. Always ensure your package.json dependencies are regularly audited for security vulnerabilities using 'npm audit'.
How it works
When you run a build tool like Vite, it starts a local development server. It intercepts your request for the web page, compiles your modern React JSX code into standard JavaScript that the browser can understand on-the-fly, and instantly injects updates into the browser without refreshing whenever you save a file (Hot Module Replacement).
Code Example
// Standard project structure
// src/
// ├─ main.jsx (Entry point, connects React to the DOM)
// ├─ App.jsx (Your main root component)
// └─ index.css (Global styles)
// Inside main.jsx:
import { createRoot } from 'react-dom/client';
import App from './App.jsx';
// Mount the App component inside the HTML element with id="root"
createRoot(document.getElementById('root')).render(<App />);Interview Questions
8 questions to test your knowledge
Summary
Vite is the modern standard for bootstrapping React applications, providing instant server starts and lightning-fast Hot Module Replacement.
Up Next
You've got your app running. Next, let's learn how to write UI inside your JavaScript using JSX.
JSX Syntax & Rules
Definition
JSX (JavaScript XML) is a syntax extension for JavaScript heavily utilized by React. It provides syntactic sugar for React.createElement() calls, allowing developers to structure component logic and UI markup within the same file.
Real-Life Problem vs Solution
Without a transpilation layer, developers must manually construct the DOM hierarchy using verbose native DOM APIs or cumbersome React.createElement() chains.
Native DOM APIs
const d = document;
const el = d.createElement('div');
el.className = 'box';
const txt = d.createTextNode('Hi');
el.appendChild(txt);
d.body.appendChild(el);JSX provides an elegant, HTML-like declarative syntax that seamlessly embeds standard JavaScript expressions, greatly enhancing code readability and maintainability.
Declarative JSX Syntax
// Declarative UI
return (
<div className="box">
Hi
</div>
);Why This Exists
Writing UI in raw JavaScript (`React.createElement('div', null, 'Hello')`) is tedious and hard to read. JSX was invented to let engineers write HTML-like syntax directly inside JavaScript files, making UI development intuitive and visual.
How Companies Use It
Virtually every React codebase at companies like X (Twitter), Meta, and Amazon uses JSX. It is the industry standard. Designers and frontend developers can easily read JSX because it closely mirrors traditional HTML structures.
Common Mistakes
Forgetting to wrap adjacent JSX elements in a single parent or Fragment (`<>...</>`). Also, trying to use HTML attributes like 'class' instead of the required 'className'.
Performance & Security
JSX inherently prevents injection attacks (XSS) by evaluating and escaping all dynamic data `{...}` before rendering it. However, if you explicitly use `dangerouslySetInnerHTML`, you bypass this protection and must manually sanitize the input.
How it works
JSX is not valid JavaScript. During the build process, bundlers like Webpack or Vite use transpilers (e.g., Babel, SWC) to parse the JSX into an Abstract Syntax Tree (AST) and compile it down to standard React._jsx() or React.createElement() object instantiations.
Code Example
function JsxExample() {
const name = "Alice";
const isLoggedIn = true;
// Returning a single wrapper element (a fragment <> can also be used)
return (
<div className="user-profile">
{/* We can inject JavaScript variables using curly braces {} */}
<h2>Welcome back, {name}!</h2>
{/* We use camelCase for attributes (className instead of class) */}
<img src="/avatar.png" alt="Profile" tabIndex="0" />
</div>
);
}Interview Questions
8 questions to test your knowledge
Summary
JSX is a syntax extension for JavaScript that looks like HTML. It makes writing React components significantly easier and more readable.
Up Next
We know how to write JSX. Let's learn how to organize this JSX into reusable, self-contained building blocks called Components.
Components
Definition
Components are the building blocks of React. A component is simply a JavaScript function that returns some UI (JSX). By creating components, you can split your UI into independent, reusable pieces. Think of them like custom HTML tags you invent yourself.
Why This Exists
Without components, web apps are just massive, unmanageable HTML files. Components let you break the UI into independent, reusable pieces (like Lego bricks), making development faster and maintenance easier.
How Companies Use It
On Netflix, the 'Movie Card', the 'Navigation Bar', and the 'Play Button' are all separate components. Engineers can work on the 'Play Button' without breaking the 'Movie Card'.
Common Mistakes
Creating massive 'God Components' that render everything in a single file instead of breaking them down. Also, forgetting that component names MUST start with a capital letter.
Performance & Security
Breaking down UI into smaller components makes it easier to optimize later using tools like React.memo, ensuring only the specific parts of the screen that changed are re-rendered.
How it works
When React sees a custom component tag (like <Button />), it calls that function to see what UI it should render. React builds a 'Component Tree' (like a family tree) starting from the root <App /> down to the smallest components. It uses this tree to track what needs to update.
Code Example
// 1. Define a small, reusable component
function SubmitButton() {
return <button className="bg-blue-500 text-white p-2">Submit</button>;
}
// 2. Use it inside a larger component
function ContactForm() {
return (
<form>
<input type="text" placeholder="Your name" />
{/* We use our custom component just like an HTML tag! */}
<SubmitButton />
</form>
);
}Interview Questions
7 questions to test your knowledge
Summary
Components are independent, reusable pieces of UI. They are the fundamental building blocks of any React application.
Up Next
Components are great, but they are static. Next, let's learn how to pass dynamic data into them using Props.
Props
Definition
Props (short for 'properties') are how components talk to each other. They allow you to pass data from a parent component down to a child component. Think of props like arguments you pass to a standard JavaScript function to change its behavior.
Why This Exists
If you build a highly styled 'Button' component, you don't want it to always say 'Click Me'. Props allow you to pass custom data (like text or colors) into a component from the outside, making it reusable across the app.
How Companies Use It
Amazon has millions of products, but they only have one 'ProductCard' component. They just pass different `props` (image, price, title) into that one component millions of times.
Common Mistakes
Trying to modify props inside the child component. Props are strictly read-only (immutable). To change data, you must use State.
Performance & Security
Passing too many props deeply through multiple components is an anti-pattern called 'Prop Drilling'. While not a direct security issue, it severely degrades maintainability.
How it works
Props are strictly read-only (immutable). A child component cannot modify the props it receives from its parent; data flows one-way (top-down). If the parent changes the prop data, React automatically re-calls the child component function with the new data, triggering a re-render.
Code Example
// The child component accepts 'props' (an object)
function Greeting({ name, color }) {
return <h1 style={{ color: color }}>Hello, {name}!</h1>;
}
// The parent passes data using attributes
function App() {
return (
<div>
<Greeting name="Alice" color="blue" />
<Greeting name="Bob" color="green" />
</div>
);
}Interview Questions
8 questions to test your knowledge
Summary
Props (properties) are read-only arguments passed from a parent component to a child component to customize its rendering.
Up Next
Props are read-only. What if the user clicks a button and we need data to change? That's where State comes in.
useState Internal Mechanics
Definition
The `useState` hook provides function components with persistent local state. It returns a stateful value and an updater function that schedules a re-render of the component when invoked.
Real-Life Problem vs Solution
Legacy vanilla JS applications often require manual DOM querying and full-page repaints to synchronize UI data with underlying application state.
Imperative Render
React abstracts state synchronization. Updating a state variable automatically triggers the Fiber reconciliation engine to patch only the specific DOM nodes dependent on that state.
Fiber State Queue
Why This Exists
Regular JavaScript variables don't trigger a visual update when they change. React needs a way to 'remember' data between renders and know exactly when to redraw the screen. `useState` provides both.
How Companies Use It
When you type into the Google Search bar, every keystroke updates the `useState` value, which triggers the UI to immediately show search suggestions below.
Common Mistakes
Mutating state directly (e.g., `count = 5`). This bypasses React's diffing engine, so the screen won't update. Always use the setter function (e.g., `setCount(5)`).
Performance & Security
State updates are batched asynchronously for performance. If your new state depends on the old state, always use a functional updater (e.g., `setCount(prev => prev + 1)`) to avoid race conditions.
How it works
Internally, React manages hooks via a linked list attached to the component's Fiber node (`memoizedState`). When a state updater is called, React pushes an update object onto the Fiber's update queue and schedules a new render pass, batching multiple updates for performance optimization.
Code Example
import { useState } from 'react';
function Counter() {
// useState returns an array: [currentValue, updaterFunction]
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
{/* We update state by calling the updater function */}
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}Interview Questions
8 questions to test your knowledge
Summary
useState is a Hook that lets you add React state to function components, enabling them to 'remember' things and re-render when data changes.
Up Next
Now we can store changing data. Let's learn how to let the user trigger those changes using Events.
Event handling in React
Definition
Handling events in React (like clicks, typing in an input, or hovering) is very similar to standard HTML, but with a few tweaks: you use camelCase (e.g., `onClick` instead of `onclick`), and you pass a JavaScript function as the event handler rather than a string.
Why This Exists
A static UI is useless. We need a way to capture user interactions like clicks, typing, and hovering. React standardizes event handling across all browsers, so you don't have to worry about browser-specific quirks like you do in vanilla JS.
How Companies Use It
When you click 'Like' on an Instagram post, an `onClick` event handler triggers, updates the `isLiked` state, and fires off an API call to save that interaction to the database.
Common Mistakes
Calling a function immediately in the event handler (e.g., `onClick={handleClick()}`) instead of passing the function reference (`onClick={handleClick}`). The former runs when the component renders, causing infinite loops.
Performance & Security
Avoid defining inline arrow functions inside large lists (e.g., `onClick={() => delete(id)}`) if performance drops, as it creates a new function on every render. Use memoization or data attributes instead.
How it works
React uses a 'Synthetic Event' system. Instead of attaching a unique event listener to every single button in the DOM, React attaches a single listener to the root of the document (Event Delegation). When you click a button, React intercepts the native browser event, wraps it in a cross-browser SyntheticEvent, and routes it to your component.
Code Example
function LightSwitch() {
// Define the event handler function
function handleClick(event) {
// We can access standard event properties
console.log("Button was clicked at coordinates:", event.clientX, event.clientY);
alert("Switch toggled!");
}
// Pass the function (don't call it immediately!)
return <button onClick={handleClick}>Toggle Light</button>;
}Interview Questions
8 questions to test your knowledge
Summary
React uses synthetic events to handle user interactions consistently across all browsers, easily bridging the gap between the UI and your state logic.
Up Next
Now we can click buttons! Next, let's learn how to show or hide entire components based on those clicks using Conditional Rendering.
Conditional rendering
Definition
Conditional rendering is how you make your app show different things based on the state. For example, showing a 'Login' button if the user is logged out, but a 'Dashboard' if they are logged in. You do this using standard JavaScript logic like `if/else`, the logical AND `&&`, or the ternary operator `? :`.
Why This Exists
Apps need to show different UI depending on the context. If a user is logged in, show their profile. If they are logged out, show the login button. Conditional rendering lets you describe these logical branches declaratively.
How Companies Use It
When you visit Netflix, it checks if you have an active subscription. If true, it conditionally renders the movie catalog. If false, it conditionally renders the payment upgrade page.
Common Mistakes
Using `if` statements directly inside JSX. JSX only accepts expressions (values), so you must use the ternary operator (`condition ? true : false`) or the logical AND (`condition && true`).
Performance & Security
When conditionally rendering large, complex components, constantly mounting and unmounting them can be expensive. Sometimes it's better to just hide them using CSS (`display: none`).
How it works
When a condition changes (e.g., a boolean state goes from false to true), React re-runs the component. If the component returns a different branch of JSX, React's diffing algorithm notices that a new element was added or removed and updates the DOM accordingly.
Code Example
function Dashboard({ isLoggedIn }) {
// Using an early return (if/else)
if (!isLoggedIn) {
return <p>Please log in to view your data.</p>;
}
return (
<div>
<h1>Welcome back!</h1>
{/* Using logical AND (&&) for quick checks */}
{isLoggedIn && <button>Logout</button>}
{/* Using Ternary Operator (? :) for either/or */}
<p>{isLoggedIn ? 'Online' : 'Offline'}</p>
</div>
);
}Interview Questions
8 questions to test your knowledge
Summary
Conditional rendering allows you to render different React elements based on the state or props of your application.
Up Next
We can now show one thing or another. But what if we need to show a hundred things, like a list of products? Let's tackle Lists & Keys.
Lists & keys
Definition
When you have an array of data (like a list of tasks or users), you use the JavaScript `map()` array method to convert that data into an array of React elements. Every item in the list must have a unique `key` prop attached to it.
Why This Exists
Writing a hardcoded `<ProductCard />` component 100 times for a store is impossible. React needs a way to take an array of raw data from a database and dynamically generate a list of components from it.
How Companies Use It
Your Facebook feed is just a massive JavaScript array of 'post' objects. React maps over that array and renders a `<Post />` component for every single item, attaching a unique key to each.
Common Mistakes
Using the array `index` as the `key` prop. If the list changes order (e.g., you delete an item or sort the list), React gets confused and might render the wrong data or lose state.
Performance & Security
Always provide a unique, stable, and predictable `key` prop (like a database ID). This allows React's diffing algorithm to instantly identify which items changed, were added, or were removed without re-rendering the whole list.
How it works
Keys help React identify which items have changed, been added, or been removed. If a list changes order and there are no keys, React has to destroy and recreate the elements, which is slow and can mess up component state. With unique keys, React can just move the existing DOM nodes to their new positions.
Code Example
function TaskList() {
const tasks = [
{ id: 1, text: "Buy groceries" },
{ id: 2, text: "Walk the dog" },
{ id: 3, text: "Learn React" }
];
return (
<ul>
{/* We map over the array and return a list item for each */}
{tasks.map((task) => (
// The key must be unique and stable (like a database ID)
<li key={task.id}>{task.text}</li>
))}
</ul>
);
}Interview Questions
8 questions to test your knowledge
Summary
You can build collections of elements and include them in JSX using array map(), provided each item has a unique key prop.
Up Next
Congratulations, you've finished the Beginner track! Let's move to Intermediate and learn about the Component Lifecycle.
useEffect & component lifecycle
Definition
Imagine you're baking a cake. You put it in the oven (rendering), and then you set a timer to check on it later. `useEffect` is exactly like that timer. It lets React do something *after* the component has finished drawing itself on the screen, like fetching data from a server or listening to a window resize.
Real-Life Problem vs Solution
If you try to run heavy logic or fetch data while React is trying to draw the UI, the browser freezes and the user sees a blank screen.
Blocks Screen Paint
React draws the visual UI first so the user sees something instantly. Then, it runs the `useEffect` logic in the background.
Paint First, Effect Later
Why This Exists
React components are pure functions. They shouldn't have 'side effects' like fetching data from a server or manually changing the DOM during render. `useEffect` gives you a safe place to run these side effects after the UI has painted.
How Companies Use It
When you open a Twitter thread, React renders the empty skeleton first. Then, `useEffect` fires in the background, fetches the replies from the API, and updates the state to show them.
Common Mistakes
Forgetting the dependency array entirely (e.g., `useEffect(() => {...})`), causing the effect to run on every single render and potentially creating an infinite loop if it updates state.
Performance & Security
Always clean up subscriptions (like WebSockets or `setInterval`) in the return function of `useEffect`. Failing to do so causes massive memory leaks and performance degradation over time.
How it works
React defers running useEffect until after the browser has painted the screen, preventing blocking of the visual update. The dependency array acts as a memoization check: React compares the current array values with the previous render's values using Object.is to determine if the effect should execute.
Code Example
import { useEffect, useState } from 'react';
function DataFetcher() {
const [data, setData] = useState(null);
useEffect(() => {
let isMounted = true;
fetch('/api/data').then(res => res.json()).then(d => {
if(isMounted) setData(d);
});
// Cleanup function runs on unmount
return () => { isMounted = false; };
}, []); // Empty array means run once on mount
return <div>{data ? data.title : 'Loading...'}</div>;
}Interview Questions
7 questions to test your knowledge
Summary
The useEffect Hook lets you perform side effects in function components, replacing older lifecycle methods like componentDidMount.
Up Next
Now we can fetch data. Let's learn how to capture user input to send back to the server using Forms & Controlled Components.
Forms & controlled components
Definition
Think of an old-school cash register. You type in numbers, but the manager in the back office has no idea what you're typing until you hit 'Submit'. In React, we put the manager right next to the register! A 'controlled component' means React knows exactly what you are typing the moment your finger hits the key.
Real-Life Problem vs Solution
Standard HTML inputs hide their data inside the DOM. React has to 'guess' or manually ask the DOM what the user typed.
DOM Holds the Truth
React intercepts every single keystroke, saves it in a centralized state cloud, and then pushes it back to the input box instantly.
React is the Single Source
Why This Exists
Traditional HTML forms handle their own state inside the DOM. This makes it hard to instantly validate passwords, format credit card numbers, or disable submit buttons. React takes control of the input to provide a seamless user experience.
How Companies Use It
When you type your credit card on Stripe, a controlled component instantly formats the numbers with spaces and checks the card type (Visa/Mastercard) on every single keystroke.
Common Mistakes
Providing a `value` prop to an input but forgetting the `onChange` handler. React will strictly enforce the value, making the input completely read-only and un-typable.
Performance & Security
For massive forms with hundreds of inputs, strictly controlled components can cause performance lag due to constant re-rendering. In those cases, use 'uncontrolled' components via `useRef` or libraries like React Hook Form.
How it works
When a user types, the browser fires a native onChange event. React intercepts this via its SyntheticEvent system, updates the state, and triggers a re-render. The input then receives its new value via the 'value' prop, synchronizing the DOM with React's memory.
Code Example
function ControlledInput() {
const [value, setValue] = useState("");
return (
<input
type="text"
value={value}
onChange={(e) => setValue(e.target.value)}
placeholder="Type here..."
/>
);
}Interview Questions
8 questions to test your knowledge
Summary
Controlled components are form elements whose value is controlled entirely by React state, enabling real-time validation and formatting.
Up Next
Our components are getting complex. What happens when two different components need access to the same form data? Let's explore Lifting State Up.
Lifting state up & prop drilling
Definition
Imagine two brothers sharing a bedroom. They have a wall between them and can't talk directly. If one brother finds a cool toy, he has to give it to his Mom (the parent), and Mom walks over and gives it to the other brother. In React, components can't share data sideways. We have to 'lift the data up' to a parent component.
Real-Life Problem vs Solution
Sibling components are isolated. A sidebar component has no way to tell a main content component what button was clicked.
Trapped State
The state is moved to the parent container. The parent receives the click from the sidebar, and passes the updated data down to both children.
State Lifted Up
Why This Exists
React data flows one way: downwards. Sibling components cannot communicate directly. To share data, you must find their closest common parent and place the state there so it can be passed down to both.
How Companies Use It
In an e-commerce app, a 'Sidebar Filter' and a 'Product Grid' are siblings. The filter state is lifted up to the 'Catalog Page' parent, which passes the filtered data down to the grid.
Common Mistakes
Lifting state too high. If you put a simple toggle state at the very top of your app (`<App />`), clicking the toggle will unnecessarily force your entire application to re-render.
Performance & Security
Passing props down through many layers (Prop Drilling) doesn't just make code ugly; it forces intermediate components to re-render even if they don't use the data.
How it works
Lifting state up does not change the core architecture, but it forces the parent component to re-render whenever the state changes. This causes all children (including the one that didn't request the change) to re-render unless optimized with React.memo.
Code Example
function Parent() {
const [activeTab, setActiveTab] = useState(0);
return (
<>
<Sidebar active={activeTab} onSelect={setActiveTab} />
<MainContent active={activeTab} />
</>
);
}Interview Questions
6 questions to test your knowledge
Summary
Lifting state up is the practice of moving state to the closest common ancestor of the components that need it.
Up Next
What if you need to pass data down 10 levels deep? Prop drilling becomes a nightmare. Let's solve this with the Context API.
Context API
Definition
Imagine a school where the principal wants to announce a snow day. Instead of telling a teacher, who tells a hallway monitor, who tells a student (Prop Drilling), the principal just uses the school Intercom System (Context API). Everyone who is listening hears it instantly!
Real-Life Problem vs Solution
Passing props through 5 layers of components (who don't even need the data) just to get it to the bottom is messy and hard to maintain.
Prop Drilling
Context creates a direct teleportation tunnel. The Provider broadcasts the data, and any component can grab it instantly using useContext.
Context Teleportation
Why This Exists
Passing props through components that don't need them (Prop Drilling) makes code brittle and hard to read. Context provides a way to 'teleport' data directly to the components that actually need it.
How Companies Use It
Almost every major application uses Context for global settings like User Authentication (are they logged in?), UI Themes (Dark/Light mode), and localization (English/Spanish).
Common Mistakes
Using Context for rapidly changing data (like keystrokes or mouse positions). Context isn't optimized for high-frequency updates and will cause widespread re-renders.
Performance & Security
Always wrap the object passed to the `value` prop of a Provider in a `useMemo` hook. Otherwise, a new object reference is created on every render, forcing all consumers to re-render.
How it works
Under the hood, Context uses a Publisher/Subscriber model. The Provider is the publisher, and any component calling useContext is a subscriber. When the Provider's value changes, React aggressively bypasses normal shouldComponentUpdate checks and forces a re-render on all subscribing components.
Code Example
const ThemeContext = createContext('light');
function App() {
return (
<ThemeContext.Provider value="dark">
<Toolbar />
</ThemeContext.Provider>
);
}
function Toolbar() {
const theme = useContext(ThemeContext);
return <div>Current theme: {theme}</div>;
}Interview Questions
7 questions to test your knowledge
Summary
Context provides a way to pass data through the component tree without having to pass props down manually at every level.
Up Next
Context handles global state. Now let's look at `useRef`, which handles mutable data without triggering any re-renders at all.
useRef
Definition
Think of `useState` as a loud alarm. Every time the state changes, the alarm rings and React redraws the whole screen. But what if you just want to secretly write down a number on a sticky note without triggering the alarm? That sticky note is `useRef`. It holds a value quietly.
Real-Life Problem vs Solution
Using state for variables that change very quickly (like a stopwatch ID) causes the screen to flash and redraw hundreds of times a second.
State Triggers Render
useRef provides a quiet vault. You can update the value inside as many times as you want without causing a single re-render.
Ref Updates Silently
(No Flashes)
Why This Exists
Sometimes you need to store data (like a timer ID or previous state) that shouldn't trigger a visual update when it changes. Or, you need to manually focus an input element. `useRef` provides a mutable variable that persists across renders without causing re-renders.
How Companies Use It
When you open a modal in an application, the first input field is often automatically focused. This is achieved by attaching a `useRef` to the input and calling `ref.current.focus()` when the modal mounts.
Common Mistakes
Using `useRef` when you actually *do* need the screen to update. If you use a ref to store a 'counter' and display it in JSX, the screen will never show the updated number until something else forces a render.
Performance & Security
Avoid overusing `useRef` to manually manipulate DOM elements (imperative programming). React expects to be the sole manager of the DOM (declarative). Mixing the two can lead to severe synchronization bugs.
How it works
React stores the ref object outside the normal render cycle. Unlike state variables which are immutable snapshots per render, a ref is the exact same JavaScript object reference across all renders. It is commonly used to hold direct references to DOM nodes.
Code Example
function TextInputWithFocusButton() {
const inputEl = useRef(null);
const onButtonClick = () => {
// Directly access the DOM node
inputEl.current.focus();
};
return (
<>
<input ref={inputEl} type="text" />
<button onClick={onButtonClick}>Focus the input</button>
</>
);
}Interview Questions
7 questions to test your knowledge
Summary
useRef returns a mutable ref object whose .current property is initialized to the passed argument. It persists for the full lifetime of the component without triggering renders.
Up Next
We know how to manage state and refs on a single page. Next, let's learn how to navigate between multiple pages using React Router.
React Router
Definition
Think of an old TV where changing the channel meant the screen went totally black for a second. That's a traditional website link. React Router turns your app into a smart TV—the menu stays perfectly still, and only the movie in the middle changes seamlessly.
Real-Life Problem vs Solution
Clicking a link causes the browser to throw away the entire app and download a brand new HTML file, creating a slow white flash.
Hard Page Reload
React Router intercepts the click. It stops the browser from loading a new page, and instead just swaps out the React components instantly.
Client-Side Routing
Why This Exists
React by itself is a Single Page Application (SPA). It doesn't know what to do if the user types `/about` in the URL bar. React Router acts as a traffic cop, intercepting URL changes and rendering the correct component without refreshing the page.
How Companies Use It
When you navigate from the 'Home' tab to the 'Notifications' tab on Twitter, the page doesn't blink or reload. React Router just unmounts the Home component and mounts the Notifications component instantly.
Common Mistakes
Using standard `<a href='/about'>` anchor tags for internal navigation. This forces the browser to do a hard refresh, completely destroying your React state. Always use React Router's `<Link>` component.
Performance & Security
Never trust client-side routing for security! Just because you hid the `/admin` route doesn't mean a malicious user can't access the API. Always secure your endpoints on the backend.
How it works
Client-side routers use the HTML5 History API (pushState, replaceState, popstate). React Router listens to these browser events and conditionally renders different component trees based on the current URL path, bypassing the default browser navigation refresh.
Code Example
import { BrowserRouter, Routes, Route, Link } from 'react-router-dom';
function App() {
return (
<BrowserRouter>
<nav><Link to="/">Home</Link> | <Link to="/about">About</Link></nav>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
</Routes>
</BrowserRouter>
);
}Interview Questions
7 questions to test your knowledge
Summary
React Router enables client-side routing, allowing your app to have multiple pages and URLs without traditional browser page reloads.
Up Next
We're building complex apps now. Instead of rewriting the same logic in every component, let's learn how to share logic using Custom Hooks.
Custom Hooks
Definition
Imagine you built an awesome Lego motor that makes a car drive. Later, you want to build a helicopter. Instead of building the motor from scratch again, you just take the motor out of the car and plug it into the helicopter! Custom Hooks let you extract logic and plug it into any component.
Real-Life Problem vs Solution
Components get huge and messy when they contain complex API fetching, error handling, and data parsing logic all mixed with the UI.
Messy Component
We extract the logic into a reusable 'hook' box. The component simply calls the hook and gets back the clean data it needs.
Custom Hook Extracted
Why This Exists
You often find yourself rewriting the same stateful logic (like fetching data, listening to window resizing, or managing a form) across multiple components. Custom Hooks let you extract that logic into a reusable function.
How Companies Use It
At large companies, engineers rarely use raw `useEffect` to fetch data. They use custom hooks like `useSWR` or `useQuery` (from React Query) which encapsulate caching, loading states, and error handling in one clean function.
Common Mistakes
Forgetting that Custom Hooks share *logic*, not *state*. If two components call `useCounter()`, they each get their own completely independent counter state. They do not share the number.
Performance & Security
Keep your Custom Hooks focused on a single responsibility. A 'God Hook' that handles auth, fetching, and theme switching is unmaintainable. Break them down into smaller hooks.
How it works
Custom hooks do not share state between components. Each call to a hook gets a completely isolated instance of state. They are simply a mechanism to share stateful logic, acting as an abstraction over React's internal linked-list hook storage.
Code Example
function useWindowWidth() {
const [width, setWidth] = useState(window.innerWidth);
useEffect(() => {
const handleResize = () => setWidth(window.innerWidth);
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, []);
return width;
}
function MyComponent() {
const width = useWindowWidth();
return <p>Window is {width}px wide</p>;
}Interview Questions
7 questions to test your knowledge
Summary
Custom Hooks let you extract component logic into reusable functions, keeping your components clean and DRY (Don't Repeat Yourself).
Up Next
Custom hooks conclude our Intermediate track! We are now moving to Advanced topics. First up: Performance optimization with useMemo and useCallback.
API Calls & Data Fetching
Definition
Frontend apps are useless without data. React needs to talk to a backend server (like a Node.js API or Firebase) to get users, posts, or products. We use tools like the native fetch API or Axios to make these HTTP requests and store the response in state.
Real-Life Problem vs Solution
Data fetching takes time (milliseconds to seconds). If React waits for the data before rendering, the user sees a blank white screen.
White Screen (Sync Fetch)
React renders the UI immediately with a 'Loading...' spinner. Once the data arrives, it updates the state and re-renders the actual content.
Async Render
Why This Exists
A React app without external data is just a static website. Data fetching connects your UI to the real world.
How Companies Use It
When you open Amazon, the UI loads instantly, and then product images and prices populate a second later as the API calls resolve.
Common Mistakes
Forgetting the dependency array in useEffect, causing a DDoS attack on your own API because it fetches on every render.
Performance & Security
Always handle network errors with try/catch. Never assume an API call will succeed. Implement robust loading and error states.
How it works
Network requests are asynchronous. When fetch is called, it returns a Promise. React continues rendering. Once the Promise resolves, a .then() block or await statement triggers a state update, queueing a new render cycle.
Code Example
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
async function fetchUser() {
const response = await fetch(`/api/users/${userId}`);
const data = await response.json();
setUser(data);
setLoading(false);
}
fetchUser();
}, [userId]);
if (loading) return <p>Loading...</p>;
return <div>{user.name}</div>;
}Interview Questions
1 questions to test your knowledge
Summary
Data fetching in React typically involves making async requests inside useEffect and storing the result in state.
Up Next
We can get data, but how do we secure it? Next, let's learn about Authentication.
Authentication & Authorization
Definition
Authentication proves WHO you are (logging in). Authorization proves WHAT you are allowed to do (admin vs user). In React, we typically manage this by receiving a JSON Web Token (JWT) from the server and storing it securely.
Real-Life Problem vs Solution
Without auth state, you have to force a full page reload every time the user logs in to check their session.
Page Reloads on Login
React stores the Auth state globally (usually in Context). The moment the token arrives, the entire UI instantly updates to show logged-in features.
Context Updates UI Instantly
Why This Exists
Almost every application requires user accounts. We need a standardized way to lock down parts of the UI and securely identify the user making API calls.
How Companies Use It
When you log into Spotify, you get a token. Every time you play a song, React attaches that token to the API request to prove it's you.
Common Mistakes
Assuming client-side route protection (e.g., hiding the /admin route in React) is secure. It's not. The backend must always verify the token.
Performance & Security
Always use HTTPS. Never send passwords or tokens over unencrypted HTTP connections.
How it works
Auth state is usually kept in a top-level Context Provider. When the token is received, the Provider state updates, immediately unlocking protected routes and changing UI elements (like showing 'Logout' instead of 'Login').
Code Example
function ProtectedRoute({ children }) {
const { user } = useContext(AuthContext);
if (!user) {
return <Navigate to="/login" replace />;
}
return children;
}Interview Questions
1 questions to test your knowledge
Summary
React handles authentication by managing the session state (often via Context) and conditionally rendering Protected Routes.
Up Next
Congratulations on finishing Intermediate! Let's move to Advanced: Performance Optimization.
Performance: useMemo & React.memo
Definition
React is fast, but it's not magic. If a parent component re-renders, ALL of its children re-render by default. `React.memo` stops a component from re-rendering if its props haven't changed. `useMemo` stops a heavy calculation from re-running if its inputs haven't changed.
Real-Life Problem vs Solution
Typing into a search bar at the top of the app forces a massive data table at the bottom of the app to re-render 10 times a second.
Wrapping the table in React.memo tells React to skip rendering the table unless the actual table data changes.
Why This Exists
As apps grow, they become heavy. You need surgical precision to tell React exactly which parts of the tree to skip during updates to maintain 60fps.
How Companies Use It
Figma is built in React. When you drag a shape, they use extreme memoization to ensure the thousands of other shapes on the canvas don't re-render.
Common Mistakes
Passing a newly created function or array down to a memoized component. The new reference breaks the memoization instantly.
Performance & Security
Memoization takes up memory. You are trading RAM for CPU cycles. Use it wisely.
How it works
React uses a shallow equality check (Object.is) on the props. If the old props exactly match the new props, it short-circuits the render phase and just reuses the old DOM output from memory.
Code Example
const ExpensiveChart = React.memo(function Chart({ data }) {
// This will only re-render if 'data' reference changes
return <canvas>...</canvas>;
});
function Dashboard() {
const [text, setText] = useState("");
// useMemo prevents array recreation on every keystroke
const data = useMemo(() => [1, 2, 3], []);
return (
<>
<input onChange={e => setText(e.target.value)} />
<ExpensiveChart data={data} />
</>
);
}Interview Questions
1 questions to test your knowledge
Summary
useMemo caches values. useCallback caches functions. React.memo caches entire components.
Up Next
Now our app is fast. How do we ensure it doesn't break when we change code? Testing.
Testing (Jest & RTL)
Definition
Automated testing means writing code that tests your code. We use Jest as the test runner, and React Testing Library (RTL) to simulate a user clicking buttons and reading text on your components.
Real-Life Problem vs Solution
You add a new feature, deploy to production, and realize you broke the login page. You lose money and users.
Automated tests run before every deployment. If the login page test fails, the deployment is blocked.
Why This Exists
Manual testing is slow, error-prone, and unscalable. Automated tests guarantee that old features don't break when you add new ones (preventing regressions).
How Companies Use It
At Facebook, thousands of tests run on every single code commit. If even one test fails, the code cannot be merged into the main branch.
Common Mistakes
Writing tests that are too tightly coupled to CSS classes. Always query by accessibility roles (getByRole) or text.
Performance & Security
Tests run in CI/CD pipelines, not on the user's device, so they don't affect production performance.
How it works
RTL renders your component in a simulated, headless browser environment (JSDOM). It provides APIs to query the virtual DOM (like screen.getByText) exactly how a screen reader or real user would interact with it.
Code Example
import { render, screen, fireEvent } from '@testing-library/react';
import Button from './Button';
test('button click changes text', () => {
render(<Button />);
const btn = screen.getByRole('button', { name: /click me/i });
expect(btn).toBeInTheDocument();
fireEvent.click(btn);
expect(screen.getByText(/clicked!/i)).toBeInTheDocument();
});Interview Questions
1 questions to test your knowledge
Summary
Jest and RTL combine to let you write robust, automated tests that interact with your components like a real user.
Up Next
We've got passing tests. Now, how do we get this app onto the internet? Deployment.
Deployment & CI/CD
Definition
Deployment is the process of taking your local React code, bundling it into highly optimized static files, and putting it on a server for the world to see. CI/CD (Continuous Integration / Continuous Deployment) automates this process every time you push to GitHub.
Real-Life Problem vs Solution
Manually building files, opening an FTP client, and dragging files to a server is slow, dangerous, and causes downtime.
You push code to GitHub. GitHub Actions automatically runs your tests, builds the app, and seamlessly deploys it to Vercel without you lifting a finger.
Why This Exists
The ultimate goal of software engineering is to deliver the product to the user. Automated pipelines make delivery fast and safe.
How Companies Use It
Companies like Vercel and Netlify have revolutionized frontend deployment by hooking directly into GitHub to offer instant, zero-config deployments.
Common Mistakes
Leaking API keys in production builds. Always ensure sensitive keys are handled server-side, not hardcoded in your React environment variables.
Performance & Security
Serving your app via a CDN ensures that users in Tokyo download the files from a server in Tokyo, not a server in New York, massively improving load times.
How it works
The build step (npm run build) compiles all JSX, minifies JavaScript, tree-shakes dead code, and generates a 'dist' folder containing pure HTML/CSS/JS. A CDN (Content Delivery Network) then hosts these files globally at the edge.
Code Example
# .github/workflows/deploy.yml
name: Deploy
on:
push:
branches: [ main ]
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: npm ci
- run: npm test
- run: npm run build
- run: npx vercel --prod --token=${{ secrets.VERCEL_TOKEN }}Interview Questions
1 questions to test your knowledge
Summary
CI/CD pipelines automate the testing and building of your React app, safely deploying optimized static files to a global CDN.
Up Next
Our app is live! But what if a bug slipped through and the app crashes in production? Let's use Error Boundaries.
Error Boundaries
Definition
If a JavaScript error occurs inside a React component, it completely unmounts the entire application, leaving the user with a blank white screen. Error Boundaries are special components that catch these crashes and display a fallback UI (like 'Oops, something went wrong') instead of crashing the whole app.
Real-Life Problem vs Solution
A minor bug in the Sidebar component crashes the entire page, including the Main Content, giving the user a 'White Screen of Death'.
An Error Boundary wraps the Sidebar. If the Sidebar crashes, only the Sidebar shows an error message. The rest of the app continues working perfectly.
Why This Exists
Bugs happen in production. You want to isolate the damage and provide a good user experience, rather than showing a blank screen.
How Companies Use It
If a single post in your Facebook feed contains corrupted data and crashes, Facebook uses an Error Boundary to show a 'Post Unavailable' box, keeping the rest of your feed completely intact.
Common Mistakes
Forgetting that Error Boundaries do NOT catch errors inside event handlers (like onClick) or async API calls. They only catch errors during the render phase.
Performance & Security
Always hook your Error Boundaries up to a tracking service like Sentry or LogRocket so you get alerted when users experience crashes.
How it works
Error Boundaries use a special class lifecycle method called componentDidCatch. When an error is thrown in any child component, it propagates up the tree until it hits this boundary, which then halts the crash and renders a fallback UI.
Code Example
class ErrorBoundary extends React.Component {
state = { hasError: false };
static getDerivedStateFromError(error) {
return { hasError: true };
}
componentDidCatch(error, info) {
logErrorToService(error, info.componentStack);
}
render() {
if (this.state.hasError) {
return <h1>Something went wrong.</h1>;
}
return this.props.children;
}
}Interview Questions
1 questions to test your knowledge
Summary
Error Boundaries are React components that catch JavaScript errors anywhere in their child component tree, log them, and display a fallback UI.
Up Next
Next, we'll learn how to split our large app into smaller chunks to speed up the initial load time.
Code Splitting & Lazy Loading
Definition
If your app has 100 pages, forcing the user to download the code for all 100 pages just to view the Homepage is terrible for performance. Code Splitting breaks your single massive JavaScript bundle into smaller chunks. Lazy Loading ensures those chunks are only downloaded right when the user needs them.
Real-Life Problem vs Solution
A massive 5MB JavaScript bundle blocks the browser. The user stares at a white screen for 10 seconds while it downloads.
Full Layout Recalculation
The homepage only downloads 100KB of JS and loads instantly. The code for the Settings page isn't downloaded until the user actually clicks 'Settings'.
Targeted DOM Patch
Why This Exists
As enterprise apps grow, bundle sizes become unmanageable. Code splitting is mandatory for fast Time-to-Interactive (TTI) scores.
How Companies Use It
When you load a heavy WebGL game on a React site, the main UI loads instantly, and the heavy 3D engine is lazy-loaded in the background.
Common Mistakes
Lazy loading tiny components (like a Button). The overhead of making a separate network request actually makes performance worse than just bundling it.
Performance & Security
Code splitting is the #1 easiest way to dramatically improve Lighthouse performance scores on massive React apps.
How it works
React.lazy() combined with Suspense tells Webpack/Vite to create separate output files. When the component is requested, React suspends rendering, fetches the new JS file over the network, and then resumes rendering.
Code Example
import React, { Suspense } from 'react';
// This component is loaded dynamically
const HeavyDashboard = React.lazy(() => import('./HeavyDashboard'));
function App() {
return (
<div>
<Suspense fallback={<p>Loading dashboard...</p>}>
<HeavyDashboard />
</Suspense>
</div>
);
}Interview Questions
1 questions to test your knowledge
Summary
Code splitting and lazy loading defer the downloading of non-critical JavaScript until it is actually needed by the user.
Up Next
Our app is fast and split. Now let's tackle managing huge amounts of state across the entire architecture.
State Management: Redux vs Zustand
Definition
Context API is great for simple things, but complex apps need robust State Management libraries. Redux is the enterprise standard, using a strict unidirectional flow with Actions and Reducers. Zustand is the modern, lightweight alternative that uses a much simpler API.
Real-Life Problem vs Solution
Using Context API for high-velocity data (like a multiplayer game state) causes the entire app to lag due to massive unnecessary re-renders.
Libraries like Zustand allow components to subscribe to only tiny slices of the store, ensuring only the specific component that needs the data re-renders.
Why This Exists
Massive enterprise apps need a predictable, debuggable way to manage state. Redux's strict rules prevent spaghetti state mutations.
How Companies Use It
Uber uses robust state management to track thousands of moving cars, user locations, and price surges globally without the UI collapsing.
Common Mistakes
Putting literally every piece of state (even a simple dropdown toggle) into the global Redux store instead of keeping it in local component state.
Performance & Security
Modern state managers automatically optimize renders by using selectors. Always use selectors to extract only the data you need.
How it works
Redux uses a single immutable state tree. To change state, you dispatch an 'Action' object. A 'Reducer' function receives the action and returns a completely new state tree. Zustand simplifies this by just giving you direct hooks to access and mutate the store.
Code Example
// Zustand Example
import { create } from 'zustand'
const useStore = create((set) => ({
bears: 0,
increasePopulation: () => set((state) => ({ bears: state.bears + 1 })),
removeAllBears: () => set({ bears: 0 }),
}))
function BearCounter() {
// Only re-renders if 'bears' changes
const bears = useStore((state) => state.bears)
return <h1>{bears} around here ...</h1>
}Interview Questions
1 questions to test your knowledge
Summary
State management libraries provide robust, scalable architectures for handling complex, high-velocity global data.
Up Next
We have all the tools. Let's look at the big picture: Large Scale Architecture.
Large Scale Architecture
Definition
Writing a 'To-Do list' is easy. Writing an enterprise application with 50 engineers is hard. Large Scale Architecture involves organizing your codebase (Feature-Sliced Design), standardizing APIs, enforcing strict ESLint rules, and separating concerns so teams can work without stepping on each other's toes.
Real-Life Problem vs Solution
All files dumped into a single 'components' folder. Engineers constantly conflict. Changing a button breaks the payment page.
Full Layout Recalculation
Code is organized by 'Features' (e.g., /features/auth, /features/checkout). Each feature is an isolated module that exposes a clean API.
Targeted DOM Patch
Why This Exists
Software architecture isn't about the code; it's about the people. Good architecture allows 50 engineers to ship features quickly without breaking the app.
How Companies Use It
At Netflix, strict architectural boundaries ensure the Video Player team can deploy updates without affecting the Billing team.
Common Mistakes
Over-engineering. Applying a massive micro-frontend architecture to a simple blog site.
Performance & Security
Well-architected code is infinitely easier to audit for security vulnerabilities because data flows are predictable.
How it works
Feature-Sliced Design (FSD) organizes code by domain logic. A 'feature' encapsulates its own components, state, API calls, and types. It cannot reach into other features directly, ensuring massive codebases remain loosely coupled.
Code Example
// Standard Enterprise Folder Structure:
// src/
// ├─ app/ # App initialization, global providers
// ├─ features/ # Business logic chunks
// │ ├─ auth/ # Everything auth related
// │ └─ cart/ # Everything cart related
// ├─ shared/ # Reusable UI (Buttons, Inputs)
// └─ pages/ # Route definitions composing features
Interview Questions
1 questions to test your knowledge
Summary
Enterprise architecture focuses on modularity, clear boundaries, and scalable folder structures like Feature-Sliced Design.
Up Next
You've made it to the end. Time to put everything together in the Capstone Project.
Capstone Projects
Definition
The ultimate test of your knowledge. In the real world, you aren't given isolated tasks; you are given a blank canvas and a business requirement. The Capstone requires you to combine Hooks, Routing, Auth, Performance, and Architecture to build a production-ready application.
Real-Life Problem vs Solution
Knowing individual React features doesn't mean you know how to combine them into a cohesive product.
Full Layout Recalculation
Building a complex project solidifies your understanding of how everything fits together in a real engineering environment.
Targeted DOM Patch
Why This Exists
Theory is useless without practice. The capstone builds your portfolio and proves your competency.
How Companies Use It
This is exactly what your first week on the job will look like: combining multiple systems to deliver a feature.
Common Mistakes
Tutorial Hell. Getting stuck watching tutorials but never building anything from scratch yourself.
Performance & Security
In your capstone, you must demonstrate security best practices (no XSS vulnerabilities) and achieve a 90+ Lighthouse performance score.
How it works
A full React application requires orchestrating the Router for navigation, Context for auth, Custom Hooks for data fetching, and memoization for performance—all simultaneously.
Code Example
// Your mission: Build a fully functional E-commerce Frontend.
// Requirements:
// 1. JWT Authentication (Login/Register)
// 2. Product Catalog with Lazy Loading
// 3. Global Cart State Management (Zustand/Redux)
// 4. Protected Checkout Route
// 5. Full RTL Test CoverageInterview Questions
1 questions to test your knowledge
Summary
The capstone project synthesizes all your learning into a massive, production-ready React application.
Up Next
Congratulations. You are now a React Master.