React Finally Made Sense — Part 1
Chapter 1: The Core Philosophy & The Ecosystem
React Finally Made Sense — Part 1
Chapter 1: The Core Philosophy & The Ecosystem
Estimated reading time: 18–22 minutes
Section 1 — Why I Started Learning React (And Why It Felt So Confusing)
If you search “Learn React” on YouTube today, you’ll probably find hundreds of videos.
Some start with Hooks.
Some jump directly into Components.
Others begin by creating a project using Vite.
And before you know it, you’ve written your very first React component without even understanding why React exists.
That was exactly my experience.
I kept writing code like this:
function App() {
return <h1>Hello World</h1>;
}
It worked.
But I had absolutely no idea what was happening behind the scenes.
Everyone kept saying things like:
“React is fast.”
“React uses Virtual DOM.”
“React re-renders.”
“React uses JSX.”
Cool…
But every new concept raised another question in my mind.
- Why is React fast?
- What exactly is Virtual DOM?
- Why do we even need JSX?
- Couldn’t we build websites using plain HTML, CSS and JavaScript?
Instead of finding answers, most tutorials simply asked me to memorize things.
And I hate memorizing.
I wanted to understand.
Once I started asking “Why?” instead of “How?”, React slowly started making sense.
This article is a collection of those “Why?” moments.
So instead of blindly writing code, let’s first understand why React was created in the first place.
Every technology exists to solve a problem.
Think about it.
Nobody wakes up one morning and decides,
“Let’s create another JavaScript library for fun.”
A technology becomes popular only because it solves a problem that people were actually facing.
React is no different.
So before learning React,
let’s first understand the problem.
Imagine building a website using only HTML, CSS and JavaScript.
Suppose you’re building a simple Restaurant website.
Initially,
your page looks something like this:
Header
Restaurant Cards
Footer
Pretty straightforward.
Now your client says,
“I want a Search Bar.”
No problem.
You add one.
A few days later…
“Can we also filter Top Rated Restaurants?”
Sure.
Then…
“Let’s add Login.”
Done.
Then…
“Can we show Live Offers?”
Okay…
Then…
“Can we fetch restaurants from an API instead of hardcoding them?”
Now things start becoming interesting.
Because suddenly,
your page isn’t static anymore.
It’s changing all the time.
The restaurant list changes.
The offers change.
The login button changes.
The search results change.
Everything is dynamic.
Now ask yourself a simple question.
Whenever something changes on the page…
How should JavaScript update the UI?
The Problem
Imagine there are 100 restaurant cards on your screen.
You type a single letter inside the search bar.
Only 15 restaurants should remain visible.
Should JavaScript
- redraw the entire page?
or
- update only those restaurant cards that actually changed?
Obviously,
the second approach is much smarter.
Because why waste time rebuilding something that hasn’t changed?
This exact problem becomes much bigger in applications like
- Swiggy
- Netflix
where hundreds of things are changing every second.
Updating the entire page again and again would be unnecessarily expensive.
There had to be a better way.
That’s one of the biggest reasons React became so popular.
We’ll understand how React solves this problem in the next chapter.
For now, just remember the problem it was trying to solve:
Efficiently updating the UI whenever data changes.
Everything else in React eventually revolves around this idea.
But wait…
Another question came into my mind.
If React is just JavaScript…
then why do we need React at all?
Can’t JavaScript already change the DOM?
The answer is…
Yes.
JavaScript can absolutely manipulate the DOM.
For example:
const heading = document.createElement("h1");
heading.innerText = "Hello World";
document.body.appendChild(heading);
This works perfectly.
Now imagine writing an entire Swiggy clone like this.
Creating every card manually.
Updating every button manually.
Removing every element manually.
Finding every element using:
document.getElementById()
Updating styles manually.
Handling hundreds of DOM operations yourself.
It quickly becomes difficult to maintain.
Not because JavaScript is weak.
But because manually managing the DOM at scale becomes painful.
React doesn’t replace JavaScript.
React simply gives us a much better way to work with it.
That’s an important distinction.
A Small Mindset Shift
When I first started learning React,
I used to think
React builds websites.
It doesn’t.
The browser still builds the website.
JavaScript still runs the code.
The DOM is still the DOM.
React simply sits in between.
You can think of React as an intelligent manager.
Instead of directly telling the browser
Create this.
Remove that.
Move this.
Update this.
React first figures out
What actually changed?
Then it tells the browser to update only what’s necessary.
We’ll eventually learn about
- Virtual DOM
- Reconciliation
- React Fiber
and all the fancy internal terms.
But remember,
all of those concepts exist to solve this one problem:
Update as little as possible while keeping the UI in sync with the data.
That’s the philosophy behind React.
One More Thing Before We Continue
When I started React, I thought the first thing I needed to learn was JSX.
It wasn’t.
Even before writing a single React component, there’s an entire ecosystem working behind the scenes.
Questions like:
- How does React even run in the browser?
- What is Vite?
- What is Parcel?
- Why do we install packages with npm?
- What is
node_modules? - Why are there thousands of folders inside it?
- What is
package.jsonactually doing?
These aren’t “advanced” topics.
They’re the foundation.
Understanding them made the rest of React feel much less mysterious.
So before we write our first component, let’s build that foundation.
In the next section, we’ll answer a question that confused me for a long time:
If browsers already understand HTML, CSS, and JavaScript… why do we need tools like Vite, Parcel, or Webpack before we can even start building a React app?
Section 2 — The Builders We Never See: Bundlers & Dependencies
When we write a standard HTML website, how do we load React?
Usually, tutorials tell you to copy-paste CDN links into your <head> tag.
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
I did that too.
But soon I realized: CDN links are not a good practice.
Why?
- What if the CDN server goes down?
- What if your user’s internet is slow and fetching those external scripts takes forever?
- What if you want to use modern JavaScript tools?
So we remove those CDN links.
Instead, we install React directly onto our computer.
How?
By running:
npm install react
npm install react-dom
This brings us to a massive folder that instantly appears in your project directory:
**node_modules**
The Mystery of node_modules & Transitive Dependencies
If you check your project folder after installing just two packages, you will notice something scary.
There are literally thousands of folders inside node_modules.
Why?
I only installed React and React-DOM! Where did all these other folders come from?
The answer lies in a concept called Transitive Dependencies.
Think of it like this:
Our project is dependent on React.
But React (or tools like Parcel) is also dependent on other code libraries to do its job.
And those libraries are dependent on even more libraries!
Basically, dependencies ki apni dependencies hoti hain, aur unki bhi apni dependencies hoti hain.
So when you run npm install, NPM downloads the entire family tree of code.
That’s why node_modules becomes so huge.
But how does NPM track this massive family tree?
package.json vs. package-lock.json (The Contract)
To manage this madness, NPM creates two files in your directory:
package.jsonpackage-lock.json
What is the difference?
Let’s explain it simply.
1. package.json
Think of package.json as a grocery list. It specifies what dependencies you need, but not the exact version.
It might say:
"dependencies": {
"react": "^18.2.0"
}
Notice that little caret (^) symbol before the version number.
What does that mean?
It means: “I am compatible with React version 18. If anyone installs this project later, fetch the latest minor version of React (like 18.2.5 or 18.3.0).”
This is great, but it has a problem.
What if a newer version of React has a bug? If someone else downloads your code tomorrow, their app might break because they fetched a newer, buggy version.
To solve this, NPM uses the second file.
2. package-lock.json
This file is like a detailed receipt. It notes down the exact version of every single dependency and transitive dependency at the moment they were installed.
If you installed React at version 18.2.0, package-lock.json locks it down to 18.2.0.
Now, when you or anyone else downloads the project and runs npm install, NPM looks at package-lock.json and fetches the exact same version.
This ensures your app behaves consistently, no matter who is running it.
NOTE: Always commit both
package.jsonandpackage-lock.jsonto GitHub!
Since they hold the list of all dependencies, you never push the giant
node_modulesfolder to GitHub (we put it in.gitignore). Anyone who downloads your project can simply run:
*npm install*
and NPM will automatically recreate the entire
node_modulesfolder from scratch.
Normal Dependencies vs. Dev Dependencies
When you install packages, you’ll see developers run commands in two different ways:
npm install react(Normal Dependency)npm install -D parcel(Dev Dependency)
What’s the difference?
- Normal Dependencies: These are required for your app to run in production (like React itself). Your app won’t function without them in the browser.
- Dev Dependencies (
-D): These are only required during development. For example, a tool likeParcelhelps us compile our code locally. But once the website is built and pushed to the internet, we don't need Parcel anymore. It's only for the developer's computer.
Okay, but what is a Bundler?
We talked about Webpack, Parcel, and Vite.
What do they actually do?
Imagine you have written 50 different JavaScript files, 10 CSS files, and imported a bunch of images.
The browser cannot load 50 different files one by one efficiently. It would make 50 separate network requests, which is super slow.
This is where a Bundler comes in.
A bundler takes all those scattered files, packages them up, and cleans them. It generates a single, optimized JS file and a single CSS file that are ready to be pushed into production.
Section 3 — Under the Hood of a Bundler: Caching, HMR, and Browserslist
Let’s focus on Parcel for a moment (though Vite does something very similar).
Once you run npm start and ignite your project, you'll notice something amazing.
You change a line of code, press save, and boom!
The changes show up in the browser instantly. You don’t even need to refresh the page.
How does this happen?
Hot Module Replacement (HMR)
Parcel keeps a constant watch on your files.
It does this using a File Watching Algorithm (which is written in C++ for maximum speed).
As soon as you save a file, the algorithm detects the change and triggers HMR (Hot Module Replacement).
Instead of reloading the entire webpage (which would wipe out your app’s state, like text in an input box), Parcel injects only the updated module directly into the running browser.
It’s like replacing a flat tire while the car is still driving!
Why are subsequent builds faster?
The first time you start Parcel, it might take a few seconds.
But the second time? It starts in milliseconds.
How?
Parcel caches everything. It creates a .parcel-cache folder in your project.
It saves a processed copy of your files. When you restart the project, it only processes the files you actually changed, fetching the rest directly from the cache.
Making it run on old browsers: browserslist
If you look at modern JavaScript, we use fancy new syntax. But what if your user is still running an old version of Safari or Internet Explorer?
You can tell your bundler to make the app compatible by adding this to your package.json:
"browserslist": [
"last 2 versions"
]
What does this do?
It tells the bundler to compile your code in a way that it is guaranteed to run on the last two versions of all major browsers.
TIP: While this is great for compatibility, it can make your build slightly slower. The bundler has to inject extra polyfills and code wrappers to support those older browsers.
Section 4 — The JSX Transpilation Lie
Now, let’s talk about the biggest lie in React: JSX.
We write code like this:
const heading = <h1 id="title">Hello from JSX!</h1>;
It looks like HTML inside JavaScript.
But wait.
Is this valid JavaScript?
Absolutely not.
If you copy-paste this line directly into a browser console, it will throw a syntax error. The JS engine inside Chrome or Safari has no idea what this is.
So, how does React run it?
Enter Babel (The Transpiler)
React doesn’t read your JSX.
Before your code reaches the browser, a tool called Babel (which is automatically shipped inside bundlers like Parcel/Vite) intercepts your code.
Babel is a transpiler. It translates modern JSX code into old-school JavaScript code that browsers understand.
Babel takes your JSX:
const heading = <h1 id="title">Hello from JSX!</h1>;
And transpiles it into:
const heading = React.createElement("h1", { id: "title" }, "Hello from JSX!");
Now this is valid JavaScript!
So the actual flow of JSX rendering is:
JSX Code
↓ (Transpiled by Babel)
React.createElement()
↓ (Executes)
React Element (which is just a plain JS Object!)
↓ (Rendered by ReactDOM)
Actual HTML Element in the DOM
To prove it, if you console.log a JSX element like this:
const heading = <h1 id="title">Hello</h1>;
console.log(heading);
You won’t see HTML in the console.
You will see a JavaScript Object with keys like type: "h1", props: { id: "title", children: "Hello" }.
Writing JavaScript inside JSX
Since JSX eventually becomes JavaScript, React allows you to write actual JS directly inside your JSX using curly braces {}.
Anything inside {} will run as normal JavaScript code:
const name = "Kushal";
const element = <h1>Hello, {name}!</h1>; // Outputs: Hello, Kushal!
You can even run expressions:
const element = <h2>Total: {10 + 20}</h2>; // Outputs: Total: 30
Section 5 — Organizing the Chaos: Project Structure & Imports
Now that we have removed the CDN links and installed React via NPM, we need to import them into our JS files.
At the very top of our App.js, we write:
import React from "react";
import ReactDOM from "react-dom/client";
But as your app grows, you can’t keep writing all your components in a single App.js file. It will become a mess.
We need structure.
Standard React Folder Structure
Usually, we structure our React projects like this:
- Create a
src(source) folder at the root. - All components go into a
src/components/folder. - Rule: The name of the file should start with a capital letter and match the component name exactly (e.g.,
Header.js,RestaurantCard.js). - All external data, configurations, and assets go into a
src/utils/(utilities) folder.
Default vs. Named Imports/Exports
When you split your components into separate files, you need to export them from their files so they can be imported elsewhere.
There are two ways to do this in ES6:
Type When to use Syntax (Export) Syntax (Import) Default Exporting only one component/thing from a file. export default Header; import Header from "./components/Header"; Named Exporting multiple things from the same file. export const LOGO_URL = "..."; import { LOGO_URL } from "../utils/constants";
Gotcha: When importing a default export, you do not use curly braces
{}. But when importing a named export, you must wrap it in{}.
Let’s look at how we destructure props in these clean files.
Suppose we are making a Restaurant Card component.
Instead of writing:
const RestaurantCard = (props) => {
return (
<div className="card">
<h4>{props.resName}</h4>
<h5>{props.location}</h5>
</div>
);
};
Many developers prefer to destructure props on the fly:
const RestaurantCard = ({ resName, location }) => {
return (
<div className="card">
<h4>{resName}</h4>
<h5>{location}</h5>
</div>
);
};
Or destructure inside the component function:
const RestaurantCard = (props) => {
const { resName, location } = props;
return (
<div className="card">
<h4>{resName}</h4>
<h5>{location}</h5>
</div>
);
};
This keeps our component code extremely clean.
Config-Driven UI: Making Apps Smart
Here is a neat industry concept: Config-Driven UI.
What does that mean?
It means your user interface is driven by a data configuration (usually a JSON API response from the backend).
For example, suppose you are building Swiggy.
You want to show “50% Off” banner cards to users in Delhi, but “Free Delivery” banners to users in Mumbai.
Would you write two different websites?
No.
You make the UI config-driven. The backend checks the user’s location and sends a specific JSON configuration. React reads this config and dynamically renders the matching UI.
It looks something like this:
const promoConfig = [
{ id: "1", type: "discount", title: "50% Off in Delhi" },
{ id: "2", type: "delivery", title: "Free Delivery in Mumbai" }
];
By coding components to adapt to dynamic objects, your frontend becomes completely flexible.
Chapter 1 Summary
- Why React? Raw JS DOM manipulation (
document.createElement) is painful and slow to maintain at scale. React is like an intelligent manager that calculates exactly what changed and updates only that. - The Ecosystem: We install React locally via
npmto avoid unstable CDNs. - The Contract:
package.jsontracks approximate dependency versions using caret (^), whilepackage-lock.jsonlocks down the exact version for consistency across systems. - Transitive Dependencies: Packages like Parcel have their own dependencies, which have their own dependencies, creating the giant family tree in
node_modules. - The Bundler & HMR: Bundlers compile our assets. Parcel uses caching and a C++ File Watcher to perform Hot Module Replacement (HMR), updating the browser without reloading the page.
- JSX is a Transpiler Lie: Browsers cannot read JSX. Babel transpiles it into
React.createElementcalls, which return JavaScript objects representing the elements. - Module Imports: We use Default exports for single files and Named exports (wrapped in
{}) for exporting multiple variables/constants.
The Transition Question
Our build system is running. We are writing clean JSX components, structuring our folders, and destructuring props.
But what happens when our restaurant list updates?
If our list of cards changes dynamically (like searching for “Pizza”), how does React know which specific restaurant card to update, delete, or add?
And what happens if we don’t help React identify them?
In the next chapter, we will look under the hood at the Virtual DOM, dive into Reconciliation, and find out why rendering lists without a unique key is a ticking time bomb.
메타데이터
- post_id
- ea31793c6ceb
- slug
- react-finally-made-sense-part-1-ea31793c6ceb
- url
- https://medium.com/@kushagradpr2005/react-finally-made-sense-part-1-ea31793c6ceb
- canonical_url
- https://medium.com/@kushagradpr2005/react-finally-made-sense-part-1-ea31793c6ceb
- author_url
- https://medium.com/@kushagradpr2005
- status
- ok
- fetched_at
- 2026-07-18 00:21:44