All posts
reactfrontendjavascript

React: A Practical Guide for Full-Stack Developers

A practical guide to React — setup, core concepts, common mistakes, and production tips for full-stack developers.

SR

Suhail Roushan

April 7, 2026

·
6 min read

React is a JavaScript library for building interactive user interfaces, and it's the foundation of modern front-end development for full-stack engineers.

If you're a full-stack developer, you've likely encountered React. It's the dominant force in front-end libraries, and for good reason. Its component-based model aligns perfectly with how we think about building applications. However, its ecosystem can feel overwhelming. This guide cuts through the noise to give you the practical React knowledge you need to integrate it effectively into your full-stack projects, from core concepts to production readiness.

Why React Matters (and When to Skip It)

React matters because it solved a fundamental problem: efficiently updating the DOM. Before React, directly manipulating the DOM was slow and error-prone. React introduced the virtual DOM, a lightweight JavaScript representation of the real DOM. When state changes, React calculates the minimal set of changes needed and applies them in a batch. This declarative model—where you describe what the UI should look like for any given state—is its killer feature.

That said, skip React for simple, mostly static websites. Using it for a basic marketing page is overkill. For those, a traditional multi-page application or a lightweight solution like Astro is more appropriate. React shines when you have complex, dynamic interfaces with lots of user interaction and state changes.

Getting Started with React

The official create-react-app is deprecated. The modern, minimal setup is via the Vite build tool. It's faster and unopinionated, giving you a clean slate. Run this in your terminal:

npm create vite@latest my-react-app -- --template react-ts
cd my-react-app
npm install
npm run dev

This creates a project with TypeScript and React. Your entry point is src/App.tsx. Here's a minimal, working component:

// App.tsx
import { useState } from 'react';

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

  return (
    <div>
      <h1>Count: {count}</h1>
      <button onClick={() => setCount(count + 1)}>
        Increment
      </button>
    </div>
  );
}

export default App;

This is a functional component using the useState Hook. It's a stateful, interactive UI piece in under 15 lines. Vite will run this on localhost:5173.

Core React Concepts Every Developer Should Know

Master these three concepts, and you'll understand 90% of React's day-to-day usage.

1. Components and Props: Components are reusable functions that return JSX. They receive data via props (properties).

// UserProfile.tsx
interface UserProfileProps {
  name: string;
  role: string;
  isActive: boolean;
}

export function UserProfile({ name, role, isActive }: UserProfileProps) {
  return (
    <div className={`profile ${isActive ? 'active' : 'inactive'}`}>
      <h2>{name}</h2>
      <p>Role: {role}</p>
      <p>Status: {isActive ? 'Online' : 'Offline'}</p>
    </div>
  );
}

// Usage in App.tsx
<UserProfile name="Suhail Roushan" role="Founder" isActive={true} />

2. State and the useState Hook: State is data that changes over time within a component. The useState Hook is how you manage it.

import { useState } from 'react';

function TaskManager() {
  const [tasks, setTasks] = useState<string[]>(['Write blog', 'Code review']);
  const [newTask, setNewTask] = useState('');

  const addTask = () => {
    if (newTask.trim()) {
      setTasks([...tasks, newTask]); // Create a new array
      setNewTask(''); // Clear input
    }
  };

  return (
    <div>
      <input
        value={newTask}
        onChange={(e) => setNewTask(e.target.value)}
        placeholder="New task"
      />
      <button onClick={addTask}>Add</button>
      <ul>
        {tasks.map((task, index) => <li key={index}>{task}</li>)}
      </ul>
    </div>
  );
}

Note the key prop for list items. React uses it to track element identity during updates.

3. Side Effects and the useEffect Hook: Use useEffect for operations that interact with the outside world: API calls, subscriptions, or manually manipulating the DOM.

import { useEffect, useState } from 'react';

function DataFetcher({ userId }: { userId: string }) {
  const [userData, setUserData] = useState(null);
  const [loading, setLoading] = useState(false);

  useEffect(() => {
    const fetchUser = async () => {
      setLoading(true);
      try {
        const response = await fetch(`/api/users/${userId}`);
        const data = await response.json();
        setUserData(data);
      } catch (error) {
        console.error('Fetch failed:', error);
      } finally {
        setLoading(false);
      }
    };

    fetchUser();
  }, [userId]); // Dependency array: re-run if `userId` changes

  if (loading) return <p>Loading...</p>;
  return <pre>{JSON.stringify(userData, null, 2)}</pre>;
}

Common React Mistakes and How to Fix Them

1. Mutating State Directly: Never modify state arrays or objects directly. React won't detect the change.

// ❌ WRONG
const handleComplete = (index) => {
  tasks[index].completed = true; // Direct mutation
  setTasks(tasks); // Won't re-render
};

// ✅ CORRECT
const handleComplete = (index) => {
  const updatedTasks = tasks.map((task, i) =>
    i === index ? { ...task, completed: true } : task
  );
  setTasks(updatedTasks); // New array, triggers re-render
};

2. Missing key Prop in Lists: This causes performance issues and bugs with component state during re-renders. Always use a stable, unique ID.

3. Infinite Loops in useEffect: Forgetting the dependency array or incorrectly setting state inside it can cause infinite re-renders.

// ❌ INFINITE LOOP
const [count, setCount] = useState(0);
useEffect(() => {
  setCount(count + 1); // Triggers re-render, which triggers effect again...
});

// ✅ FIXED: Add empty dependency array to run once on mount
useEffect(() => {
  // Setup logic here
  console.log('Component mounted');
}, []);

When Should You Use React?

Use React when you're building a single-page application (SPA) or a complex, interactive dashboard with real-time updates. It's ideal for products where the UI needs to respond instantly to user input or data changes without full page reloads. Examples include admin panels, data visualization tools, real-time chat interfaces, and feature-rich web applications like project management tools or social media feeds.

Avoid React for simple content websites, blogs without dynamic interactivity, or projects where SEO for static content is the primary concern and you cannot implement server-side rendering (SSR). In those cases, the overhead isn't justified.

React in Production

First, manage state seriously. For complex state shared across many components, don't just prop-drill. Use a state management library like Zustand or Redux Toolkit. Zustand is my preference for its simplicity and minimal boilerplate.

Second, implement server-side rendering (SSR) or static site generation (SSG) for performance and SEO. The React meta-framework Next.js is the industry standard for this. It handles routing, SSR, and API routes in a unified way, making it a powerful tool for full-stack developers. You can see a practical implementation on my portfolio at suhailroushan.com.

Finally, always use TypeScript. The type safety it provides for props, state, and event handlers prevents a whole class of runtime errors and makes your components self-documenting.

Start your next dynamic feature by writing the component structure in JSX before you write any logic—it clarifies your data flow instantly.

Related posts

Written by Suhail Roushan — Full-stack developer. More posts on AI, Next.js, and building products at suhailroushan.com/blog.

Get in touch