Learning JS frameworks with me(part 4): React.js(1/3)-Getting Started with the UI Library That Changed the Web
After exploring vanilla JavaScript, jQuery, angular.js and Vue.js in our journey through frontend frameworks, it's time to tackle the elephant in the room: React. As one of the most influential and widely-used UI libraries in the world, React deserves our undivided attention—which is why I'm dedicating not one, but three blog posts to mastering it. When I first approached React, I'll admit I was intimidated. Coming from jQuery and even Vue, React's component-based architecture and its JSX syntax looked alien. Where were my familiar templates? Why was I writing HTML inside JavaScript? Yet, as I pushed through that initial confusion, I discovered a mental model that fundamentally changed how I approach building user interfaces. In this first installment of our React mini-series, we'll focus on the essentials: setting up React, understanding components, and building a simple version of our task application. Let's begin this exciting chapter of our learning journey together. React's Rise to Dominance Created by Jordan Walke at Facebook in 2011 and released to the public in 2013, React was built to solve a specific problem: building large applications with data that changes over time. Traditional DOM manipulation became unwieldy as Facebook's UI grew more complex, so they needed a new approach. React introduced a revolutionary idea: what if we treated our UI as a function of state? Instead of manually manipulating the DOM when data changes, we could simply describe what the UI should look like for any given state, and let React handle the updates. This paradigm shift, combined with React's component-based architecture, propelled it to become the most popular frontend library in the world. Today, React powers millions of websites and applications, from small personal projects to giants like Facebook, Instagram, Netflix, and Airbnb. Why React Matters in 2025 Despite being over a decade old—an eternity in the JavaScript ecosystem—React continues to dominate frontend development for several compelling reasons: Component-Based Architecture: React's component model has proven to be an elegant and scalable way to build UIs, influencing virtually every framework that followed. Massive Ecosystem: With over 3 million packages on npm, countless tutorials, and robust tooling, React has the largest ecosystem of any frontend technology. Career Opportunities: React consistently tops the list of most in-demand frontend skills, with more job listings than any other JavaScript framework. Future-Proof Investment: React's core principles have remained stable despite its evolution, making it a safe long-term investment for developers and companies alike. Meta's Commitment: With continued investment from Meta (formerly Facebook), React remains actively developed with regular improvements. React Native: Learning React opens the door to mobile app development with React Native, allowing you to leverage your skills across platforms. React's Core Philosophy Before diving into code, it's crucial to understand React's fundamental philosophy, which can be summarized in a few key principles: Declarative UI: Describe what your UI should look like, not how to change it. Component-Based: Build encapsulated components that manage their own state, then compose them to make complex UIs. Learn Once, Write Anywhere: React's core concepts transfer across platforms (web, mobile, desktop). Unidirectional Data Flow: Data flows down from parent to child components. Virtual DOM: An abstraction of the DOM that allows React to update only what needs to change. These principles might seem abstract now, but they'll become clearer as we put them into practice. Setting Up Your First React Project For this first post, we'll use the simplest approach to get started with React—using script tags in an HTML file. In later posts, we'll explore more sophisticated setups with build tools. Step 1: Create Your Project Structure react-project/ ├── index.html ├── css/ │ └── style.css └── js/ └── main.js Step 2: Set Up Your HTML File React Task List My React Task App Step 3: Use The Same CSS To maintain visual consistency with our previous examples, we'll use the same CSS styles from our earlier projects. Step 4: Writing Your First React Component Now for the exciting part—let's write our first React code: // js/main.js // Simple Task App - First Version function App() { // State using React Hooks const [tasks, setTasks] = React.useState([]); const [newTask, setNewTask] = React.useState(''); // Effect Hook for localStorage React.useEffect(() => { const savedTasks = JSON.parse(localStorage.getItem('tasks')) || []; setTasks(savedTasks); }, []); // Save tasks to l

After exploring vanilla JavaScript, jQuery, angular.js and Vue.js in our journey through frontend frameworks, it's time to tackle the elephant in the room: React. As one of the most influential and widely-used UI libraries in the world, React deserves our undivided attention—which is why I'm dedicating not one, but three blog posts to mastering it.
When I first approached React, I'll admit I was intimidated. Coming from jQuery and even Vue, React's component-based architecture and its JSX syntax looked alien. Where were my familiar templates? Why was I writing HTML inside JavaScript? Yet, as I pushed through that initial confusion, I discovered a mental model that fundamentally changed how I approach building user interfaces.
In this first installment of our React mini-series, we'll focus on the essentials: setting up React, understanding components, and building a simple version of our task application. Let's begin this exciting chapter of our learning journey together.
React's Rise to Dominance
Created by Jordan Walke at Facebook in 2011 and released to the public in 2013, React was built to solve a specific problem: building large applications with data that changes over time. Traditional DOM manipulation became unwieldy as Facebook's UI grew more complex, so they needed a new approach.
React introduced a revolutionary idea: what if we treated our UI as a function of state? Instead of manually manipulating the DOM when data changes, we could simply describe what the UI should look like for any given state, and let React handle the updates.
This paradigm shift, combined with React's component-based architecture, propelled it to become the most popular frontend library in the world. Today, React powers millions of websites and applications, from small personal projects to giants like Facebook, Instagram, Netflix, and Airbnb.
Why React Matters in 2025
Despite being over a decade old—an eternity in the JavaScript ecosystem—React continues to dominate frontend development for several compelling reasons:
Component-Based Architecture: React's component model has proven to be an elegant and scalable way to build UIs, influencing virtually every framework that followed.
Massive Ecosystem: With over 3 million packages on npm, countless tutorials, and robust tooling, React has the largest ecosystem of any frontend technology.
Career Opportunities: React consistently tops the list of most in-demand frontend skills, with more job listings than any other JavaScript framework.
Future-Proof Investment: React's core principles have remained stable despite its evolution, making it a safe long-term investment for developers and companies alike.
Meta's Commitment: With continued investment from Meta (formerly Facebook), React remains actively developed with regular improvements.
React Native: Learning React opens the door to mobile app development with React Native, allowing you to leverage your skills across platforms.
React's Core Philosophy
Before diving into code, it's crucial to understand React's fundamental philosophy, which can be summarized in a few key principles:
- Declarative UI: Describe what your UI should look like, not how to change it.
- Component-Based: Build encapsulated components that manage their own state, then compose them to make complex UIs.
- Learn Once, Write Anywhere: React's core concepts transfer across platforms (web, mobile, desktop).
- Unidirectional Data Flow: Data flows down from parent to child components.
- Virtual DOM: An abstraction of the DOM that allows React to update only what needs to change.
These principles might seem abstract now, but they'll become clearer as we put them into practice.
Setting Up Your First React Project
For this first post, we'll use the simplest approach to get started with React—using script tags in an HTML file. In later posts, we'll explore more sophisticated setups with build tools.
Step 1: Create Your Project Structure
react-project/
├── index.html
├── css/
│ └── style.css
└── js/
└── main.js
Step 2: Set Up Your HTML File
lang="en">
charset="UTF-8">
name="viewport" content="width=device-width, initial-scale=1.0">
React Task List
rel="stylesheet" href="css/style.css">
My React Task App
class="todo-app">
id="app">
Step 3: Use The Same CSS
To maintain visual consistency with our previous examples, we'll use the same CSS styles from our earlier projects.
Step 4: Writing Your First React Component
Now for the exciting part—let's write our first React code:
// js/main.js
// Simple Task App - First Version
function App() {
// State using React Hooks
const [tasks, setTasks] = React.useState([]);
const [newTask, setNewTask] = React.useState('');
// Effect Hook for localStorage
React.useEffect(() => {
const savedTasks = JSON.parse(localStorage.getItem('tasks')) || [];
setTasks(savedTasks);
}, []);
// Save tasks to localStorage whenever they change
React.useEffect(() => {
localStorage.setItem('tasks', JSON.stringify(tasks));
}, [tasks]);
// Event Handlers
const handleInputChange = (e) => {
setNewTask(e.target.value);
};
const handleAddTask = (e) => {
e.preventDefault();
if (newTask.trim() === '') return;
const task = {
id: Date.now(),
text: newTask,
completed: false
};
setTasks([...tasks, task]);
setNewTask('');
};
const handleToggleTask = (id) => {
const updatedTasks = tasks.map(task =>
task.id === id ? { ...task, completed: !task.completed } : task
);
setTasks(updatedTasks);
};
const handleDeleteTask = (id) => {
const filteredTasks = tasks.filter(task => task.id !== id);
setTasks(filteredTasks);
};
// Render the UI
return (
<div>
<h2>Task List</h2>
{/* Task input form */}
<form onSubmit={handleAddTask}>
<input
type="text"
value={newTask}
onChange={handleInputChange}
placeholder="Add a new task..."
required
/>
<button type="submit">Add Task</button>
</form>
{/* Task list */}
<ul id="task-list">
{tasks.map(task => (
<li key={task.id} data-id={task.id}>
<span
className={task.completed ? 'completed' : ''}
onClick={() => handleToggleTask(task.id)}
>
{task.text}
</span>
<button
className="delete-btn"
onClick={() => handleDeleteTask(task.id)}
>
Delete
</button>
</li>
))}
</ul>
</div>
);
}
// Mount our App component to the DOM
const root = ReactDOM.createRoot(document.getElementById('app'));
root.render(<App />);
React vs. Previous Approaches
Now that we've implemented our task app in React, let's compare it with our jQuery and Vue implementations to highlight the key differences.
1. Component-Based Architecture
Unlike jQuery, which operates directly on DOM elements, React organizes code into components—self-contained pieces that combine markup and logic.
jQuery Approach:
// Separate concerns by function
function renderTasks() { /* ... */ }
function handleAddTask() { /* ... */ }
React Approach:
// Everything related to a component stays together
function App() {
// State
const [tasks, setTasks] = React.useState([]);
// Event handlers
const handleAddTask = () => { /* ... */ };
// Return JSX (the UI)
return (
<div>
{/* ... */}
</div>
);
}
2. JSX: JavaScript + XML
React uses JSX, which allows you to write HTML-like syntax directly within JavaScript. This might look strange at first, but it creates a much tighter connection between your markup and logic.
Vue Template Approach:
v-for="task in tasks" :key="task.id">
{{ task.text }}
React JSX Approach:
return (
<div>
<ul>
{tasks.map(task => (
<li key={task.id}>{task.text}li>
))}
ul>
div>
);
3. State Management
React's approach to state is explicit and immutable. Rather than modifying state directly, you replace it with a new value using the setter function.
jQuery Approach:
// Directly modify the array
tasks.push(newTask);
renderTasks(); // Manually trigger re-render
React Approach:
// Create a new array with the new task
setTasks([...tasks, newTask]); // Automatically triggers re-render
4. Event Handling
React's event system is more consistent and closer to standard DOM events than jQuery's.
jQuery Approach:
$('#task-list').on('click', '.delete-btn', function() {
// need to find the ID from the parent
const taskId = $(this).parent().data('id');
// ...
});
React Approach:
<button
className="delete-btn"
onClick={() => handleDeleteTask(task.id)}
>
Delete
button>
Key React Concepts for Beginners
Let's delve deeper into some fundamental React concepts that will form the foundation of your React journey:
1. Components and Props
Components are the building blocks of React applications. There are two types:
Function Components (what we used in our example):
function Greeting(props) {
return <h1>Hello, {props.name}!h1>;
}
Class Components (older style, still used in legacy code):
class Greeting extends React.Component {
render() {
return <h1>Hello, {this.props.name}!h1>;
}
}
Props are how data flows from parent to child components:
// Parent component passes props
<Greeting name="Sarah" />
// Child component receives props
function Greeting(props) {
// Can access props.name
return <h1>Hello, {props.name}!h1>;
}
2. State and Hooks
State represents data that changes over time in your components. In modern React, we manage state with Hooks:
function Counter() {
// useState returns current state and a function to update it
const [count, setCount] = React.useState(0);
return (
<div>
<p>You clicked {count} timesp>
<button onClick={() => setCount(count + 1)}>
Click me
button>
div>
);
}
Other important hooks include:
- useEffect: Handle side effects (like data fetching or subscriptions)
- useContext: Access context data
- useRef: Reference DOM elements or persist values without causing re-renders
3. JSX Rules and Gotchas
JSX looks like HTML but has some important differences:
- Use
className
instead ofclass
(asclass
is a reserved word in JavaScript) - All tags must be closed (including self-closing tags like
)
- Expressions go inside curly braces:
{expression}
- Style attributes take an object, not a string:
style={{ color: 'red' }}
- You must return a single root element (or use a fragment:
<>>
)
4. Rendering Lists
When rendering lists in React, each item needs a unique key
prop to help React identify which items have changed:
function TaskList({ tasks }) {
return (
<ul>
{tasks.map(task => (
<li key={task.id}>{task.text}li>
))}
ul>
);
}
5. The Virtual DOM
React's performance comes from its implementation of a Virtual DOM—a lightweight copy of the actual DOM. When your state changes, React:
- Creates a new Virtual DOM tree
- Compares it with the previous one (diffing)
- Calculates the minimum changes needed
- Updates only those parts of the real DOM
This process, called reconciliation, is what makes React so efficient at updating the UI.
Breaking Down Our Task App
Now that we understand the basics, let's improve our task app by breaking it into smaller, reusable components—a core React best practice.
// Task component for rendering individual tasks
function Task({ task, onToggle, onDelete }) {
return (
<li data-id={task.id}>
<span
className={task.completed ? 'completed' : ''}
onClick={() => onToggle(task.id)}
>
{task.text}
span>
<button
className="delete-btn"
onClick={() => onDelete(task.id)}
>
Delete
button>
li>
);
}
// TaskForm component for adding new tasks
function TaskForm({ newTask, setNewTask, addTask }) {
return (
<form onSubmit={addTask}>
<input
type="text"
value={newTask}
onChange={(e) => setNewTask(e.target.value)}
placeholder="Add a new task..."
required
/>
<button type="submit">Add Taskbutton>
form>
);
}
// Main App component
function App() {
const [tasks, setTasks] = React.useState([]);
const [newTask, setNewTask] = React.useState('');
// Load tasks from localStorage (same as before)
React.useEffect(() => {
const savedTasks = JSON.parse(localStorage.getItem('tasks')) || [];
setTasks(savedTasks);
}, []);
// Save to localStorage when tasks change (same as before)
React.useEffect(() => {
localStorage.setItem('tasks', JSON.stringify(tasks));
}, [tasks]);
const handleAddTask = (e) => {
e.preventDefault();
if (newTask.trim() === '') return;
const task = {
id: Date.now(),
text: newTask,
completed: false
};
setTasks([...tasks, task]);
setNewTask('');
};
const handleToggleTask = (id) => {
setTasks(tasks.map(task =>
task.id === id ? { ...task, completed: !task.completed } : task
));
};
const handleDeleteTask = (id) => {
setTasks(tasks.filter(task => task.id !== id));
};
return (
<div>
<h2>Task Listh2>
<TaskForm
newTask={newTask}
setNewTask={setNewTask}
addTask={handleAddTask}
/>
<ul id="task-list">
{tasks.map(task => (
<Task
key={task.id}
task={task}
onToggle={handleToggleTask}
onDelete={handleDeleteTask}
/>
))}
ul>
div>
);
}
// Mount our App component to the DOM
const root = ReactDOM.createRoot(document.getElementById('app'));
root.render(<App />);
This component-based structure provides several benefits:
- Improved Readability: Each component has a clear, focused responsibility
- Reusability: Components can be reused in different parts of the app
- Maintainability: Easier to update or fix specific functionality
- Testing: Smaller components are easier to unit test
Using a Modern React Setup
While the script tag approach is great for learning, real-world React development typically uses build tools like Create React App, Vite, or Next.js. In our next post, we'll explore these options in depth, but here's a preview of how you'd set up a project with Vite:
# Create a new project with Vite
npm create vite@latest my-react-app -- --template react
# Navigate to the project directory
cd my-react-app
# Install dependencies
npm install
# Start the development server
npm run dev
This gives you:
- Hot module replacement for instant feedback
- JSX compilation without browser Babel
- Modern JavaScript features without polyfills
- Optimized production builds
- Component file organization
Where to Go From Here
We've covered a lot of ground in this first React post, but we've only scratched the surface. Here's what's coming in the next two installments:
Part 2: Advanced Component Patterns & State Management
- Component lifecycle and useEffect patterns
- Context API for state sharing
- React Router for navigation
- Form handling and validation
- Custom hooks
Part 3: Building Production-Ready React Applications
- State management with Redux or Zustand
- Styling solutions (CSS-in-JS, Tailwind, etc.)
- Performance optimization
- Testing React components
- Deployment strategies
Conclusion: Beginning Your React Journey
Learning React is a journey, not a destination. Don't worry if some concepts feel unclear at first—they'll solidify as you continue building and experimenting. The component model and declarative approach may require a mental shift, especially if you're coming from jQuery, but this investment will pay dividends throughout your development career.
What I've found most powerful about React isn't just its technical capabilities, but how it encourages a component-based mindset that improves how you structure all your front-end code. Even when I'm working with other frameworks or vanilla JS, the lessons from React about isolation, reusability, and state management influence my approach.
In the next post, we'll dive deeper into React's ecosystem, explore more advanced patterns, and enhance our task application with features like filtering, context-based state management, and routing.
Until then, I encourage you to experiment with the code we've written. Try adding features to the task app, like task categories or due dates. Break components down further or combine them in new ways. The best way to learn React is to build with it!
Have you started your React journey yet? What concepts are you finding most challenging? Let me know in the comments, and I'll address them in the upcoming posts!
Happy coding!