React has revolutionized how we build user interfaces, but have you ever wondered what happens behind the scenes when you write React code?
This comprehensive guide will take you through every step of React's internal workings, from writing JSX to seeing the final result in your browser.
Before we jump into React, let’s first have a quick overview of how JavaScript works internally.
Chapter 1: JavaScript Internal Working - Quick Overview
Before diving into React, it’s important to understand how JavaScript works behind the scenes.
- When you run JavaScript in the browser, it’s executed by a JavaScript engine (like Chrome’s V8). The engine first parses your code into an Abstract Syntax Tree (AST), then uses a Just-In-Time (JIT) compiler to convert it into optimized machine code for efficient execution.
- JavaScript is single-threaded, executing one task at a time via the Call Stack. Asynchronous operations such as API calls, timers, or DOM events are handled using Web APIs and the Event Loop, allowing tasks to run in the background without blocking the main thread.
- Each function runs in its own execution context, which tracks variables and scope. These contexts form a scope chain, enabling closures and consistent access to variables across nested functions.
Chapter 2: Writing React Components
React applications are built using components - self-contained, reusable pieces of code that describe what the user interface should look like. Think of components as LEGO blocks: each one serves a specific purpose and can be combined with others to create complex structures.
function Welcome(props) {
const [count, setCount] = useState(0);
return (
<div>
<h1>Hello, {props.name}!</h1>
<button onClick={() => setCount(count + 1)}>
Clicked {count} times
</button>
</div>
);
}
This simple component demonstrates several key React concepts:
JSX,Props,State,Event Handlers.
Now let us understand the component lifecycle:
Every React component goes through a series of phases from the time it’s created until it’s removed from the DOM. These phases collectively are called the component lifecycle, and they let you run code at specific points, like fetching data or cleaning up resources.
1.Class Component Lifecycle
Mounting (Creation): When the component is first added to the DOM.
constructor() → initialize state and bind methods
render() → returns JSX
componentDidMount() → runs after the component is addedUpdating: When state or props change, triggering a re-render.
render() → creates updated UI
componentDidUpdate() → runs after updatesUnmounting: When the component is removed from the DOM.
componentWillUnmount() → cleanup tasks like removing listeners
2.Functional Component Lifecycle (with Hooks)
- Mounting: Runs after the first render.
useEffect(() => { … }, [])
- Updating: Runs whenever dependencies change.
useEffect(() => { … }, [dependencies])
- Unmounting: Runs cleanup code before the component is removed.
useEffect(() => { return () => { … } }, [])
Chapter 3: JSX - The Bridge Between JavaScript and HTML
JSX (JavaScript XML) is a syntax extension that allows you to write HTML like code directly in JavaScript. While it looks like HTML, JSX is actually syntactic sugar that gets transformed into regular JavaScript function calls.
// This JSX code...
const element = <h1 className="greeting">Hello, World!</h1>;
// ...becomes this JavaScript:
const element = React.createElement(
'h1',
{ className: 'greeting' },
'Hello, World!'
);
The React.createElement() function is the heart of React's element creation system. It takes three parameters:
- Type: The element type (string for HTML elements, function for components)
- Props: An object containing attributes and event handlers
- Children: Child elements or text content
Chapter 4: What Babel Actually Does
Babel is a JavaScript transpiler that mainly handles syntax transformations, like converting JSX or modern ES6+ JavaScript into code browsers can understand. Its core tasks are:
- Parsing: Converts your code into an Abstract Syntax Tree (AST)
- Transformation: Converts JSX elements into React.createElement() calls
- Code Generation: Outputs browser-compatible JavaScript
Polyfills are related but separate they are pieces of code that add missing JavaScript features (like Promise, Array.from, Object.assign) in older browsers. Babel can inject polyfills via tools like "@babel/preset-env" or core-js, but polyfills aren’t the main thing Babel does; they are optional and usually configured explicitly.
// Before Babel (what you write):
function App() {
return (
<div>
<h1>My App</h1>
<Button onClick={handleClick}>Click me!</Button>
</div>
);
}
// After Babel (what the browser receives):
function App() {
return React.createElement(
'div',
null,
React.createElement('h1', null, 'My App'),
React.createElement(Button, { onClick: handleClick }, 'Click me!')
);
}
Chapter 5: Webpack – The Module Bundler
After Babel transforms your code, you still need a way to bundle all your files JavaScript, CSS, images into something the browser can load efficiently. That’s where Webpack comes in.
What Webpack does:
- Bundles modules: Combines all your JS files and dependencies into one (or a few) bundle files.
- Handles assets: Can process CSS, images, fonts, and more using loaders.
- Optimizes code: Supports minification, tree-shaking, and code splitting to make your app faster.
- Works with plugins: Adds extra features like HTML generation, environment variables, and caching.
In short, Babel transforms your code, and Webpack packages it all together so the browser can understand and run your app efficiently.
Chapter 6: The Virtual DOM - React's Secret Weapon
The Virtual DOM is React's most innovative feature, a lightweight, in-memory representation of the real DOM. Instead of directly manipulating the browser's DOM (which is slow), React creates and maintains a virtual copy in JavaScript memory.
The Virtual DOM process involves several steps:
- Initial Render: React creates a complete Virtual DOM tree representing your entire UI
- State Changes: When data changes, React creates a new Virtual DOM tree
- Diffing: React compares the new tree with the previous one
- Reconciliation: Only the differences are applied to the real DOM
The Reconciliation Algorithm
React's reconciliation algorithm is what makes the Virtual DOM efficient. It uses a sophisticated diffing process to minimize DOM updates:
- Element Type Comparison: If two elements have different types (e.g., vs ), React tears down the old tree and builds a new one from scratch.
- Same Type Elements: For elements of the same type, React keeps the same DOM node and only updates the changed attributes.
- List Reconciliation: React uses the key prop to identify which items have changed, been added, or removed in lists, making updates more efficient.
Chapter 7: React Fiber - The Modern Engine
React Fiber is a complete rewrite of React's core algorithm, introduced in React 16. It addresses the limitations of the original "stack reconciler" by enabling incremental rendering.
Why Fiber Was Necessary
The original React reconciler had a problem: it worked synchronously, meaning once it started updating the UI, it couldn't be interrupted. For complex applications, this could cause the browser to freeze, making the app feel unresponsive.
Fiber solves this by:
- Breaking work into units: Each component update becomes a unit of work that can be paused and resumed
- Prioritizing updates: User interactions get higher priority than background updates
- Enabling concurrent features: Multiple updates can be worked on simultaneously
Fiber Architecture
Each component in your app corresponds to a fiber node - a JavaScript object that contains:
- Type: The component type
- Props: The component's properties
- State: The component's internal state
- Child/Sibling pointers: Links to other fiber nodes
These fiber nodes form a fiber tree that mirrors your component hierarchy, allowing React to traverse and update components efficiently.
Chapter 8: State Management and Data Flow
React follows a unidirectional data flow.
Data flows down from parent components to children through props,
while changes flow up through callback functions.
State vs Props
State:
- Internal to a component
- Can be modified by the component itself
- Changes trigger re-renders
- Managed with useState (functional) or setState (class)
Props:
- External data passed from parent components
- Read-only within the receiving component
- Enable component reusability
- Can include children
- Changes come from the parent component
Chapter 9: The Complete React Rendering Process
Let’s put everything together and see what happens when you run a React application.
Step 1: Build Process
- You write React components using JSX and modern JavaScript.
- Babel transforms your code into React.createElement() calls.
- Webpack (or another bundler) packages all your files for the browser.
Step 2: Initial Render
- React creates the initial Virtual DOM tree representing your UI.
- The Reconciler converts the Virtual DOM into real DOM elements.
- The browser displays your application.
Step 3: User Interactions and Updates
- When a user interacts with your app (clicks, types, etc.),
- State updates are triggered via setState or hook updaters.
- React schedules a re-render using Fiber’s scheduler.
- A new Virtual DOM tree is created.
- The diffing algorithm compares the old and new Virtual DOM trees.
- The Reconciler applies only the minimal necessary changes to the real DOM.
- The browser updates just the parts of the UI that changed.
Step 4: Fiber Work Loop
- React Fiber breaks work into units that can be paused and resumed.
- High-priority updates (like user input) are handled first.
- Less urgent work is paused and completed later, keeping the app responsive.
In short, React transforms your code → builds a virtual representation → efficiently updates the browser using reconciliation and Fiber → keeps your UI fast and responsive.
Chapter 10: Performance Optimizations
React includes several built-in optimizations that make your apps fast:
- Batching Updates React automatically batches multiple state updates that happen in the same event handler, reducing the number of re-renders.
- Component Memoization React can skip re-rendering components when their props haven't changed, using techniques like React.memo() and useMemo().
- Lazy Loading Components can be loaded on-demand using React.lazy() and Suspense, reducing initial bundle size.
Wrapping It All Up
Now that we’ve walked through the journey from writing JSX to seeing your app in the browser, you have a clear picture of what’s happening behind the scenes. React isn’t just magic it’s a carefully designed system with Babel transforming your code, Webpack bundling it, the Virtual DOM and Fiber keeping updates fast, and state and props managing the flow of data.
Understanding this gives you a huge advantage. You can write cleaner code, debug smarter, and even optimize your app like a pro.
So, the next time you click “save” and see your UI update, take a moment to appreciate the orchestra of processes happening under the hood. The better you understand this internal flow, the more powerful and confident you’ll be as a React developer.
If you found this helpful, don’t forget to like, comment, and follow. I’ll be posting more blogs breaking down JavaScript, React, and frontend internals in an easy to understand way. Let’s keep learning together, Happy Coding!
Top comments (0)