React.js Learning Roadmap

Master React.js from fundamentals to advanced concepts with hands-on projects.

Introduction to React.js

React.js is a popular open-source JavaScript library developed by Facebook for building fast, interactive user interfaces for web and mobile applications. Instead of manipulating the DOM directly, React uses a virtual DOM to enhance performance and efficiency.

It is component-based, which means the UI is divided into reusable pieces that manage their own state. React makes it easy to build complex UIs by composing simple components, and it has become an industry standard in modern web development.

πŸ” Why Learn React?
  • Used by major companies like Facebook, Instagram, Netflix, Airbnb, and Uber
  • Supports efficient rendering through the Virtual DOM
  • Large ecosystem, community, and job opportunities
  • Easy integration with tools like Redux, React Router, and Next.js
  • Ideal for Single Page Applications (SPAs) and modern frontends

In this course, you’ll learn React step by step β€” from basic concepts like components and props, to advanced topics like hooks, context API, and performance optimization.

Setup

1. Install Node.js & NPM

Make sure Node.js is installed on your system. React requires Node.js to manage packages. You can download it from nodejs.org.

2. Create a New React App
  • Using Create React App (CRA):
    npx create-react-app my-app
  • Or using Vite (faster build):
    npm create vite@latest my-app -- --template react
3. Project Folder Structure

A basic folder structure looks like this:


    my-app/
    β”œβ”€β”€ node_modules/
    β”œβ”€β”€ public/
    β”‚   └── index.html
    β”œβ”€β”€ src/
    β”‚   β”œβ”€β”€ assets/         β†’ Images, fonts
    β”‚   β”œβ”€β”€ components/     β†’ Reusable components
    β”‚   β”œβ”€β”€ pages/          β†’ Page-level components
    β”‚   β”œβ”€β”€ App.jsx         β†’ Root component
    β”‚   β”œβ”€β”€ main.jsx        β†’ Entry point (for Vite)
    β”‚   └── index.js        β†’ Entry point (for CRA)
    β”œβ”€β”€ .gitignore
    β”œβ”€β”€ package.json
    └── README.md
      
4. Install Common Developer Tools
  • VS Code: Recommended code editor.
  • Extensions:
    • ES7+ React/Redux snippets
    • Prettier – Code formatter
    • React Developer Tools (Chrome/Edge extension)
5. Basic Scripts
  • npm start – Run app in development.
  • npm run build – Create optimized production build.
  • npm run dev – For Vite-based projects.
6. JSX & React Syntax Basics

JSX lets you write HTML-like syntax in JavaScript. Example:


    function Welcome() {
      return <h1>Hello, React!</h1>;
    }
    export default Welcome;
      
7. Folder Structure Best Practices (Optional)
  • Group by feature or route (e.g., src/features or src/modules).
  • Separate UI components, hooks, contexts, and utilities.
  • Maintain clean, modular, and scalable file organization.
8. Version Control

Initialize Git in your project:

git init

Push to GitHub for version control and collaboration.

Core Concepts

Understanding these core concepts is essential to building efficient, scalable, and maintainable applications in React.

  • JSX (JavaScript XML): JSX is a syntax extension for JavaScript that looks similar to HTML and is used in React to describe UI structure. JSX gets compiled to React.createElement().
    Example: <h1>Hello, World!</h1>
  • Components: React applications are built using reusable components. There are two main types:
    • Functional Components: JavaScript functions that return JSX. Preferred in modern React (with Hooks).
    • Class Components: Older ES6 class-based components that use lifecycle methods and internal state.
  • Props (Properties): Props are read-only data passed from parent to child components, enabling reusability.
    Example: <User name="John" />
  • State: State is mutable data specific to a component. Changing state causes re-renders.
    Example: const [count, setCount] = useState(0);
  • Immutability: React encourages immutability for props and state. Never mutate state directlyβ€”always use setters like setState() or setCount().
  • Event Handling: React handles browser events like onClick, onChange using camelCase and function references.
    Example: <button onClick={handleClick}>Click Me</button>
  • Fragments: Used to group elements without adding extra DOM nodes.
    Example: <><h1>Hi</h1><p>Welcome</p></>
  • Rendering:
    • Conditional Rendering: Use if, ? :, or && to show/hide elements.
    • List Rendering: Render dynamic lists with Array.map().
    • Keys: Provide unique keys to list elements to help React identify changes efficiently.
  • Re-rendering: React re-renders a component when its state or props change. It uses a virtual DOM to compare previous and current trees to optimize updates.
  • Component Lifecycle (Overview): Components go through mounting, updating, and unmounting. Functional components use useEffect() to hook into lifecycle moments.

React Hooks

Hooks are special functions introduced in React 16.8 that allow functional components to use features previously available only in class components, such as state management and lifecycle methods. Hooks simplify code and make it more reusable and readable.

  • useState: Manages local component state. It allows you to store values (like form inputs, counters) and update them dynamically.
    Example: const [count, setCount] = useState(0);
  • useEffect: Handles side effects such as data fetching, subscriptions, timers, and manually manipulating the DOM. It runs after the render is committed to the screen.
    Example: Fetching data from an API when the component mounts.
    useEffect(() => { fetchData(); }, []);
  • useContext: Allows components to access and share global data (like themes, authentication, or user settings) without prop drilling.
    Example: const user = useContext(UserContext);
  • useRef: Creates mutable references that persist between renders without causing re-renders. Commonly used to access DOM elements directly or store values like timers.
    Example: const inputRef = useRef(null);
  • useReducer: An alternative to useState for managing complex state logic, especially when the next state depends on the previous state (like Redux but local).
    Example: Handling multiple form inputs or counters with complex updates.
  • useMemo & useCallback:
    • useMemo: Memoizes expensive computed values to optimize performance and prevent recalculations on every render.
      Example: const result = useMemo(() => computeValue(data), [data]);
    • useCallback: Memoizes functions so that they are not recreated on every render, useful when passing functions to child components.
      Example: const handleClick = useCallback(() => doSomething(), []);

Hooks have become the standard for writing modern React applications, reducing the need for class-based components and making code cleaner and easier to maintain.

Styling in React

Styling in React can be handled in multiple ways, offering flexibility depending on the size, complexity, and scalability needs of the project. Below are the most common approaches to styling React applications:

  • Inline Styles:
    Add styles directly to elements using the style attribute with a JavaScript object. Useful for quick styles or dynamic styling but limited in features like pseudo-classes.
    Example:
    <div style={{ color: 'blue', fontSize: '20px' }}>Hello</div>
  • CSS Stylesheets:
    Traditional CSS files that are imported into your React components or entry file.
    Example: import './App.css';
    Limitation: CSS is global, which can lead to class name conflicts in large apps.
  • CSS Modules:
    A way to locally scope CSS by automatically generating unique class names. File names typically end with .module.css.
    Example:
    App.module.css:
    .container { color: red; }
    Component:
    import styles from './App.module.css';
    <div className={styles.container}>Hello</div>

    βœ… Best for medium to large projects to avoid conflicts.
  • Styled Components (CSS-in-JS):
    A library that allows writing actual CSS inside JavaScript using tagged template literals. Styles are scoped to components and can access props.
    Example:
    import styled from 'styled-components';
    const Button = styled.button`
    background: blue;
    color: white;
    `;
    <Button>Click Me</Button>

    πŸ”₯ Great for component-driven designs and dynamic styling.
  • Tailwind CSS:
    A utility-first CSS framework that provides predefined classes for rapid UI development.
    Example:
    <button className="bg-blue-500 text-white px-4 py-2 rounded">Click</button>
    βœ… Highly scalable, reduces custom CSS writing, and fits well with React's component-based approach.
  • CSS-in-JS Libraries (Alternative to Styled Components):
    Other options include Emotion, Stitches, and Linaria, which offer similar benefits with different syntax or performance optimizations.

Conclusion: Your choice of styling depends on the project type:
βœ” For small projects β†’ Inline styles or simple CSS files.
βœ” For medium projects β†’ CSS Modules.
βœ” For large scalable apps β†’ Tailwind CSS or Styled Components with CSS-in-JS approach.

Routing in React

Routing in React enables navigation between different pages or components within a single-page application (SPA) without refreshing the browser. This is typically achieved using the popular library React Router.

Key Concepts in React Routing:
  • Basic Routing: Define routes for different components based on the URL path using <Route> and <Routes>.
    Example:
    <Route path="/about" element={} />
  • Navigation: Use <Link> or <NavLink> components instead of anchor tags (<a>) to enable client-side navigation without page reloads.
    Example:
    <Link to="/about">About</Link>
  • Nested Routes: Allows rendering components inside parent routes. Useful for dashboard layouts, settings pages, etc.
    Example:
    <Route path="dashboard" element={} >
      <Route path="settings" element={} />
    </Route>
  • Dynamic Routes (Route Parameters): Create routes that accept dynamic values like IDs or slugs.
    Example:
    <Route path="/product/:id" element={} />
    Access the parameter with:
    const { id } = useParams();
  • Protected Routes: Restrict certain routes to authenticated users. Typically done by wrapping routes with a component that checks authentication.
    Example:
    <Route path="/dashboard" element={isLoggedIn ? : } />
  • 404 Not Found (Catch-All Routes): Handle unknown paths gracefully.
    Example:
    <Route path="*" element={} />
Example Structure:
    import { BrowserRouter, Routes, Route, Link } from 'react-router-dom';
    
    function App() {
      return (
        <BrowserRouter>
          <nav>
            <Link to="/">Home</Link>
            <Link to="/about">About</Link>
          </nav>
          <Routes>
            <Route path="/" element={<Home />} />
            <Route path="/about" element={<About />} />
            <Route path="*" element={<NotFound />} />
          </Routes>
        </BrowserRouter>
      );
    }
      

Conclusion: React Router is a powerful tool that allows building seamless navigation experiences in single-page applications (SPAs). Mastering routing, including dynamic and protected routes, is essential for any production-grade React project.

API Integration in React

API (Application Programming Interface) integration allows your React app to communicate with external services or databases. This is essential for fetching, sending, or updating data such as user details, product lists, or weather information.

🧠 What You Will Learn:
  • How to fetch data from APIs
  • How to handle loading and errors
  • Display fetched data in your React components
πŸ”— Common Tools:
  • fetch API: Built-in browser method to make HTTP requests.
  • Axios: A popular library for making HTTP requests with cleaner syntax and better error handling.
πŸš€ Basic Example Using fetch():
    import React, { useEffect, useState } from 'react';
    
    function Users() {
      const [users, setUsers] = useState([]);
      const [loading, setLoading] = useState(true);
      const [error, setError] = useState(null);
    
      useEffect(() => {
        fetch('https://jsonplaceholder.typicode.com/users')
          .then(response => {
            if (!response.ok) {
              throw new Error('Failed to fetch data');
            }
            return response.json();
          })
          .then(data => {
            setUsers(data);
            setLoading(false);
          })
          .catch(err => {
            setError(err.message);
            setLoading(false);
          });
      }, []);
    
      if (loading) return <p>Loading...</p>;
      if (error) return <p>Error: {error}</p>;
    
      return (
        <div>
          <h4>Users</h4>
          <ul>
            {users.map(user => (
              <li key={user.id}>{user.name}</li>
            ))}
          </ul>
        </div>
      );
    }
    
    export default Users;
        
βš™οΈ Example Using Axios:
    import axios from 'axios';
    import { useEffect, useState } from 'react';
    
    function Posts() {
      const [posts, setPosts] = useState([]);
    
      useEffect(() => {
        axios.get('https://jsonplaceholder.typicode.com/posts')
          .then(response => setPosts(response.data))
          .catch(error => console.error('Error fetching posts:', error));
      }, []);
    
      return (
        <div>
          <h4>Posts</h4>
          <ul>
            {posts.map(post => (
              <li key={post.id}>{post.title}</li>
            ))}
          </ul>
        </div>
      );
    }
    
    export default Posts;
        
πŸ’‘ Best Practices:
  • Always handle loading and error states for a better user experience.
  • Move API calls to separate files or custom hooks for cleaner code.
  • Use try...catch with async/await for more readable asynchronous code.

Conclusion: API integration is a crucial skill in React development. Whether you're building a blog, an e-commerce site, or a dashboard, mastering API calls enables your app to communicate with real-world data and services.

State Management

As your React application grows, managing state (data) across multiple components becomes more challenging. State management is a strategy to efficiently handle, share, and update data in your application without creating confusion or bugs.

Why State Management Matters:

  • Ensures data consistency across components.
  • Simplifies communication between unrelated components.
  • Makes the app easier to debug, maintain, and scale.

⭐ 1. React Context API

The Context API is a built-in React feature designed for simple state sharing. It's perfect for themes, authentication status, language settings, or small to medium-sized apps.

  • No need to install extra librariesβ€”comes with React.
  • Prevents "prop drilling" (passing data through multiple levels unnecessarily).
  • Simple but not ideal for highly dynamic data or large apps.
Example Use Case:

Managing user authentication state across the app.

πŸš€ 2. Redux Toolkit (RTK)

Redux Toolkit is the official, modern approach to using Redux. It solves many complexities that older Redux implementations had.

  • Centralized state management for large-scale apps.
  • Powerful developer tools for debugging and time-travel debugging.
  • Includes async logic handling (using createAsyncThunk).
  • Structured with slices, reducers, and actionsβ€”makes state predictable.
Example Use Case:

Managing a shopping cart, user profiles, or global notifications in large apps.

πŸͺΆ 3. Lightweight Alternatives

  • Zustand: A minimalistic and scalable state management library. No boilerplate, hooks-based, and super fast.
  • Recoil: Designed by Meta (Facebook), it provides a shared state model with support for derived state and async queries, similar to atoms and selectors.

These tools are excellent for those who want global state without the complexity of Redux.

πŸ’‘ Best Practices:

  • Use local useState for UI-specific states (e.g., modals, form inputs).
  • Use Context for simple global needs (theme, auth).
  • Choose Redux Toolkit or Zustand for apps that require complex shared state.
  • Keep state minimal and avoid unnecessary global states.

Conclusion: Mastering state management is crucial as your app grows. Whether you're building small components or large enterprise apps, choosing the right state management strategy improves maintainability, scalability, and user experience.

Performance & Optimization

As your React application grows, performance optimization becomes critical for ensuring fast load times, smooth user interactions, and a better user experience. React offers multiple techniques and tools to help developers write efficient and optimized applications.

⚑ React.memo

React.memo is a higher-order component that prevents unnecessary re-renders of functional components if their props haven’t changed.

  • Wraps a component to memoize its output.
  • Improves performance for components that render frequently with the same props.
  • Best for pure functional components with predictable props.

🧠 useMemo & useCallback

Both hooks help in memoizing values and functions to prevent expensive recalculations or unnecessary function re-creations during renders.

  • useMemo: Memoizes the result of a calculation (e.g., filtered lists, computed data).
  • useCallback: Memoizes a function definition so it doesn’t get recreated unless dependencies change.
  • Improves performance in components passing functions as props to child components.

⏳ Lazy Loading Components

Lazy loading breaks your application into smaller chunks and loads them only when needed, reducing the initial bundle size and improving load time.

  • Implemented using React.lazy and Suspense.
  • Ideal for loading pages, components, or features only when a user navigates to them.
  • Example: Load the Dashboard component only when a user navigates to "/dashboard".

🚫 Error Boundaries

Error boundaries are React components that catch JavaScript errors in their child component tree, log those errors, and display a fallback UI.

  • Prevents the entire app from crashing due to a component error.
  • Implemented with componentDidCatch and getDerivedStateFromError (Class components only).
  • Commonly used to wrap high-risk components like dynamic imports or third-party widgets.

πŸ“¦ Code Splitting

Code splitting divides your app into separate bundles that can be loaded on demand instead of loading everything upfront.

  • Implemented using dynamic import() along with React.lazy.
  • Reduces initial load time and improves performance, especially for larger apps.
  • Tools like Webpack or Vite automatically handle code splitting based on your configuration.

πŸ’‘ Best Practices for React Performance:

  • Keep components small and focused.
  • Use pagination or infinite scroll instead of rendering large datasets.
  • Optimize images, videos, and assets (e.g., use srcSet, compression).
  • Minimize unnecessary state updates.
  • Debounce or throttle heavy functions (like search inputs).
  • Use browser caching and server-side rendering (SSR) when appropriate.

Conclusion: Mastering React performance techniques ensures your applications remain fast, responsive, and scalable, offering a better experience for users and clients alike.

Authentication & Authorization

Authentication and Authorization are critical for securing web applications. Authentication verifies a user's identity (e.g., login), while Authorization controls what resources that user can access based on their permissions or roles.

πŸ” JWT Token-based Authentication

JSON Web Tokens (JWT) are a secure way to verify users. After a successful login, the server sends a JWT token that the client stores (typically in localStorage or httpOnly cookies).

  • Clients attach this token to requests to verify identity.
  • JWT contains encoded user information and an expiration time.
  • On every request to protected APIs, the token is validated on the backend.

πŸ‘₯ Login & Logout Functionality

Implement user authentication with forms for login and signup. Upon successful login:

  • The server returns a JWT token.
  • The token is saved in localStorage or sessionStorage for client-side apps.
  • Logout simply removes the token, ending the user's session.

πŸ›‘οΈ Protected Routes with React Router

Some routes should only be accessible to authenticated users (e.g., Dashboard, Profile, Admin Pages).

  • Create a wrapper component (e.g., PrivateRoute) that checks if the user has a valid token.
  • If not authenticated, redirect users to the login page.
  • React Router makes it simple to protect and control access based on roles (e.g., Admin, User).

πŸ’‘ Best Practices:

  • Store tokens securely (prefer httpOnly cookies for sensitive apps).
  • Implement token expiration and refresh logic.
  • Ensure the backend verifies token validity on every request.
  • Use HTTPS to prevent token interception.

Summary: Implementing robust Authentication and Authorization ensures your application is secure, scalable, and provides the right access to the right users.

Testing in React

Testing is a crucial part of modern React development. It ensures your application works as expected, reduces bugs, and increases confidence in code changes. Testing is typically divided into three types: Unit Testing, Component Testing, and End-to-End (E2E) Testing.

βœ… Unit Testing with Jest

Unit testing verifies that individual functions, methods, or components work correctly in isolation. React apps commonly use Jestβ€”a powerful JavaScript testing framework.

  • Test JavaScript functions or small pieces of logic.
  • Check outputs based on different inputs.
  • Fast, isolated, and runs in a Node environment.
  • Example: Testing a function that calculates total prices.

πŸ”§ Component Testing with React Testing Library (RTL)

React Testing Library (RTL) focuses on testing components from the user's perspectiveβ€”not just the internals of React components.

  • Test how components render and behave in the DOM.
  • Simulate user interactions like clicks, typing, or form submissions.
  • Check if expected elements are present, visible, or updated based on user actions.
  • RTL works together with Jest to create meaningful UI tests.
  • Example: Testing if a button click updates a counter.

🌐 End-to-End (E2E) Testing using Cypress or Playwright

E2E testing simulates real user behavior across the entire app, from clicking buttons to navigating between pages. It tests the complete flow, including the frontend, backend, and APIs.

  • Test login flows, form submissions, and dashboard navigation.
  • Run tests in an actual browser environment for real-world accuracy.
  • Tools like Cypress and Playwright offer easy-to-read tests with screenshot and video recording capabilities.
  • Example: Test the full user flow from signing in to creating a post.

πŸ’‘ Best Practices in Testing

  • Focus more on testing behavior than implementation details.
  • Write tests for critical features and business logic.
  • Automate testing as part of your CI/CD pipeline.
  • Combine Unit, Component, and E2E testing for full coverage.

Summary: Effective testing improves reliability, speeds up development, and prevents bugs in production. React Testing Library + Jest for unit/component tests and Cypress or Playwright for E2E create a powerful testing strategy.

Deployment

Deployment is the process of making your React application accessible to users over the internet. Once development is complete, the React app needs to be built (optimized for production) and hosted on a web server or cloud platform.

πŸ› οΈ Build Your App

Before deploying, React apps need to be compiled into static files (HTML, CSS, JS). Run the following command:

npm run build

This generates an optimized /build folder that contains static files ready for deployment.

πŸš€ Popular Deployment Platforms

  • Netlify: Drag-and-drop deployment or connect via GitHub for automated deployments. Supports free SSL, custom domains, and CI/CD.
  • Vercel: Zero-configuration deployment, automatic scaling, custom domains, and preview deployments for each pull request.
  • Firebase Hosting: Fast and secure hosting for static and dynamic content. Comes with a global CDN and easy CLI for deployment.
  • GitHub Pages: Great for hosting simple static websites directly from your GitHub repository (best for portfolios or demo apps).
  • Render / AWS / Azure / DigitalOcean: For advanced deployments with backend support, APIs, and serverless functions.

βœ… Deployment Example (Netlify)

  1. Push your project to GitHub or GitLab.
  2. Create an account on Netlify.
  3. Click β€œNew Site from Git” and connect your repository.
  4. Set build command to npm run build and publish directory to build.
  5. Click Deploy. Done! Your app is live.

πŸ’‘ Best Practices for Deployment

  • Set up automatic deployment from your Git repository.
  • Enable HTTPS with SSL certificates (most platforms provide this for free).
  • Use environment variables for sensitive data (API keys, secrets).
  • Test the production build locally before deploying using npm install -g serve and serve -s build.
  • Monitor uptime and performance after deployment.

Summary: Deploying React apps has become easy with modern hosting platforms like Netlify and Vercel. Focus on automating deployments, ensuring site performance, and handling environment configurations securely.

Advanced Topics

Once you're comfortable with core React concepts, diving into advanced topics will help you build scalable, performant, and production-ready applications. These concepts extend React's capabilities and improve user experience, performance, and global reach.

  • πŸš€ Server-Side Rendering (SSR) - Next.js:
    Server-side rendering allows pages to be pre-rendered on the server before being sent to the client, improving SEO and initial load performance. Next.js is the most popular framework for SSR with React, offering routing, API routes, and optimizations out of the box.
  • πŸ—’οΈ Static Site Generation (SSG):
    Generate HTML pages at build time instead of on request. This makes pages load extremely fast and reduces backend dependencies. Tools like Next.js support SSG, making it great for blogs, portfolios, and marketing sites.
  • πŸ“± Progressive Web Apps (PWAs):
    PWAs offer app-like experiences directly in the browser. They support offline usage, background sync, push notifications, and can be installed on devices. This is achieved using service workers, manifest files, and caching strategies.
  • β™Ώ Accessibility (a11y):
    Ensures your app is usable by everyone, including users with disabilities. This involves using semantic HTML, proper ARIA roles, keyboard navigation, and screen reader compatibility. Tools like axe help audit accessibility.
  • 🌍 Internationalization (i18n):
    Make your application adaptable for different languages and regions. Libraries like react-i18next help in managing translations, date formats, currency conversions, and right-to-left (RTL) support for languages like Arabic or Hebrew.

Summary: Mastering these advanced topics elevates your skills from building basic React apps to delivering robust, globally optimized, accessible, and production-ready applications. These are crucial for real-world, enterprise-grade projects.

JSX Syntax

JSX (JavaScript XML) is a syntax extension for JavaScript that allows you to write HTML-like code within your JavaScript files. It’s used with React to describe what the UI should look like. JSX makes your code more readable and expressive.

πŸ”Ή Why Use JSX?
  • Makes code cleaner and easier to understand.
  • Combines markup and logic in one file (component).
  • Helps visualize component structure more easily.
πŸ”Ή Basic JSX Example:

const element = <h1>Hello, React!</h1>;

function App() {
  return (
    <div>
      <h2>Welcome to JSX</h2>
      <p>This looks like HTML but it's JSX.</p>
    </div>
  );
}
  
πŸ”Ή JSX Rules & Notes:
  • **Return one parent element** (wrap everything in a div or fragment).
  • **Use `className` instead of `class`** (because `class` is a reserved word in JS).
  • **Use `camelCase` for HTML attributes** (e.g., `onClick`, `tabIndex`).
  • **Close all tags properly**, even self-closing ones like `` or ``.
  • JSX can include JavaScript expressions inside { } braces.
πŸ”Ή Embedding Expressions in JSX

You can embed any JavaScript expression inside curly braces:


const name = "Arun";
const element = <h1>Hello, {name}!</h1>;
  
πŸ”Ή JSX with JavaScript Logic

You can use conditional statements and loops in JSX (inside functions):


// Conditional rendering
const isLoggedIn = true;
return (
  <div>
    {isLoggedIn ? <p>Welcome Back!</p> : <p>Please Log In</p>}
  </div>
);

// Looping through data
const fruits = ['Apple', 'Banana', 'Mango'];
return (
  <ul>
    {fruits.map((fruit, index) => <li key={index}>{fruit}</li>)}
  </ul>
);
  
πŸ”Ή Fragments in JSX

React requires only one parent element. To avoid adding unnecessary elements to the DOM, use fragments:


return (
  <>
    <h2>Title</h2>
    <p>Subtitle</p>
  </>
);
  
πŸ› οΈ Tools Tip:
  • Use extensions like ES7+ React/Redux/React-Native snippets for faster JSX coding in VS Code.
  • Install Prettier and ESLint to auto-format and validate JSX syntax.

Conclusion: JSX is not required to write React, but it makes writing components easier and cleaner. Understanding JSX deeply helps in writing readable and maintainable code.

Components in React

In React, components are the building blocks of your UI. Each component is a JavaScript function or class that returns JSX and describes part of the interface. They help you split the UI into independent, reusable pieces.

πŸ”Ή Types of Components
  • Functional Components: Most commonly used, especially with hooks.
  • Class Components: Older style, still valid but less common with modern React.
πŸ”Ή Functional Component Example

function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

// Usage:
<Welcome name="Arun" />
  
πŸ”Ή Class Component Example

class Welcome extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}
  
πŸ”Ή Props (Properties)

Props are used to pass data from one component to another, like function arguments.


// Parent component
function App() {
  return <UserCard name="Arun" age={25} />;
}

// Child component
function UserCard(props) {
  return (
    <div>
      <p>Name: {props.name}</p>
      <p>Age: {props.age}</p>
    </div>
  );
}
  
πŸ”Ή Component Composition

Components can contain other components, making them reusable and modular:


function Page() {
  return (
    <div>
      <Header />
      <Content />
      <Footer />
    </div>
  );
}
  
πŸ”Ή Best Practices
  • Use PascalCase for component names.
  • Keep components small and focused.
  • Separate logic and presentation when possible.
  • Reuse components wherever applicable.

Conclusion: Components are the heart of React development. Mastering how to write, organize, and reuse them is key to building scalable applications.

Props & Prop Drilling

In React, props (short for "properties") are used to pass data from a parent component to child components. They are read-only and help components communicate with each other.

πŸ”Ή Using Props

Here’s an example of passing props from one component to another:


function Welcome(props) {
  return <h2>Hello, {props.name}!</h2>;
}

function App() {
  return <Welcome name="Arun" />;
}
  
πŸ”Ή Props Are Read-Only

Props cannot be changed by the receiving component. They are controlled by the parent component.


function Message(props) {
  // ❌ This is not allowed
  // props.text = "New text";
  return <p>{props.text}</p>;
}
  
πŸ”Ή Destructuring Props

You can simplify code using ES6 destructuring:


function Profile({ name, age }) {
  return <p>{name} is {age} years old.</p>;
}
  

🚨 What is Prop Drilling?

Prop Drilling happens when you pass props through multiple layers of components just to reach a deeply nested child. It can make your code harder to maintain.


// Top-level component
function App() {
  const user = { name: "Arun", age: 25 };
  return <Parent user={user} />;
}

// Intermediate component
function Parent({ user }) {
  return <Child user={user} />;
}

// Deep component
function Child({ user }) {
  return <p>{user.name} is {user.age}</p>;
}
  
πŸ”§ Solutions to Avoid Prop Drilling
  • React Context API – Share data without passing it through every level.
  • State Management Libraries – Like Redux, Zustand, Recoil.
  • Component Composition – Reorganize components to reduce nesting.

Summary: Use props to pass data, but be aware of prop drilling. If many layers are involved, consider using React Context or a state manager to keep your code clean and scalable.

Lifecycle Methods

React components go through a lifecycle of events: Mounting, Updating, and Unmounting. Understanding lifecycle methods is crucial for managing data fetching, subscriptions, DOM manipulation, and cleanup.

πŸ”Έ Lifecycle in Class Components

  • constructor() – Called before the component is mounted. Used to initialize state.
  • componentDidMount() – Called once after the component is rendered. Great for API calls.
  • shouldComponentUpdate() – Determines whether the component should re-render.
  • componentDidUpdate() – Called after the component updates.
  • componentWillUnmount() – Called before the component is removed. Useful for cleanup.

class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = { count: 0 };
  }

  componentDidMount() {
    console.log('Component mounted');
  }

  componentDidUpdate() {
    console.log('Component updated');
  }

  componentWillUnmount() {
    console.log('Component will unmount');
  }

  render() {
    return <div>Count: {this.state.count}</div>;
  }
}
  

πŸ”Ή Lifecycle in Functional Components

Functional components use the useEffect() hook to handle lifecycle behavior.

  • Mount: useEffect with empty dependency []
  • Update: useEffect with specific dependencies
  • Unmount: return a cleanup function inside useEffect

import { useEffect, useState } from 'react';

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

  // Mount
  useEffect(() => {
    console.log('Mounted');

    // Unmount
    return () => {
      console.log('Component will unmount');
    };
  }, []);

  // Update
  useEffect(() => {
    console.log('Count updated:', count);
  }, [count]);

  return <button onClick={() => setCount(count + 1)}>
    Count: {count}
  </button>;
}
  
🧠 When to Use Lifecycle Logic
  • πŸ“¦ API Calls: In componentDidMount or useEffect
  • πŸ“Š Analytics: On mount or route change
  • 🧹 Cleanup: On unmount to remove listeners or clear intervals

Note: Class components still work, but hooks (like useEffect) are the modern approach.

Context API

The React Context API is a built-in solution for managing global state. It allows you to share values (like theme, language, or user data) between components without having to pass props manually through every level of the component tree.

πŸ”Ή Why Use Context?

  • πŸ“¦ Avoid prop drilling (passing props through multiple layers)
  • 🌍 Manage global app state (e.g., dark mode, auth user, language)
  • πŸ” Makes data access cleaner and more scalable in large apps

πŸ”§ How It Works

  1. Create a context with createContext()
  2. Wrap components with Context.Provider
  3. Access data using useContext()
✨ Example: Theme Context

// ThemeContext.js
import { createContext } from 'react';

export const ThemeContext = createContext(null);
  

// App.js
import { ThemeContext } from './ThemeContext';

function App() {
  const theme = 'dark';

  return (
    <ThemeContext.Provider value={theme}>
      <Home />
    </ThemeContext.Provider>
  );
}
  

// Home.js
import { useContext } from 'react';
import { ThemeContext } from './ThemeContext';

function Home() {
  const theme = useContext(ThemeContext);
  return <div>Current Theme: {theme}</div>;
}
  
πŸ“˜ Best Practices
  • Use Context for read-heavy global data
  • For complex state management, consider tools like Redux or Zustand
  • Split contexts for different domains (ThemeContext, AuthContext, etc.)

πŸ” Note: The Context API is best for low-frequency updates. Avoid using it to manage frequently changing values like form inputs or animation states.

Forms & Validation

Forms are essential for collecting user input in web applications. In React, form elements like <input>, <textarea>, and <select> are controlled using component state.

✏️ Controlled Components

Controlled components are form inputs where the value is tied to React state. This gives you full control over user input and form behavior.


// Basic form with controlled input
import { useState } from "react";

function ContactForm() {
  const [name, setName] = useState("");

  const handleSubmit = (e) => {
    e.preventDefault();
    alert("Submitted: " + name);
  };

  return (
    <form onSubmit={handleSubmit}>
      <input 
        type="text" 
        value={name} 
        onChange={(e) => setName(e.target.value)} 
        placeholder="Your name" 
      />
      <button type="submit">Submit</button>
    </form>
  );
}
  

βœ… Form Validation

Validation ensures that the user input meets certain rules before submitting the form.

🧰 Manual Validation Example:

const handleSubmit = (e) => {
  e.preventDefault();
  if (name.trim() === "") {
    alert("Name is required");
    return;
  }
  // proceed with submission
};
  
πŸ“¦ Using a Form Library: react-hook-form

react-hook-form is a popular library that simplifies form handling and validation.


import { useForm } from "react-hook-form";

function Register() {
  const { register, handleSubmit, formState: { errors } } = useForm();

  const onSubmit = data => console.log(data);

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input {...register("email", { required: true })} placeholder="Email" />
      {errors.email && <span>Email is required</span>}
      
      <input type="password" {...register("password", { minLength: 6 })} placeholder="Password" />
      {errors.password && <span>Password must be 6+ characters</span>}

      <button type="submit">Sign Up</button>
    </form>
  );
}
  

πŸ›‘οΈ Tips for Good Form UX

  • Show real-time validation messages
  • Highlight errors with color and icons
  • Disable the submit button until the form is valid
  • Use semantic labels for accessibility

🧠 Summary: React gives you full control over form behavior with controlled components. For complex forms and scalable validation, libraries like react-hook-form or Formik are highly recommended.

Error Boundaries

In React, Error Boundaries are components that catch JavaScript errors anywhere in their child component tree, log those errors, and display a fallback UI instead of crashing the entire application.

🧠 Why Use Error Boundaries?

  • Prevent the entire UI from breaking due to a single component error
  • Provide user-friendly fallback UIs
  • Help with debugging and error logging

πŸ› οΈ Creating an Error Boundary

Error Boundaries can only be created using class components. You must define either componentDidCatch() or getDerivedStateFromError().


import React, { Component } from "react";

class ErrorBoundary extends Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }

  static getDerivedStateFromError(error) {
    // Update state to show fallback UI
    return { hasError: true };
  }

  componentDidCatch(error, info) {
    // You can log error info here
    console.error("ErrorBoundary caught an error", error, info);
  }

  render() {
    if (this.state.hasError) {
      return <h2>Something went wrong.</h2>;
    }

    return this.props.children;
  }
}

export default ErrorBoundary;
  

πŸ“¦ Using the Error Boundary

Wrap your potentially unstable component(s) with the Error Boundary:


import ErrorBoundary from './ErrorBoundary';
import BuggyComponent from './BuggyComponent';

function App() {
  return (
    <ErrorBoundary>
      <BuggyComponent />
    </ErrorBoundary>
  );
}
  

⚠️ Limitations

  • Error boundaries only catch errors in the rendering lifecycle, not in event handlers, async code, or server-side rendering.
  • Use try/catch inside functions or hooks for non-rendering errors.

🧩 Best Practice: Use multiple Error Boundaries at different levels (e.g., around major UI sections) for better fault isolation and recovery.

Custom Hooks

Custom Hooks in React allow you to extract and reuse logic across multiple components. They are regular JavaScript functions that start with the word use and can call other hooks internally.

πŸ” Why Use Custom Hooks?

  • Reuse logic across components without repeating code
  • Keep components clean and focused on UI
  • Share stateful logic while maintaining separation of concerns

πŸ› οΈ Creating a Custom Hook

Here’s an example of a simple custom hook that tracks the current window width:


// useWindowWidth.js
import { useState, useEffect } from "react";

function useWindowWidth() {
  const [width, setWidth] = useState(window.innerWidth);

  useEffect(() => {
    const handleResize = () => setWidth(window.innerWidth);
    window.addEventListener("resize", handleResize);

    // Cleanup
    return () => window.removeEventListener("resize", handleResize);
  }, []);

  return width;
}

export default useWindowWidth;
  

πŸ“¦ Using the Custom Hook


import useWindowWidth from "./useWindowWidth";

function ResponsiveComponent() {
  const width = useWindowWidth();

  return (
    <div>
      <p>Current window width: {width}px</p>
    </div>
  );
}
  

βœ… Best Practices

  • Name your hook with the prefix use (e.g., useFetch, useForm)
  • Use other hooks inside them, such as useState or useEffect
  • Encapsulate only the logic – UI should stay in components
  • Keep custom hooks composable and reusable

Custom Hooks are a powerful tool in modern React development, helping to abstract logic and make your code more modular, testable, and readable.

Reconciliation & Virtual DOM

React uses a concept called the Virtual DOM and a process known as Reconciliation to efficiently update the UI. These are key to React’s performance and user experience benefits.

πŸ“¦ What is the Virtual DOM?

  • The Virtual DOM is a lightweight JavaScript representation of the actual DOM.
  • When state or props change, React first updates the Virtual DOM.
  • React then compares the new Virtual DOM with the previous version (a process called diffing).
  • Only the parts of the real DOM that changed are updated β€” making updates faster and more efficient.

πŸ” What is Reconciliation?

Reconciliation is the process React uses to compare the current Virtual DOM with the previous one and update the real DOM accordingly.

  • React identifies what has changed by comparing nodes and elements.
  • It updates only the necessary parts of the DOM (not the whole thing).
  • This results in minimal DOM manipulation and better performance.

βš™οΈ How React Reconciles

  1. React renders your component and builds a new Virtual DOM tree.
  2. It compares this tree with the previous Virtual DOM tree.
  3. It figures out the minimum number of changes needed.
  4. It updates only the changed elements in the real DOM (DOM patching).

🧠 Why It's Important

  • Makes React fast and efficient β€” avoids unnecessary re-rendering.
  • Improves performance for complex UI with frequent updates.
  • Enables a declarative coding style β€” you describe β€œwhat” the UI should look like, React handles the β€œhow.”

πŸ’‘ Example Illustration

Imagine your component changes just one button color β€” React:

  1. Builds a new Virtual DOM version of the component
  2. Finds that only the button's color property changed
  3. Updates only the button in the real DOM, not the whole page

πŸ“˜ Key Terms

  • Diffing Algorithm: Compares old vs new Virtual DOM
  • DOM Patching: Applies necessary changes to real DOM
  • Render Optimization: Prevents unnecessary component re-renders

Understanding Virtual DOM and Reconciliation helps you write better React apps with optimal performance and fewer bugs.

React Learning Roadmap

The roadmap below outlines a structured path for learning React, from beginner to advanced level. Follow these stages to build a strong foundation and become proficient in React development.

πŸš€ Beginner Stage

  • Understand HTML, CSS, and JavaScript fundamentals
  • Learn ES6+ concepts: arrow functions, destructuring, spread/rest operators
  • Install Node.js and npm
  • Set up a React project using create-react-app or Vite
  • Understand JSX and rendering components
  • Learn props, state, and conditional rendering

βš™οΈ Intermediate Stage

  • Work with React Hooks: useState, useEffect
  • Understand form handling and validation
  • Learn component lifecycle (class and function-based)
  • Implement Routing with React Router
  • Perform API calls using Fetch or Axios
  • Build small to medium apps (To-Do App, Blog, Weather App)

πŸ’‘ Advanced Stage

  • Understand advanced hooks: useContext, useReducer, useMemo, useCallback
  • Manage global state with Context API or external libraries (Redux, Zustand)
  • Use custom hooks to reuse logic
  • Implement Error Boundaries for error handling
  • Explore performance optimization and lazy loading
  • Learn about the Virtual DOM and Reconciliation
  • Use code splitting and dynamic imports

πŸ§ͺ Testing & Deployment

  • Write unit tests using Jest and React Testing Library
  • Set up CI/CD workflows for deployment
  • Deploy apps using platforms like Netlify, Vercel, Firebase, or GitHub Pages

πŸ“ˆ Bonus & Real-World Skills

  • Use TypeScript with React
  • Work with CSS-in-JS libraries like styled-components or Tailwind CSS
  • Learn server-side rendering (Next.js)
  • Understand accessibility (a11y) in React
  • Build a portfolio with real projects

Mastering React is a journey. Practice consistently, build real-world apps, and contribute to open source to solidify your skills. πŸš€

Test Your React.js Knowledge

Type a React.js snippet or statement below to check for basic syntax validity and common mistakes.