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:

  1. 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.

  2. Massive Ecosystem: With over 3 million packages on npm, countless tutorials, and robust tooling, React has the largest ecosystem of any frontend technology.

  3. Career Opportunities: React consistently tops the list of most in-demand frontend skills, with more job listings than any other JavaScript framework.

  4. 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.

  5. Meta's Commitment: With continued investment from Meta (formerly Facebook), React remains actively developed with regular improvements.

  6. 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:

  1. Declarative UI: Describe what your UI should look like, not how to change it.
  2. Component-Based: Build encapsulated components that manage their own state, then compose them to make complex UIs.
  3. Learn Once, Write Anywhere: React's core concepts transfer across platforms (web, mobile, desktop).
  4. Unidirectional Data Flow: Data flows down from parent to child components.
  5. 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

</span>
 lang="en">

     charset="UTF-8">
     name="viewport" content="width=device-width, initial-scale=1.0">
    React Task List
     rel="stylesheet" href="css/style.css">
    
    <span class="na">src="https://unpkg.com/react@18/umd/react.development.js">
    <span class="na">src="https://unpkg.com/react-dom@18/umd/react-dom.development.js">
    
    <span class="na">src="https://unpkg.com/@babel/standalone/babel.min.js">


    
        My React Task App
    

    
         class="todo-app">
            
             id="app">
        
    

    
    <span class="na">type="text/babel" src="js/main.js">





    Enter fullscreen mode
    


    Exit fullscreen mode
    





  
  
  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 />);



    Enter fullscreen mode
    


    Exit fullscreen mode
    





  
  
  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() { /* ... */ }



    Enter fullscreen mode
    


    Exit fullscreen mode
    




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>
    );
}



    Enter fullscreen mode
    


    Exit fullscreen mode
    





  
  
  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 }}
            
        
    




    Enter fullscreen mode
    


    Exit fullscreen mode
    




React JSX Approach:

return (
    <div>
        <ul>
            {tasks.map(task => (
                <li key={task.id}>{task.text}li>
            ))}
        ul>
    div>
);



    Enter fullscreen mode
    


    Exit fullscreen mode
    





  
  
  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



    Enter fullscreen mode
    


    Exit fullscreen mode
    




React Approach:

// Create a new array with the new task
setTasks([...tasks, newTask]); // Automatically triggers re-render



    Enter fullscreen mode
    


    Exit fullscreen mode
    





  
  
  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');
    // ...
});



    Enter fullscreen mode
    


    Exit fullscreen mode
    




React Approach:

<button 
    className="delete-btn"
    onClick={() => handleDeleteTask(task.id)}
>
    Delete
button>



    Enter fullscreen mode
    


    Exit fullscreen mode
    





  
  
  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>;
}



    Enter fullscreen mode
    


    Exit fullscreen mode
    




Class Components (older style, still used in legacy code):

class Greeting extends React.Component {
    render() {
        return <h1>Hello, {this.props.name}!h1>;
    }
}



    Enter fullscreen mode
    


    Exit fullscreen mode
    




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>;
}



    Enter fullscreen mode
    


    Exit fullscreen mode
    





  
  
  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>
    );
}



    Enter fullscreen mode
    


    Exit fullscreen mode
    




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 of class (as class 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>
    );
}



    Enter fullscreen mode
    


    Exit fullscreen mode
    





  
  
  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 />);



    Enter fullscreen mode
    


    Exit fullscreen mode
    




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



    Enter fullscreen mode
    


    Exit fullscreen mode
    




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!