React Real World Vademecum
Part I: The React Paradigm
Before writing a single component, you need to understand why React exists and what it changed forever. This first part is not an optional introduction: it is the difference between using React mechanically and truly understanding it.
The React Paradigm
1. Why "React"? (The Name Has a Story)
React is not called that by accident. The name reveals the entire intent of the library: to create user interfaces that react automatically to data. To understand the revolution, you first need to understand the problem it was designed to solve.
The Core Idea: A UI That Reacts to Data
Think about how a modern thermostat works. You do not have to get up and adjust the heating every time the temperature changes: you set the desired temperature, and the thermostat reacts automatically to the current state of the room to reach it.
This is exactly the principle behind React: you declare the desired state of your UI, and React takes care of updating the browser to reflect it on its own.
Before React, the life of a front-end developer was different.
// THE OLD WAY (Vanilla JavaScript)
// Every time data changes, you have to remember to update EVERY part of the DOM
function updateUI(newData) {
// Remember the title...
document.querySelector('#title').textContent = newData.name;
// ...the counter...
document.querySelector('#counter').textContent = newData.notifications;
// ...the badge...
document.querySelector('.badge').classList.toggle('hidden', newData.notifications === 0);
// ...and so on for every element that depends on the data
}
With React, you write once how the UI should look for every possible state of the data. Then you update the data, and React handles the rest.
The Facebook Problem in 2013
The story of React was born from a concrete problem that Facebook had with its interface. Imagine the notification bar at the top: it showed a number. Then the chat on the left: it showed unread messages. Then the comments on posts: they showed new replies. All these elements had to stay synchronized with the same data.
With Vanilla JavaScript, every small change required manually updating dozens of elements in the DOM. If you forgot one, you had a bug. If the structure changed, you had to rewrite everything. The code became impossible to maintain.
Jordan Walke, a Facebook engineer, created React in 2011 (publicly released in 2013) to solve exactly this: making the UI automatically synchronized with the data, without manual intervention.
The "Reactive" in the Name
React draws inspiration from the concept of reactive programming, but it is not a pure implementation of it in the academic sense. It is a practical simplification of the idea: instead of writing "when X changes, update Y and Z", you write "the UI is this when X has this value". React takes care of figuring out what needs to change and when.
2. Library vs Framework (The IKEA Model)
You will often hear the question: "Is React a library or a framework?". The correct answer is: it is a library. But understanding why is more useful than the answer itself.
What Is a Library? (IKEA)
A library is a collection of tools that you call when you need them. You are in control. You decide when to use it, how to integrate it, and what to do with the rest of the application.
Think of IKEA. You walk in, pick the furniture you need, pay, and leave. Then you assemble everything at your place however you want. You decide where to put the couch, whether to add a rug, how to organize the kitchen. IKEA sells you the pieces, it does not dictate how you furnish your home.
React is IKEA: it gives you the tools to build your interface, but it does not tell you how to structure routing, how to manage global state, how to communicate with the server. Those are your decisions.
Key Characteristic: You Call the Library
When you use React, it is your code that imports and calls React's components. The library knows nothing about the rest of your application until you invoke it.
What Is a Framework? (The Luxury Condo)
A framework is the opposite: it calls you. This concept has a precise name: Inversion of Control.
Imagine a luxury condo. The load-bearing walls are already in place. The kitchen is already in its designated spot. The electrical wires run through fixed paths. You can choose the wall colors, but you cannot move the kitchen into the bathroom. The condo has already made the structural decisions for you.
A framework works the same way: it tells you where to put the files, what to name them, how to structure them. When the app starts, it is the framework that calls your code, not the other way around.
Examples of real frameworks:
- Angular: has everything built in (routing, forms, HTTP client, testing). You move into an already furnished house.
- Docusaurus: want a documentation site? Your
.mdor.mdxfiles go in/docs/, blog posts in/blog/. You cannot do it differently.
The advantage of frameworks is that you have fewer decisions to make since the conventions are standardized; the major disadvantage is that you have much less freedom, making them harder to customize.
React in the Real World: The Library That Becomes a Framework
Here is the practical truth that is important to know from the start:
React on its own is just a UI library. It has no built-in router, no authentication system, and no standard way to fetch data.
But you never use React on its own in production. As soon as you add:
- React Router (for navigation)
- useState + Context (for global state)
- Vite (for the build)
- React Query (for data fetching)
...you have built your own handcrafted framework. You have made all the IKEA decisions yourself.
Next.js is the answer to the inevitability of this process: it is a framework built on React that has already made those decisions for you. It has built-in routing, built-in SSR, built-in data fetching. It is the luxury condo built with IKEA bricks from React.
React (library) → handles only the UI
React + Router + State → your handcrafted framework
Next.js → ready-made framework, decisions already made
Rule:
For small projects and for learning → React with Vite (you are the boss). For complex production projects → consider Next.js (someone has already chosen for you).
3. SPA, MPA, SSR, SSG, RSC (React Is Not Always a SPA)
One of the most common misconceptions among React beginners: "React = single-page app". That is not always true. React is a tool that can be used in many different ways. Understanding the differences allows you to choose the right architecture for each project.
The Classic Web (MPA, Multi-Page Application)
Before SPAs, the entire web worked this way. Every click on a link or every form submission triggered a sequence:
- The browser sent a request to the server
- The server built a complete HTML page
- The browser received the HTML and tore down the entire current page
- The browser redrew everything from scratch with the new page
Analogy: A theater where between scenes the stage is demolished, rebuilt, repainted, and refurnished from scratch, while the audience waits in the dark.
The "browser trauma" is that white screen lasting a few seconds between pages. It worked, but the UX was choppy.
The SPA Illusion (Single-Page Application)
React popularized a different approach: the entire app lives in a single index.html file. React intercepts link clicks, figures out which "page" to show, and replaces only the part of the DOM that needs to change, without ever reloading the browser.
Analogy: The same theater, but the stage stays in place. The audience never stands up. While the show continues, silent stagehands change only the necessary set pieces.
No white page. Instant transitions. UX as fluid as a native app.
// With React Router, "navigation" is a controlled illusion
// No new page is downloaded, React swaps the components
<Routes>
<Route path="/" element={<HomePage />} />
<Route path="/profile" element={<Profile />} />
<Route path="/shop" element={<Shop />} />
</Routes>
The Problems of a Pure SPA
Developing a Single Page Application involves two main trade-offs:
- Poor SEO (Indexing): On first contact, Google receives a completely empty HTML file. The actual content is "injected" by JavaScript only after the fact. Since search engine bots are in a hurry, they often do not wait for this processing, do not read the content, and do not index the site.
- Slow Initial Load: A user with a weak connection will stare at a white screen for a long time. Before being able to show them even a single button, the browser is forced to download, decode, and execute the entire JavaScript "bundle" that powers the application.
SSR (Server-Side Rendering)
The two SPA problems (SEO and slow initial load) brought back an old approach, adapted for the modern era.
In a classic SPA, rendering is client-side (CSR): the server sends the browser a nearly empty HTML file along with a large JavaScript bundle, and the browser must execute all that code before it can show anything. It is like buying furniture from IKEA: it arrives disassembled and you have to put it together at home.
With Server-Side Rendering (SSR), the work shifts to the server. When a user requests a page, the server runs React, builds the complete HTML with all content already in place, and sends it to the browser ready to display. It is like going to a restaurant: the dish arrives already prepared at the table and you can eat right away.
The advantage is twofold: the user sees the page instantly (no waiting for JavaScript) and search engines find an HTML full of content to index.
Hydration, React's Awakening
The HTML that arrives from the server is static, like a photograph: you can see everything but you cannot click anything. At that point React loads in the browser and "attaches" itself to the already present HTML, connecting events and hooks to make the page interactive without having to redraw it from scratch. This process is called Hydration.
The most widely used SSR frameworks with React are Next.js (the most popular) and Remix.
SSG (Static Site Generation)
With SSG, React does not run in the user's browser. It is used only during the compilation phase (the build), before the site even goes online. At that point the framework executes all React components, generates the corresponding HTML pages, and saves them as static files. When a visitor opens the site, their browser downloads ready-made HTML, without having to execute heavy JavaScript and without needing an active server.
It is like writing a book with professional typesetting software: the tool is sophisticated, but the final product is a PDF that anyone can open without having that software installed.
Main SSG tools: Gatsby, Astro, Next.js (with getStaticProps). Ideal for blogs, documentation, and marketing sites, meaning content that rarely changes.
RSC (React Server Components), the New Frontier
With SSR and SSG, the server generates the HTML but then still ships all the JavaScript needed to run React to the browser. React Server Components take it a step further: certain components are executed exclusively on the server and the browser receives only their HTML output, without a single line of their JavaScript code ever being downloaded by the user.
// This component lives only on the server
// The browser will never receive this code, only its HTML output
async function ArticleList() {
const articles = await readFromDatabase(); // direct database access, no API
return (
<ul>
{articles.map(a => <li key={a.id}>{a.title}</li>)}
</ul>
);
}
The main advantage is the reduction of the bundle size: the libraries that a server component imports (for example a 50 KB Markdown parser) stay on the server and never end up in the bundle the user downloads.
Practical Summary: React Is Agnostic
Pure SPA React + React Router (client-side navigation)
SSR Next.js or Remix (server generates the HTML)
SSG Next.js / Gatsby / Astro (build generates static HTML)
Hybrid React injected into an existing HTML page
Rule: React is not "always a SPA". The architecture depends on the project.
4. Virtual DOM and Reconciliation (The Architect's Blueprint)
With Vanilla JavaScript you manipulate the DOM directly: you select an element, change its text, add another one. React instead never touches the DOM directly. To understand why it chose this path, you need to understand how much each single DOM modification costs the browser.
Why Not Touch the DOM Directly?
Every DOM node (every <div>, every <p>, every <button>) is a JavaScript object with hundreds of properties inherited from the browser's prototype hierarchy. Modifying even one of these objects triggers a costly sequence:
- Reflow (Layout): the browser recalculates the dimensions and positions of all elements that might have been affected by the change
- Repaint: it redraws the pixels on screen in the affected area
If you make four consecutive changes, the browser might have to recalculate the layout four times. It is like moving a wall on a construction site: it is not enough to tear down the wall, you also have to recalculate where the pipes and electrical wires that ran through it go.
// POTENTIALLY SLOW, every line touches the real DOM
element1.textContent = newData.title; // Reflow + Repaint
element2.style.display = 'block'; // Reflow + Repaint
element3.classList.add('active'); // Reflow + Repaint
element4.setAttribute('disabled', true); // Reflow + Repaint
In complex apps with dozens of updates per second, this process can make the experience slow and janky.
The Virtual DOM (The Blueprint on Paper)
React solves this problem by placing an intermediate layer between your code and the real DOM: the Virtual DOM. It is a lightweight JavaScript representation of the DOM tree, a simple object that describes how the UI should look at a given moment.
// Simplified version of how React internally represents
// the tag <button className="red">Hello</button>
{
type: 'button',
props: { className: 'red', children: 'Hello' },
children: []
}
Creating or modifying this object is extremely fast because it happens entirely in RAM, without touching any pixel on the screen. The idea is the same as an architect who does not move walls on the real construction site every time they want to try a different layout: they move them first on the CAD blueprint, where changes are instant and free, and only when the design is final do they send workers to intervene on the real site.
Diffing (Spot the Differences)
Every time the state of your app changes, React performs four steps:
- It calls your component again (which is a function)
- The function returns a new Virtual DOM (the updated blueprint)
- React compares it with the old Virtual DOM (the previous blueprint)
- It identifies exactly what changed. This comparison is called Diffing
The mechanism is the same as the "spot the differences" game between two nearly identical images: React analyzes them in milliseconds and identifies that, for example, the only thing that changed is the text inside a button.
Old Virtual DOM: <button>0 likes</button>
New Virtual DOM: <button>1 like</button>
↑
Difference found: only the text!
The Result: Surgical Precision
Once the Diffing is complete, React holds the exact list of differences and goes to touch in the real DOM only the nodes that actually changed. Everything else on the page remains untouched, with no unnecessary reflows or repaints. Instead of demolishing an entire apartment to change a door handle, React sends a worker to replace only the handle.
Real DOM before: <button>0 likes</button>
↑
React touches ONLY the text "0 likes"
and replaces it with "1 like"
Real DOM after: <button>1 like</button>
As a developer, you never interact directly with the Virtual DOM. Your job is to write JSX that describes the desired UI, and React handles the entire process of comparing and efficiently updating the real DOM.
5. Declarative vs. Imperative (The Taxi Passenger)
This is the most important mental shift when you move from Vanilla JavaScript to React. It is not just about syntax: it changes the way you reason about code.
The Imperative Way (The Driving Instructor)
With Vanilla JavaScript, you write code in an imperative way: you describe every single step to execute, in the precise order. It is like a driving instructor who dictates every gesture, from the order in which to check the mirrors to the exact moment to press a pedal.
// IMPERATIVE WAY (Vanilla JavaScript)
// You have to describe HOW to do everything
function showMessage(text) {
// Step 1: find the element
const container = document.querySelector('#messages');
// Step 2: create the new element
const newDiv = document.createElement('div');
// Step 3: add the class
newDiv.classList.add('message');
// Step 4: insert the text
newDiv.textContent = text;
// Step 5: add to the DOM
container.appendChild(newDiv);
// Step 6: if there are too many messages, remove the oldest
if (container.children.length > 5) {
container.removeChild(container.firstChild);
}
}
If you forget a step the app breaks, and if the HTML structure changes you have to rewrite all the code that manipulates it.
The Declarative Way (The Taxi Passenger)
With React the reasoning flips. You write code in a declarative way: you describe the desired final state and React figures out how to get there on its own. It is like getting into a taxi and saying "Cathedral Square, please" without specifying which road to take. You give the destination, not the directions.
// DECLARATIVE WAY (React)
// You describe WHAT you want to see for each possible state
function MessageList({ messages }) {
return (
<div id="messages">
{messages.slice(-5).map(msg => (
<div key={msg.id} className="message">
{msg.text}
</div>
))}
</div>
);
}
Notice the difference? You do not tell React "create a div, add the class, remove the old one". You say "This is the UI I want to see when messages contains this data", and React takes care of comparing the current UI with the desired one, figuring out the differences, and updating the DOM accordingly.
Why It Changes Everything
The advantage of declarative thinking is not just aesthetic and about readability; rather, it means you cannot forget a step, precisely because you are not describing steps, you are describing the goal. The code becomes more readable: if (isLoggedIn) return <Welcome />; return <Login />; reads almost like a plain sentence. And most importantly, you stop thinking about the DOM. With React you no longer reason in terms of "select, modify, update" but in terms of "what data do I have and how should the UI look for this data?". The DOM becomes an implementation detail that React handles.
Rule: In React, your job is to describe the What (what the UI looks like in each state). React's job is to handle the How (how to get from one state to another in the DOM).
6. JSX (The Wolf in Sheep's Clothing)
JSX looks like HTML and behaves almost like HTML, but it is not. Understanding its true nature is essential to avoiding dozens of incomprehensible bugs.
JSX Is Not HTML (Under the Hood)
No browser in the world knows how to interpret JSX. Tags like <Navbar /> or expressions like {variable} are not part of any web standard. The JSX you write never reaches the browser: before that moment, a compiler like Babel (or the one built into Vite) translates it entirely into pure JavaScript calls.
In practice, every JSX tag is converted into a call to React.createElement().
// WHAT YOU WRITE (JSX)
const element = <h1 className="title">Hello {name}</h1>;
// WHAT THE COMPILER GENERATES (pure JavaScript)
const element = React.createElement(
'h1', // tag type
{ className: 'title' }, // the props (attributes)
'Hello ', // first child (text)
name // second child (variable)
);
JSX is therefore syntactic sugar: a notation visually similar to HTML that the compiler transforms into ordinary JavaScript code. It is the wolf (JavaScript) dressed as a sheep (HTML).
The Dimensional Portal { } (Static vs. Dynamic)
Curly braces { } in JSX work like a portal between two worlds. Everything you write outside the braces is static text, exactly like in traditional HTML. The moment you open a brace, you enter the JavaScript world and can insert any expression: variables, calculations, function calls, ternary operators.
const userName = "Mario";
const age = 25;
// Outside the braces: literal string
<p className="greeting">Welcome</p>
// Inside the braces: JavaScript expressions
<p>Hello, {userName}!</p> // variable
<p>You are {age} years old</p> // number
<p>In 10 years you will be {age + 10}</p> // calculation
<p>{age >= 18 ? "Adult" : "Minor"}</p> // ternary
The Crucial Distinction Between Strings and Expressions in Props
// Passes the literal string "2 + 2", no calculation!
<Child message="2 + 2" />
// Passes the result of the calculation: the number 4
<Child message={2 + 2} />
// Passes the value of the variable
<Child message={message} />
// Passes a boolean true (written as a prop without a value)
<Child active />
// is equivalent to:
<Child active={true} />
The Switch Trick (How Babel Reads JSX)
To understand how JSX and JavaScript can coexist in the same file, it helps to know how the compiler thinks. Babel starts in JavaScript mode and expects normal JS expressions. When it encounters a < followed by a letter, it understands that a tag is starting and switches to UI mode (interprets the markup). When it encounters a { inside the markup, it reopens the JavaScript portal to evaluate the expression. And so on, continuously alternating between the two modes.
This mechanism is what makes it possible to write code like the following, where markup and logic intertwine naturally.
function List({ items }) {
return (
<ul> {/* UI mode */}
{items.map(item => {/* JS portal */}
<li key={item.id}>{item.name}</li> {/* UI mode again */}
)}
</ul>
);
}
className Instead of class (The Reserved Word Problem)
In HTML you write <div class="container">, but in JSX you must write <div className="container">. The reason is that JSX transforms into JavaScript objects, and JSX attributes become keys of that object. The word class in JavaScript is reserved for declaring OOP Classes.
// In JavaScript, 'class' already has a precise meaning
class Person {
constructor(name) {
this.name = name;
}
}
If it were possible to write { class: "container" } in a JS object, the parser would not know whether you are defining a class or assigning a CSS style. JSX resolves the conflict by using className, which is exactly the name of the real DOM property that Vanilla JavaScript also uses.
// In Vanilla JS the property is also called 'className', not 'class'
document.querySelector('div').className = "container";
In other words, JSX speaks the language of the JavaScript DOM, not that of HTML.
// ❌ WRONG in JSX
<div class="container">...</div>
// ✅ CORRECT in JSX
<div className="container">...</div>
htmlFor Instead of for (The Same Problem)
The same problem arises with the for attribute on <label>. Since for in JavaScript is the reserved word for loops, in JSX you use htmlFor.
// ❌ WRONG in JSX
<label for="email">Your email:</label>
<input id="email" type="email" />
// ✅ CORRECT in JSX
<label htmlFor="email">Your email:</label>
<input id="email" type="email" />
This connection is essential for accessibility: when a user clicks on the label text, the browser automatically moves focus to the linked input. Without htmlFor, the user is forced to click exactly on the small text field.
The Parentheses Trap in return() (ASI)
You might write a component that returns undefined for no apparent reason. The cause is almost always ASI (Automatic Semicolon Insertion): a JavaScript mechanism that automatically inserts a semicolon at the end of certain lines. In particular, when the parser sees the word return followed by a newline, it assumes the return value is "nothing" and closes the function.
// ❌ TRAP, ASI inserts ';' right after 'return'
function Greeting() {
return // JavaScript reads: return; the function ends here!
<h1>Hello</h1>; // This code is never reached
}
// Result: the function returns 'undefined'
The solution is to open the parentheses on the same line as return. This way JavaScript understands that the expression is not yet finished and continues reading until the closing parenthesis.
// ✅ CORRECT, the parentheses hold everything together
function Greeting() {
return ( // JavaScript reads: return (... and waits for the closing parenthesis
<h1>Hello</h1>
);
}
Rule: as soon as the JSX in your return spans multiple lines, always use parentheses.
Fragment <>, the Invisible Shopping Bag
A React component is a function, and a JavaScript function can return only one value. This means you cannot return two sibling JSX elements directly.
// ❌ ERROR, you are trying to return two elements
function TwoHeadings() {
return (
<h1>Main Heading</h1>
<h2>Subheading</h2>
);
}
The instinctive solution is to wrap everything in a <div>, but this adds an extra node to the DOM that can break Flexbox or Grid layouts and complicate CSS selectors.
// Works, but "pollutes" the DOM with a useless div
function TwoHeadings() {
return (
<div>
<h1>Main Heading</h1>
<h2>Subheading</h2>
</div>
);
}
The Fragment solves the problem cleanly: it groups the elements to satisfy the "single return value" rule, but once in the browser it disappears completely without leaving a trace in the DOM. It is like an invisible shopping bag that groups the products for transport and then dissolves.
// ✅ CORRECT, the Fragment groups without adding nodes to the DOM
function TwoHeadings() {
return (
<>
<h1>Main Heading</h1>
<h2>Subheading</h2>
</>
);
}
The short syntax <>...</> covers the vast majority of cases. The only exception is when you need to pass a key prop (in lists): the short syntax does not accept attributes, so the explicit form <React.Fragment key={...}> is needed.
// The long syntax is needed only when you need key
{items.map(item => (
<React.Fragment key={item.id}>
<dt>{item.term}</dt>
<dd>{item.definition}</dd>
</React.Fragment>
))}
Double Braces {{ }}, It Is Not Special Syntax
Seeing style={{ color: 'red' }} for the first time, you might think that {{ }} is special React syntax for styles. In reality it is simply two constructs layered on top of each other: the first brace { opens the JavaScript portal (as we saw earlier), and the second brace { begins a normal JavaScript object literal. The visual result is two consecutive braces, but conceptually it is an object inside a portal.
// These two notations are identical
// Compact notation (the "double braces")
<div style={{ backgroundColor: 'red', fontSize: 16 }}>...</div>
// Explicit notation (clearer for understanding what happens)
const myStyle = { backgroundColor: 'red', fontSize: 16 };
<div style={myStyle}>...</div>
The same logic applies every time you pass an object as a prop.
<Component config={{ theme: 'dark', language: 'en' }} />
// { outer = opens the JS portal
// { inner = start of the JS object
// } inner = end of the JS object
// } outer = closes the portal
Security by Default (dangerouslySetInnerHTML)
JSX has a built-in protection against XSS (Cross-Site Scripting) attacks. When you insert a value inside { }, React sanitizes it automatically before rendering: it converts potentially dangerous characters like <, >, ", and & into safe HTML entities. This means that even if a malicious user inserts harmful code into a text field, React turns it into harmless text.
const userInput = "<script>alert('Hacked!')</script>";
// React sanitizes automatically, no danger
<p>{userInput}</p>
// The browser literally shows: <script>alert('Hacked!')</script>
// The script is NOT executed
The only way to bypass this protection is to use the dangerouslySetInnerHTML prop, whose name is intentionally scary. React wants you to be aware that you are disabling the security protections every time you use it.
// ⚠️ DANGEROUS, use only with HTML you control 100%
<div dangerouslySetInnerHTML={{ __html: trustedServerHtml }} />
Rule: Use dangerouslySetInnerHTML only for HTML generated by yourself or by a trusted system like a CMS that sanitizes content on its own. Never with user input or data from unverified sources.