React Tutorial: A Beginner's Guide

Audrey Lopez

Audrey Lopez

13 June 2025

React Tutorial: A Beginner's Guide

Welcome, aspiring React developer! You've made a fantastic choice. React is a powerful and popular JavaScript library for building user interfaces, and learning it is a surefire way to boost your web development skills. This comprehensive, step-by-step guide will take you from zero to hero, equipping you with the practical knowledge you need to start building your own React applications in 2025. We'll focus on doing, not just reading, so get ready to write some code!

💡
Want a great API Testing tool that generates beautiful API Documentation?

Want an integrated, All-in-One platform for your Developer Team to work together with maximum productivity?

Apidog delivers all your demands, and replaces Postman at a much more affordable price!
button

Let's Setup Your React Development Environment

Before we can start building amazing user interfaces, we need to set up a place to work. Think of this as preparing your workshop before you start a new project.

Installing the Essentials: Node.js and npm

React applications are built and managed using Node.js and its package manager, npm (Node Package Manager).

To get started, head over to the official Node.js website and download the latest Long-Term Support (LTS) version. The installer is straightforward; just follow the on-screen instructions. Once installed, you'll have both Node.js and npm ready to go. You can verify the installation by opening your terminal or command prompt and typing:Bash

node -v
npm -v

These commands should print the versions of Node.js and npm you have installed, respectively.

Your First React Project with Vite

In the past, create-react-app was the go-to tool for starting a new React project. However, the modern web development landscape moves fast, and in 2025, Vite is the recommended choice for its incredible speed and efficiency.

To create a new React project with Vite, open your terminal and run the following command:Bash

npm create vite@latest my-first-react-app -- --template react

Let's break down this command:

Once the command finishes, you'll have a new directory with your project's name. Navigate into this directory:Bash

cd my-first-react-app

Next, you need to install the project's dependencies. These are the other packages that your React application needs to function correctly. Run this command:Bash

npm install

Finally, to see your brand new React application in action, start the development server:Bash

npm run dev

Your terminal will display a local URL, usually http://localhost:5173. Open this URL in your web browser, and you'll see the default React application created by Vite. Congratulations, you've just set up your first React project!


The Heart of React: Components and JSX

Now that you have a running React application, let's dive into the core concepts that make React so powerful: components and JSX.

What are Components?

At its core, a React application is a collection of reusable, self-contained pieces of UI called components. Think of a webpage as being built with LEGO bricks. Each brick is a component, and you can combine them to create more complex structures.

In your my-first-react-app project, open the src folder and you'll find a file named App.jsx. This is your main application component. Let's simplify its content to understand the basics:JavaScript

// src/App.jsx

function App() {
  return (
    <div>
      <h1>Hello, React World!</h1>
      <p>This is my very first React component.</p>
    </div>
  );
}

export default App;

In this code:

Understanding JSX: JavaScript XML

JSX is a syntax extension for JavaScript that allows you to write HTML-like code within1 your JavaScript files. It's not actually HTML, but it makes writing UI code much more intuitive and readable.

Behind the scenes, a tool called a transpiler (in our case, powered by Vite) converts this JSX into regular JavaScript that browsers can understand.

Let's modify our App.jsx to see the power of JSX. We can embed JavaScript expressions directly within our JSX by using curly braces {}.JavaScript

// src/App.jsx

function App() {
  const name = "Beginner Developer";
  const year = new Date().getFullYear();

  return (
    <div>
      <h1>Hello, {name}!</h1>
      <p>Welcome to your React journey in {year}.</p>
    </div>
  );
}

export default App;

Save the file, and your browser will automatically update to display the new content. This is a feature of Vite called Hot Module Replacement (HMR), and it makes for a fantastic development experience.

Creating Your First Custom Component

Let's create our own component. In the src folder, create a new file named Greeting.jsx.JavaScript

// src/Greeting.jsx

function Greeting() {
  return (
    <h2>This is a greeting from my custom component!</h2>
  );
}

export default Greeting;

Now, let's use this new Greeting component inside our App.jsx component.JavaScript

// src/App.jsx
import Greeting from './Greeting'; // Import the Greeting component

function App() {
  const name = "Beginner Developer";
  const year = new Date().getFullYear();

  return (
    <div>
      <h1>Hello, {name}!</h1>
      <p>Welcome to your React journey in {year}.</p>
      <Greeting /> {/* Use the Greeting component */}
    </div>
  );
}

export default App;

By importing and then using <Greeting /> just like a regular HTML tag, we've composed our UI from multiple components. This is the fundamental building block of React applications.


Passing Data with Props

Our Greeting component is a bit static. What if we want to greet different people? This is where props (short for properties) come in. Props are how you pass data from a parent component to a child component.

Making Components Dynamic with Props

Let's modify our Greeting.jsx to accept a name prop.JavaScript

// src/Greeting.jsx

function Greeting(props) {
  return (
    <h2>Hello, {props.name}! This is a greeting from my custom component.</h2>
  );
}

export default Greeting;

Now, in App.jsx, we can pass a name prop to our Greeting components.

JavaScript

// src/App.jsx
import Greeting from './Greeting';

function App() {
  return (
    <div>
      <Greeting name="Alice" />
      <Greeting name="Bob" />
      <Greeting name="Charlie" />
    </div>
  );
}

export default App;

You'll now see three different greetings, each with a unique name. Props allow us to reuse components with different data, making our UI incredibly flexible.

A common and modern JavaScript practice is to destructure the props object directly in the function's parameter list. Let's refactor Greeting.jsx:JavaScript

// src/Greeting.jsx

function Greeting({ name }) {
  return (
    <h2>Hello, {name}! This is a greeting from my custom component.</h2>
  );
}

export default Greeting;

This achieve the same result but with cleaner and more concise code.


Managing Component Memory with State

While props are great for passing data down the component tree, what happens when a component needs to remember and manage its own data? This is where state comes into play. State is data that is managed within a component. When a component's state changes, React will automatically re-render that component to reflect the new state.

Introducing the useState Hook

To manage state in functional components, we use a special function from React called a Hook. The most fundamental Hook is useState.

Let's build a simple counter component to understand how useState works. Create a new file in your src folder called Counter.jsx.JavaScript

// src/Counter.jsx
import { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

export default Counter;

Let's break this down:

  1. count: The current value of the state.
  2. setCount: A function that we can use to update the count state.

Now, let's add this Counter component to our App.jsx:JavaScript

// src/App.jsx
import Counter from './Counter';

function App() {
  return (
    <div>
      <h1>My Awesome React App</h1>
      <Counter />
    </div>
  );
}

export default App;

You should now see a counter in your browser. Every time you click the button, the number increases. React is re-rendering the Counter component each time its state changes.


Responding to User Actions: Handling Events

Interactivity is at the heart of modern web applications. React provides a simple and consistent way to handle events like clicks, form submissions, and keyboard input.

We've already seen a basic event handler in our Counter component with onClick. Let's explore this further by building a simple form that takes user input.

Create a new component file named NameForm.jsx.JavaScript

// src/NameForm.jsx
import { useState } from 'react';

function NameForm() {
  const [name, setName] = useState('');

  const handleSubmit = (event) => {
    event.preventDefault(); // Prevents the default form submission behavior
    alert(`Hello, ${name}!`);
  };

  return (
    <form onSubmit={handleSubmit}>
      <label>
        Name:
        <input
          type="text"
          value={name}
          onChange={(event) => setName(event.target.value)}
        />
      </label>
      <button type="submit">Submit</button>
    </form>
  );
}

export default NameForm;

Let's analyze this form component:

Add this NameForm to your App.jsx to see it in action.


Displaying Information: Conditional Rendering and Lists

Real-world applications often need to show or hide content based on certain conditions, and they frequently need to display lists of data.

Showing and Hiding with Conditional Rendering

Let's create a component that displays a different message depending on whether a user is logged in. Create a file named LoginMessage.jsx.JavaScript

// src/LoginMessage.jsx

function LoginMessage({ isLoggedIn }) {
  if (isLoggedIn) {
    return <h2>Welcome back!</h2>;
  }
  return <h2>Please log in.</h2>;
}

export default LoginMessage;

We can also use the ternary operator for more concise conditional rendering.JavaScript

// src/LoginMessage.jsx

function LoginMessage({ isLoggedIn }) {
  return (
    <div>
      {isLoggedIn ? <h2>Welcome back!</h2> : <h2>Please log in.</h2>}
    </div>
  );
}

export default LoginMessage;

You can then use this component in App.jsx and pass the isLoggedIn prop to see the different messages.

Displaying Lists of Data

To render a list of items, you'll typically use the map() array method. Let's create a component to display a list of fruits. Create a file named FruitList.jsx.JavaScript

// src/FruitList.jsx

function FruitList() {
  const fruits = ['Apple', 'Banana', 'Cherry', 'Date'];

  return (
    <div>
      <h3>My Favorite Fruits:</h3>
      <ul>
        {fruits.map((fruit, index) => (
          <li key={index}>{fruit}</li>
        ))}
      </ul>
    </div>
  );
}

export default FruitList;

Here, we're mapping over the fruits array and for each fruit, we're returning a <li> element.

You'll notice the key={index} prop. When you render a list of items, React needs a unique key for each item to efficiently update the list when it changes. Using the array index as a key is acceptable for simple, static lists. However, for dynamic lists where items can be added, removed, or reordered, it's best to use a unique ID from your data if available.


Adding Style to Your Application

A great application needs to look good. There are several ways to style your React components.

CSS Stylesheets

The most straightforward way is to use regular CSS files. In the src folder, you'll find an App.css file. You can add your styles there.

For example, to style our FruitList component, you could add this to App.css:CSS

/* src/App.css */

.fruit-list {
  list-style-type: none;
  padding: 0;
}

.fruit-item {
  background-color: #f0f0f0;
  margin: 5px 0;
  padding: 10px;
  border-radius: 5px;
}

Then, in your FruitList.jsx, you can use these CSS classes.JavaScript

// src/FruitList.jsx
import './App.css'; // Make sure to import the CSS file

function FruitList() {
  const fruits = ['Apple', 'Banana', 'Cherry', 'Date'];

  return (
    <div>
      <h3>My Favorite Fruits:</h3>
      <ul className="fruit-list">
        {fruits.map((fruit, index) => (
          <li key={index} className="fruit-item">{fruit}</li>
        ))}
      </ul>
    </div>
  );
}

export default FruitList;

Notice that we use className instead of class in JSX, as class is a reserved keyword in JavaScript.

CSS Modules

For larger applications, CSS Modules offer a way to scope styles to a specific component, preventing style conflicts. A CSS Module file is named with a .module.css extension.

Let's create FruitList.module.css:CSS

/* src/FruitList.module.css */

.list {
  list-style-type: square;
}

.item {
  color: blue;
}

Now, in FruitList.jsx, you import the styles as an object:JavaScript

// src/FruitList.jsx
import styles from './FruitList.module.css';

function FruitList() {
  const fruits = ['Apple', 'Banana', 'Cherry', 'Date'];

  return (
    <div>
      <h3>My Favorite Fruits (Styled with CSS Modules):</h3>
      <ul className={styles.list}>
        {fruits.map((fruit, index) => (
          <li key={index} className={styles.item}>{fruit}</li>
        ))}
      </ul>
    </div>
  );
}

export default FruitList;

Vite will automatically generate unique class names, ensuring that the styles in FruitList.module.css only apply to the FruitList component.


Most web applications have multiple pages. To handle navigation between these "pages" in a single-page application (SPA) like one built with React, we use a library called React Router.

Setting Up React Router

First, you need to install React Router:Bash

npm install react-router-dom

Creating Your Pages

Let's create two simple page components: HomePage.jsx and AboutPage.jsx.JavaScript

// src/HomePage.jsx

function HomePage() {
  return (
    <div>
      <h1>Home Page</h1>
      <p>Welcome to the home page of our amazing app!</p>
    </div>
  );
}

export default HomePage;

JavaScript

// src/AboutPage.jsx

function AboutPage() {
  return (
    <div>
      <h1>About Page</h1>
      <p>This is all about our incredible application.</p>
    </div>
  );
}

export default AboutPage;

Configuring the Router

Now, we'll configure our routes in App.jsx.JavaScript

// src/App.jsx
import { BrowserRouter as Router, Routes, Route, Link } from 'react-router-dom';
import HomePage from './HomePage';
import AboutPage from './AboutPage';

function App() {
  return (
    <Router>
      <div>
        <nav>
          <ul>
            <li>
              <Link to="/">Home</Link>
            </li>
            <li>
              <Link to="/about">About</Link>
            </li>
          </ul>
        </nav>

        <hr />

        <Routes>
          <Route path="/" element={<HomePage />} />
          <Route path="/about" element={<AboutPage />} />
        </Routes>
      </div>
    </Router>
  );
}

export default App;

Let's break down the new components from React Router:

Now, when you click the "Home" and "About" links, the content will change without a full page reload. You've successfully implemented client-side routing!


Going Further: The useEffect Hook

The useState Hook is for managing state that directly affects what's rendered. But what about side effects, like fetching data from an API, setting up subscriptions, or manually changing the DOM? For this, we use the useEffect Hook.

The useEffect Hook runs after every render by default. Let's see it in action by creating a component that fetches data from a fake API.

Create a new file DataFetcher.jsx.JavaScript

// src/DataFetcher.jsx
import { useState, useEffect } from 'react';

function DataFetcher() {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    // This function will be called after the component renders
    const fetchData = async () => {
      try {
        const response = await fetch('https://jsonplaceholder.typicode.com/posts/1');
        const jsonData = await response.json();
        setData(jsonData);
      } catch (error) {
        console.error('Error fetching data:', error);
      } finally {
        setLoading(false);
      }
    };

    fetchData();
  }, []); // The empty dependency array is important!

  if (loading) {
    return <p>Loading...</p>;
  }

  return (
    <div>
      <h3>Fetched Data:</h3>
      <h4>{data.title}</h4>
      <p>{data.body}</p>
    </div>
  );
}

export default DataFetcher;

The key to understanding useEffect here is the second argument: the dependency array.

Add DataFetcher to your App.jsx to see it fetch and display data when the component loads.

Conclusion and Next Steps

You've come a long way! You've learned how to:

This is a massive achievement, and you now have a solid foundation to build upon. The world of React is vast and exciting. Here are some topics you might want to explore next:

The most important thing you can do now is to keep building. Practice is key. Try recreating a simple website or application you use daily. Challenge yourself with new features. The more you code, the more confident and skilled you'll become.

Welcome to the React community. Happy coding!

💡
Want a great API Testing tool that generates beautiful API Documentation?

Want an integrated, All-in-One platform for your Developer Team to work together with maximum productivity?

Apidog delivers all your demands, and replaces Postman at a much more affordable price!
button

Explore more

What is Shadcn/UI? Beginner's Tutorial to Get Started

What is Shadcn/UI? Beginner's Tutorial to Get Started

For web developers, the quest for the perfect UI toolkit is a constant endeavor. For years, React developers have relied on traditional component libraries like Material-UI (MUI), Ant Design, and Chakra UI. These libraries offer a wealth of pre-built components, promising to accelerate development. However, they often come with a trade-off: a lack of control, style overrides that feel like a battle, and bloated bundle sizes. Enter Shadcn UI, a paradigm-shifting approach that has taken the React

14 June 2025

10 Best Small Local LLMs to Try Out (< 8GB)

10 Best Small Local LLMs to Try Out (< 8GB)

The world of Large Language Models (LLMs) has exploded, often conjuring images of massive, cloud-bound supercomputers churning out text. But what if you could harness significant AI power right on your personal computer, without constant internet connectivity or hefty cloud subscriptions? The exciting reality is that you can. Thanks to advancements in optimization techniques, a new breed of "small local LLMs" has emerged, delivering remarkable capabilities while fitting comfortably within the me

13 June 2025

How to Use DuckDB MCP Server

How to Use DuckDB MCP Server

Discover how to use DuckDB MCP Server to query DuckDB and MotherDuck databases with AI tools like Cursor and Claude. Tutorial covers setup and tips!

13 June 2025

Practice API Design-first in Apidog

Discover an easier way to build and use APIs