Skip to main content

⚛️ Advanced React Features You Should Know

🚀 8 Advanced React Features You Must Know in 2025

React has grown a lot since its early days. Beyond state, props, and hooks, React 18 introduced powerful advanced features that make apps more responsive, resilient, and scalable.

If you’re aiming to level up your React skills, understanding these features is a must.
In this blog, we’ll explore 8 advanced React features with definitions, explanations, and code examples.


1. ⚡ Concurrent Rendering (React 18)

Definition
Concurrent Rendering in React 18 allows React to prepare multiple versions of the UI simultaneously without blocking the main thread.

Explanation

  • Makes rendering interruptible and non-blocking.
  • Keeps the UI responsive during heavy updates.
  • Enabled by default in React 18 with features like startTransition and useDeferredValue.

Example

import { useState } from "react";

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

  return (
    <div>
      <button onClick={() => setCount(c => c + 1)}>Increase</button>
      <p>Count: {count}</p>
    </div>
  );
}

👉 In React 18, even with expensive components, rendering can be interrupted so urgent updates like user input remain smooth.


2. ⏳ Transitions → useTransition

Definition
useTransition marks updates as non-urgent, allowing React to prioritize urgent updates (like typing) first.

Explanation

  • Perfect for cases like search + filtering large lists.
  • Keeps typing smooth while running heavy computations.

Example

import { useState, useTransition } from "react";

function Search() {
  const [query, setQuery] = useState("");
  const [list, setList] = useState([]);
  const [isPending, startTransition] = useTransition();

  function handleChange(e) {
    const value = e.target.value;
    setQuery(value);

    startTransition(() => {
      const results = Array.from({ length: 10000 }, (_, i) => `Item ${i}`)
        .filter(item => item.includes(value));
      setList(results);
    });
  }

  return (
    <div>
      <input value={query} onChange={handleChange} placeholder="Search..." />
      {isPending && <p>Loading...</p>}
      <ul>{list.map((item, i) => <li key={i}>{item}</li>)}</ul>
    </div>
  );
}

👉 useTransition ensures typing stays smooth, even while filtering thousands of items.


3. 🕒 useDeferredValue → Handling Slow Computations

Definition
useDeferredValue defers updating a value, letting React keep the UI responsive.

Explanation

  • Similar to useTransition, but works on values instead of updates.
  • Useful for heavy rendering after input changes.

Example

import { useState, useDeferredValue } from "react";

function Search() {
  const [query, setQuery] = useState("");
  const deferredQuery = useDeferredValue(query);

  const results = Array.from({ length: 5000 }, (_, i) => `Item ${i}`)
    .filter(item => item.includes(deferredQuery));

  return (
    <div>
      <input value={query} onChange={(e) => setQuery(e.target.value)} />
      <ul>{results.map((item, i) => <li key={i}>{item}</li>)}</ul>
    </div>
  );
}

👉 The input updates instantly, while results update slightly later, avoiding UI freezes.


4. 🎭 Suspense for Data Fetching

Definition
Suspense lets React wait for data before rendering a component, showing a fallback UI meanwhile.

Explanation

  • First used for code-splitting, now extended to data fetching (Next.js, React Query, etc.).
  • Enables streaming UI rendering.

Example

import React, { Suspense } from "react";

const UserProfile = React.lazy(() => import("./UserProfile"));

function App() {
  return (
    <Suspense fallback={<p>Loading profile...</p>}>
      <UserProfile />
    </Suspense>
  );
}

👉 With SSR + streaming, React can render parts of the UI while other parts are still loading.


5. 🛑 Error Boundaries

Definition
Error Boundaries are React components that catch errors in child components and display a fallback UI.

Explanation

  • Works only in class components.
  • Doesn’t catch errors in event handlers or async code.

Example

class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }
  static getDerivedStateFromError() {
    return { hasError: true };
  }
  componentDidCatch(error, info) {
    console.error("Error:", error, info);
  }
  render() {
    if (this.state.hasError) {
      return <h2>Something went wrong.</h2>;
    }
    return this.props.children;
  }
}

function BuggyComponent() {
  throw new Error("Crashed!");
}

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

👉 Prevents app crashes by showing a fallback UI.


6. 🪟 Portals → Rendering Outside Parent Hierarchy

Definition
Portals let you render children into a DOM node outside their parent hierarchy.

Explanation

  • Ideal for modals, tooltips, dropdowns.
  • Breaks out of CSS/DOM constraints.

Example

import ReactDOM from "react-dom";

function Modal({ children }) {
  return ReactDOM.createPortal(
    <div className="modal">{children}</div>,
    document.getElementById("modal-root")
  );
}

function App() {
  return (
    <div>
      <h1>Main App</h1>
      <Modal>
        <p>This is inside a portal!</p>
      </Modal>
    </div>
  );
}

👉 Even though Modal is inside App, it renders into #modal-root.


7. 🎯 Forwarding Refs → React.forwardRef

Definition
Forwarding refs allows parent components to pass refs down to child components.

Explanation

  • Crucial for reusable UI components (like custom inputs).
  • Lets parents control child elements directly.

Example

const CustomInput = React.forwardRef((props, ref) => (
  <input ref={ref} {...props} />
));

function App() {
  const inputRef = React.useRef();

  return (
    <div>
      <CustomInput ref={inputRef} placeholder="Type here..." />
      <button onClick={() => inputRef.current.focus()}>Focus Input</button>
    </div>
  );
}

👉 Now the parent can directly control the input focus.


8. 🔍 StrictMode Behaviors

Definition
StrictMode is a development-only feature that helps identify potential issues in your React app.

Explanation

  • Runs some lifecycle methods twice (in dev).
  • Warns about deprecated APIs.
  • Ensures your app is ready for concurrent rendering.

Example

function App() {
  return (
    <React.StrictMode>
      <MyComponent />
    </React.StrictMode>
  );
}

👉 Don’t panic if useEffect runs twice — it’s just for debugging.


🎯 Final Thoughts

React keeps evolving, and mastering these advanced features helps you build faster, resilient, and scalable apps:

Concurrent Rendering → Keeps UI responsive
useTransition & useDeferredValue → Smooth heavy updates
Suspense → Elegant async handling
Error Boundaries → Prevent crashes
Portals → Flexible modals/tooltips
Forwarding Refs → Reusable UI components
StrictMode → Catch bugs early

Comments

Popular posts from this blog

🌟 Dot net Microservices interview questions

Here is a comprehensive list of 200 .NET microservices coding questions covering all core microservices concepts and cross-cutting concerns relevant for designing, building, deploying, and maintaining .NET-based distributed systems. 🧩 A. Microservices Fundamentals (20) Build a microservice in .NET 8 that exposes a simple CRUD API. Implement communication between two microservices using REST. How would you design microservices for an e-commerce application? Create a microservice that handles user registration and login. How do you isolate domain logic in a microservice? How to apply the "Single Responsibility Principle" in microservices? Design a service registry/discovery mechanism using custom middleware. Implement a service that handles file uploads and metadata separately. Build a stateless microservice and explain its benefits. Implement health check endpoints in .NET 8. Demonstrate versioning in a microservice API. Add Swagger/OpenAPI support to your m...

⚡ Part 1: Introduction to Generics in C#

🌍 Why Do We Need Generics? Imagine you want to create a stack (like a pile of books 📚): You can push items on top You can pop items off the top If we write a stack for integers : public class IntStack { private int[] items = new int[10]; private int index = 0; public void Push(int item) => items[index++] = item; public int Pop() => items[--index]; } 👉 Problem: This only works for int . What if we want a string stack ? Or a Customer stack ? We’d have to duplicate code for every type. 😢 ✅ Solution: Generics Generics let us create type-safe reusable code without duplication. We can say: “I don’t care what type it is yet — I’ll decide later.” 1) Generic Classes Here’s a generic stack : // Generic class "Stack<T>" // The <T> is a placeholder for any type public class Stack<T> { private T[] items = new T[10]; // Array of type T private int index = 0; // Push adds an item of type T public void P...

🚪 Part 9: API Gateway for .NET 8 Microservices (Ocelot & YARP)

Once you have multiple microservices (Products, Orders, Payments…), exposing each one directly to clients gets messy: Different base URLs Duplicated auth logic No unified rate limiting / caching Hard to evolve routes or aggregate data 👉 Enter the API Gateway — your single front door for all microservices. An API Gateway handles: ✅ Routing & path rewriting ✅ Load balancing, retries, circuit breakers ✅ Authentication & Authorization (JWT, OAuth2) ✅ Rate limiting & caching ✅ Aggregation (compose results from multiple services) In this post we’ll implement two strong options: Ocelot → config-driven, mature, DevOps-friendly YARP (Yet Another Reverse Proxy) → Microsoft’s code-first, extensible gateway ⚖️ Ocelot vs YARP — When to Choose Ocelot → JSON config, minimal C#, built-in QoS (rate limit, circuit breaker). Perfect for teams that like DevOps config-as-code. YARP → full C# control, middleware-friendly, can embed into broader apps (e.g. add dashb...