Follow AiTechWorlds on LinkedIn for professional AI content!Follow Now →

Building Your First React App: A Story-Driven Tutorial for Beginners

Learn React from scratch in 2025 with this beginner React tutorial: components, props, state, hooks, and building a real app step by step.

A
AiTechWorlds Team
May 27, 2026 7 min read
📱

Get more content like this on Telegram!

Daily AI tips, notes & resources — free

Join Free →

Building Your First React App: A Story-Driven Tutorial for Beginners

My first React component was a disaster. I had copied a tutorial, gotten it working, and immediately broke it trying to add one more feature. I didn't understand what the code actually meant — I was pattern-matching from examples without the underlying mental model.

If I could go back and give myself one piece of advice, it would be this: React is simpler than it looks. The complexity is in the ecosystem, not the core idea.

React's core idea: your UI is a function of your data. Change the data, the UI updates. That's it.

In this tutorial, you'll learn React from scratch by building a real task manager app. By the end, you'll understand components, props, state, events, and hooks — the foundation for everything else in React.


What You'll Build

A functional task manager application with:

  • Adding and displaying tasks
  • Marking tasks complete
  • Filtering by status
  • Deleting tasks

Simple enough to understand, real enough to use.


Prerequisites

Before starting:

  • Basic JavaScript: variables, functions, arrays, objects
  • Arrow functions and destructuring
  • map() and filter() array methods

If any of those are unfamiliar, spend time on our modern JavaScript guide first.


Setup: Create Your React Project

npm create vite@latest task-manager -- --template react
cd task-manager
npm install
npm run dev

Open http://localhost:5173. You'll see the default Vite React page. We'll replace all of it.

Open the project in your editor. The important files:

  • src/main.jsx — entry point, renders the app
  • src/App.jsx — your main component (we'll start here)

Core Concept 1: Components

A React component is a JavaScript function that returns JSX — HTML-like syntax that React transforms into real DOM elements.

Clear out src/App.jsx and start fresh:

function App() {
  return (
    <div>
      <h1>My Task Manager</h1>
      <p>Let's get organized.</p>
    </div>
  );
}

export default App;

This is a component. A function. It returns what should appear on screen.

Your First Custom Component

Create src/components/TaskItem.jsx:

function TaskItem({ task }) {
  return (
    <div className="task-item">
      <span>{task.text}</span>
    </div>
  );
}

export default TaskItem;

Notice { task } — this is destructuring props. When you use <TaskItem task={...} />, React passes an object of all attributes as the first argument. We destructure task out of it.


Core Concept 2: Props

Props let you pass data from parent to child. They make components reusable.

// Parent (App.jsx)
import TaskItem from './components/TaskItem';

function App() {
  const task = { id: 1, text: 'Learn React', done: false };
  
  return (
    <div>
      <h1>My Task Manager</h1>
      <TaskItem task={task} />
    </div>
  );
}

Props flow one direction: down from parent to child. A child cannot directly modify its parent's data.


Core Concept 3: State with useState

State is data that lives inside a component and can change. When state changes, the component re-renders.

import { useState } from 'react';

function App() {
  // useState returns [currentValue, setterFunction]
  const [tasks, setTasks] = useState([
    { id: 1, text: 'Learn React', done: false },
    { id: 2, text: 'Build something', done: false },
  ]);
  
  return (
    <div>
      <h1>Tasks ({tasks.length})</h1>
      {tasks.map(task => (
        <TaskItem key={task.id} task={task} />
      ))}
    </div>
  );
}

Why key? When React renders a list, it needs a unique identifier for each item to track changes efficiently. Always provide a key prop when mapping arrays.


Core Concept 4: Event Handling

Let's add the ability to mark tasks complete:

function App() {
  const [tasks, setTasks] = useState([
    { id: 1, text: 'Learn React', done: false },
    { id: 2, text: 'Build something', done: false },
  ]);
  
  function toggleTask(id) {
    // Never mutate state directly — create a new array
    setTasks(tasks.map(task => 
      task.id === id ? { ...task, done: !task.done } : task
    ));
  }
  
  return (
    <div>
      <h1>Tasks</h1>
      {tasks.map(task => (
        <TaskItem 
          key={task.id} 
          task={task} 
          onToggle={toggleTask}
        />
      ))}
    </div>
  );
}

Update TaskItem to use the onToggle prop:

function TaskItem({ task, onToggle }) {
  return (
    <div 
      className={`task-item ${task.done ? 'done' : ''}`}
      onClick={() => onToggle(task.id)}
    >
      <input 
        type="checkbox" 
        checked={task.done} 
        onChange={() => onToggle(task.id)}
      />
      <span style={{ textDecoration: task.done ? 'line-through' : 'none' }}>
        {task.text}
      </span>
    </div>
  );
}

Core Concept 5: Adding Items — Controlled Inputs

A controlled input's value is driven by state:

function AddTaskForm({ onAdd }) {
  const [text, setText] = useState('');
  
  function handleSubmit(e) {
    e.preventDefault();  // Prevent page reload
    if (!text.trim()) return;
    
    onAdd(text.trim());
    setText('');  // Clear input after adding
  }
  
  return (
    <form onSubmit={handleSubmit}>
      <input
        type="text"
        value={text}
        onChange={(e) => setText(e.target.value)}
        placeholder="Add a task..."
      />
      <button type="submit">Add</button>
    </form>
  );
}

Back in App.jsx:

function App() {
  const [tasks, setTasks] = useState([]);
  
  function addTask(text) {
    const newTask = {
      id: Date.now(),  // Simple unique ID
      text,
      done: false,
    };
    setTasks([...tasks, newTask]);
  }
  
  function toggleTask(id) {
    setTasks(tasks.map(t => 
      t.id === id ? { ...t, done: !t.done } : t
    ));
  }
  
  function deleteTask(id) {
    setTasks(tasks.filter(t => t.id !== id));
  }
  
  return (
    <div>
      <h1>Task Manager</h1>
      <AddTaskForm onAdd={addTask} />
      {tasks.map(task => (
        <TaskItem 
          key={task.id} 
          task={task} 
          onToggle={toggleTask}
          onDelete={deleteTask}
        />
      ))}
    </div>
  );
}

Core Concept 6: useEffect for Side Effects

useEffect runs code in response to component mounting or state changes:

import { useState, useEffect } from 'react';

function App() {
  const [tasks, setTasks] = useState(() => {
    // Load from localStorage on initial render
    const saved = localStorage.getItem('tasks');
    return saved ? JSON.parse(saved) : [];
  });
  
  // Save to localStorage whenever tasks changes
  useEffect(() => {
    localStorage.setItem('tasks', JSON.stringify(tasks));
  }, [tasks]);  // [tasks] is the dependency array
  
  // ... rest of component
}

The dependency array [tasks] tells React: "run this effect when tasks changes." An empty array [] means "run once on mount." No array means "run after every render."


Filtering Tasks

Add filter state and computed values:

function App() {
  const [tasks, setTasks] = useState([]);
  const [filter, setFilter] = useState('all'); // 'all', 'active', 'done'
  
  const filteredTasks = tasks.filter(task => {
    if (filter === 'active') return !task.done;
    if (filter === 'done') return task.done;
    return true;
  });
  
  return (
    <div>
      <h1>Task Manager</h1>
      <AddTaskForm onAdd={addTask} />
      
      <div className="filters">
        {['all', 'active', 'done'].map(f => (
          <button 
            key={f}
            onClick={() => setFilter(f)}
            className={filter === f ? 'active' : ''}
          >
            {f}
          </button>
        ))}
      </div>
      
      {filteredTasks.map(task => (
        <TaskItem key={task.id} task={task} onToggle={toggleTask} onDelete={deleteTask} />
      ))}
      
      <p>{tasks.filter(t => !t.done).length} tasks remaining</p>
    </div>
  );
}

What You Just Learned

You've covered the fundamentals that power every React application:

  1. Components — reusable functions that return JSX
  2. Props — data passed from parent to child
  3. useState — state that triggers re-renders when changed
  4. Event handlers — responding to user input
  5. Controlled inputs — form inputs driven by state
  6. useEffect — side effects like localStorage and API calls
  7. Derived state — computing values from existing state (filtering)

These seven concepts are the foundation of everything else in React.


What to Learn Next

With React fundamentals solid, the next steps are:

  • React Router — navigation between pages
  • React Query or SWR — data fetching from APIs
  • Context API — sharing state across components without prop drilling

For the next step in your React journey, our React hooks tutorial covers all the hooks beyond useState and useEffect. When you're ready for a real framework, our React vs Next.js vs Remix comparison helps you choose the right one. And to understand state as your app grows, check out our React state management 2025 guide.


Frequently Asked Questions

How long does it take to learn React as a beginner?

2–4 weeks for fundamentals with daily practice. A real project takes another month. Full ecosystem comfort takes 3–6 months. Build things — don't just watch tutorials.

Do I need to know JavaScript before learning React?

Yes. Arrow functions, destructuring, array methods (map/filter), and Promises are required knowledge before React makes sense.

What is JSX in React?

JSX is HTML-like syntax inside JavaScript. It compiles to React.createElement() calls. Use curly braces {} to embed JavaScript expressions.

What is the difference between props and state?

Props come from outside (parent) and are read-only. State lives inside the component and can change. Change state to trigger re-renders.

Share this article:

Frequently Asked Questions

With consistent daily practice, you can understand React fundamentals in 2–4 weeks. Building your first real project takes another 2–4 weeks. Becoming comfortable with the full ecosystem (routing, state management, data fetching) realistically takes 3–6 months of regular use. The key is building projects, not just reading — every hour of coding beats two hours of watching tutorials.
A

AiTechWorlds Team

✓ Verified Writer

The AiTechWorlds team is passionate about AI, technology, and education. We create high-quality, research-backed content to help you learn, grow, and succeed in the modern digital world.

Related Articles

10K+ Members Growing Daily

Get Free AI Notes Daily

Join AiTechWorlds on Telegram and get daily AI tips, prompt engineering templates, coding resources, and exclusive content — 100% free!

📚 Free Study Notes🤖 AI Tips Daily⚡ Prompt Templates💻 Coding Resources
Join Free Channel

No spam. Leave anytime.

!