Tag: performance

  • Why Your React App Re-renders and How to Fix It

    Why Your React App Re-renders and How to Fix It

    Short answer: React re-renders when state, props, or context change. To fix unnecessary re-renders, use React.memo to memoize components, useMemo and useCallback to stabilize values and functions, and restructure your component tree to avoid broad context updates.

    Key takeaways

    • React re-renders when state, props, or context change.
    • Unnecessary re-renders happen when parent components update child components that don’t need to change.
    • React.memo prevents re-renders if props haven’t changed.
    • useMemo and useCallback stabilize values and functions across renders.
    • Avoiding inline functions and objects in JSX reduces re-renders.
    • Context updates cause all consumers to re-render; split contexts to minimize impact.

    You’ve built a React app that works. But as it grows, you notice it slowing down. Every keystroke lags, and the UI feels sluggish. The culprit is often unnecessary re-renders. In this article, you’ll learn exactly why React re-renders components and how to fix the most common causes. By the end, you’ll have a clear set of tools to make your React app fast and responsive.

    How React Decides to Re-render

    React re-renders a component when its state changes, when its parent re-renders, or when the context it consumes updates. When state changes inside a component, that component re-renders. Then, all its children re-render as well. This cascading behavior is by design: React ensures the UI is always in sync with the data.

    However, this default behavior can cause performance issues. If a parent re-renders, every child re-renders, even if their props haven’t changed. That’s where optimization comes in.

    Common Causes of Unnecessary Re-renders

    A developer looking at a laptop with a frustrated expression, symbolizing performance issues from re-renders
    Unnecessary re-renders can slow down your app. — Photo: Pexels / Pixabay

    Let’s look at the most frequent reasons for too many re-renders in production React apps.

    Inline Functions and Objects in JSX

    When you define a function or create an object directly inside JSX, you create a new reference on every render. For example:

    <Button onClick={() => handleClick(id)} />

    This inline arrow function is a new function every time. If Button is wrapped in React.memo, it will still re-render because the onClick prop reference changes. The same problem applies to inline objects like <Component style={{color: 'red'}} />. A common mistake is to think that React.memo alone solves everything — it doesn’t if you pass new references each time.

    To fix this, extract the inline callback or object outside the JSX, or use useCallback and useMemo to stabilize references. For example, instead of onClick={() => handleClick(id)}, you can define const handleClickCallback = useCallback(() => handleClick(id), [id]); and pass handleClickCallback to the button.

    State Updates That Don’t Change State

    Calling setState with the same value doesn’t cause a re-render in class components, but in functional components using hooks, it does. Every setState call triggers a re-render, even if the value is identical. This is a common source of extra renders when you accidentally update state too frequently, like in an event handler that runs multiple times. A real-world example: a dropdown that opens and closes — if you call setIsOpen(!isOpen) when it’s already open, the state changes and triggers an extra render. Check that you only call state setters when the value actually needs to change.

    Context Re-renders Everything

    When a context provider value changes, every consumer of that context re-renders. If you put too many values in one context, a change to one field triggers re-renders for components that only read the other fields. This is a subtle but major performance drain. To mitigate this, split your context into smaller contexts or use useMemo for the value object so that consumers only re-render when the parts they depend on change.

    For example, if you have a UserContext that holds both userName and theme, a change to theme will re-render components that only read userName. Instead, create separate UserNameContext and ThemeContext providers.

    Tools to Prevent Unnecessary Re-renders

    Diagram showing a tree of components with highlighted nodes indicating re-renders
    Understanding the component tree helps identify re-render patterns. — Photo: 777546 / Pixabay

    React gives you several tools to prevent unnecessary re-renders. Use them wisely — over-optimizing can make your code harder to read.

    Tool Use Case Important Note
    React.memo Prevent re-render of a component when its props haven’t changed. Only shallow-compares props. Use React.memo for pure presentational components that receive simple props. If props contain functions or objects, they must be stable references.
    useMemo Cache a computed value until its dependencies change. Use for expensive calculations, not trivial operations like useMemo(() => a + b, [a, b]) — that adds unnecessary overhead. Reserve it for array transformations, filtering, or complex data manipulations.
    useCallback Return a memoized function reference that only changes when dependencies change. Use when passing callbacks to child components wrapped in React.memo. Always list all dependencies used inside the callback to avoid stale closures.
    useRef Keep a mutable value across renders without causing re-renders. Excellent for timers, DOM references, or any value that changes but shouldn’t trigger a UI update. Unlike state, changing a ref does not trigger a re-render.

    Another important strategy is to lift state down. Instead of putting all state in a high-level component, keep state as close as possible to where it’s used. This reduces the number of components that need to re-render when that state changes. For example, if only one child needs a piece of state, store it in that child instead of the parent. If multiple children need it but not all, consider using a shared context with a granular provider.

    Step-by-Step: Fixing a Common Re-render Problem

    Let’s walk through a typical scenario. You have a parent component that holds a search query and a list of items. The list is filtered based on the query. Without optimization, every keystroke re-renders the entire list.

    1. Identify the problem: Profile your app using React DevTools to see which components re-render. You’ll likely see the list re-rendering on every input change. Open the Profiler tab, start recording, type in the search box, and stop. Look at the flamegraph to spot the list component highlighted as re-rendered.
    2. Memoize the list component: Wrap the list component in React.memo. Now it will only re-render if its props change.
    3. Stabilize the filter function: Use useMemo to compute the filtered list only when the search query or the original list changes. The filtered list reference stays stable if the query didn’t change. Example: const filteredList = useMemo(() => list.filter(item => item.name.includes(query)), [list, query]);
    4. Memoize the filter callback: If you pass a callback to a child, use useCallback to keep the reference stable. This is especially important if the child is wrapped in React.memo. For instance, a delete button inside each list item should receive a stable onDelete function.
    5. Test and profile again: Verify that the list no longer re-renders on unrelated state changes. Use the React DevTools Profiler again to confirm the list component is not re-rendering when only the query changes.

    Common pitfalls: forgetting to include all dependencies in useMemo or useCallback arrays, or wrapping everything in memo without measuring. Always check if the optimization actually reduces re-renders by using the profiler.

    When Not to Optimize

    Optimization has a cost. React.memo adds a prop comparison on every render. useMemo and useCallback add overhead for dependency tracking. Only optimize when you measure a performance problem. Premature optimization can make your code harder to maintain without real benefit.

    A good rule of thumb: focus on components that render often (like list items) or that have expensive render logic (like complex charts). For components that render infrequently or are cheap, let React do its job. Also, avoid wrapping a component in React.memo if it receives children or other JSX — those are objects and always change, defeating the purpose.

    How to Profile Re-renders Effectively

    Before you optimize, you need to know what to fix. Open React DevTools and go to the Profiler tab. Click the record button, interact with your app (like typing in an input), then stop. The profiler shows you all components that re-rendered during that interaction, with timing bars. Look for components that re-render frequently and take a long time. Also, enable the “Record why each component rendered” option to see the reason (state change, parent re-render, context change). This tells you exactly which hook call or prop change caused the re-render.

    Another technique: add console.log statements inside your component to track renders. For example, console.log('MyComponent rendered'). Just remember to remove them in production. This is a low-tech but effective way to spot unnecessary renders.

    For deeper analysis, you can use the why-did-you-render package, but it can be noisy. Only use it on specific components when you need to see why they re-render despite seemingly unchanged props.

    If you’re tackling performance, you might also want to explore lazy loading to reduce initial bundle size. Check out our guide on How to Improve Web Performance with Lazy Loading for techniques that complement re-render optimization.

    Another common issue is accessibility mistakes that accidentally cause re-renders, like using improper ARIA attributes. Read 10 Common Accessibility Mistakes Frontend Developers Make to avoid those pitfalls.

    And if you’re wondering whether React or a framework like Next.js is right for your next project, we compare them in React vs Next.js: Choosing the Right Framework for Your Project.

    Wrapping Up

    Understanding why React re-renders and how to prevent unnecessary ones is key to building performant apps. Start by identifying the worst offenders with React DevTools, then apply React.memo, useMemo, and useCallback where they provide measurable improvement. Always measure before and after. That way, you keep your code clean and your app fast.

    Frequently asked questions

    Does React re-render when props change?

    Yes, when a parent component re-renders, all its children re-render by default, regardless of whether their props have changed. React’s reconciliation algorithm then diffs the virtual DOM, but the re-render function still runs. To prevent this, use React.memo on child components so they only re-render when their props actually change.

    What is the difference between useMemo and useCallback?

    useMemo returns a memoized value from a function, while useCallback returns a memoized function itself. Both accept a dependencies array and only recompute when dependencies change. Use useMemo for expensive computed values, and useCallback when you want to pass a stable function reference to a child component that is wrapped in React.memo.

    Why does my React app re-render on every keystroke?

    This usually happens because your input’s onChange handler updates state in a parent component, causing that parent and all its children to re-render. To fix it, move the state and input logic into a separate component that handles only the input, or use React.memo on the list component to prevent it from re-rendering when the input changes.

    Can context cause unnecessary re-renders?

    Yes. When the value of a context provider changes, every component that consumes that context re-renders, even if they only use a part of the context that didn’t change. To mitigate this, split large contexts into smaller, focused ones, or use useMemo to create the context value so it only changes when necessary.

    Is it always good to use React.memo on every component?

    No. React.memo adds a shallow prop comparison on every render, which has its own cost. It’s best used on components that render often and receive stable props, or on components that have expensive render logic. For lightweight components or those that rarely re-render, the comparison overhead may outweigh the benefit.

  • React vs Next.js: Choosing the Right Framework for Your Project

    React vs Next.js: Choosing the Right Framework for Your Project

    Short answer: React is a library for building user interfaces, best for single-page applications and when you need full control over the stack. Next.js is a React framework that adds server-side rendering, static site generation, and routing out of the box. Use React for interactive SPAs; choose Next.js for SEO-friendly, content-heavy, or multi-page sites.

    Key takeaways

    • React is ideal for highly interactive single-page applications.
    • Next.js provides SSR and SSG for better SEO and performance.
    • Next.js includes built-in routing, image optimization, and API routes.
    • React gives you more flexibility but requires more setup.
    • Choose Next.js for content-driven sites and e-commerce.
    • React is lighter for simple apps with a custom backend.

    Every developer faces the decision: React or Next.js? Both are popular in the frontend world, but they serve different purposes. React is a JavaScript library for building user interfaces. Next.js is a full framework built on top of React. This guide will help you understand when to use each one for your project.

    Developer writing React code on a laptop with code editor open
    Building a React application — Photo: Pexels / Pixabay

    What is React?

    React, maintained by Meta, is a library for building user interfaces. It lets you create reusable UI components and manage state efficiently. React uses a virtual DOM to update only the parts of the page that change, making it fast for dynamic interactions.

    You can use React with other tools like Webpack, Babel, and React Router to build a complete application. But out of the box, React handles only the view layer. You have to choose your own tools for routing, data fetching, and build configuration.

    What is Next.js?

    Next.js is a React framework created by Vercel. It provides a complete solution for building React applications. It includes built-in routing, server-side rendering (SSR), static site generation (SSG), API routes, and image optimization. Next.js also supports incremental static regeneration and middleware.

    With Next.js, you get a lot of decisions made for you. This speeds up development and enforces best practices. You can still customize things, but the defaults are production-ready.

    Key Differences Between React and Next.js

    Let’s break down the major differences in a comparison table.

    Feature React Next.js
    Type Library Framework
    Rendering Client-side only (unless you add a third-party) SSR, SSG, CSR, ISR
    Routing Manual (React Router) File-based routing
    SEO Poor without SSR Excellent with SSR/SSG
    Performance Good for SPAs Better initial load time
    Data Fetching Any library (fetch, Axios) getServerSideProps, getStaticProps
    API Routes Not included Built-in
    Image Optimization Not included Built-in component
    Bundle Size Smaller (only React) Larger (framework features)
    Learning Curve Moderate Moderate, plus specific Next.js concepts

    This table shows that Next.js extends React with many features. The choice depends on whether you need those features or prefer the simplicity of React alone.

    When to Choose React

    React is a great choice for single-page applications (SPAs) that require heavy user interaction. For example, a dashboard for analytics, a project management tool, or a social media feed. These apps work well with client-side rendering because they change data often without full page reloads.

    Consider React when you need full control over your tech stack. You want to pick your own state management (Redux, Zustand, etc.), routing library, and build tools. React also integrates easily with Micro Frontend architectures where each team manages its own part of the UI.

    If your application has a backend that handles SEO, like an already server-rendered page that includes a React widget, then React alone is sufficient. Also, if you are building a mobile app with React Native, sharing components with a web version is simpler if you stick to plain React.

    Another case is when server-side rendering is not necessary. For example, an internal admin panel or a logged-in user portal where search engines don’t index the content. In such cases, React’s client-side rendering is fast enough, especially if you improve web performance with lazy loading of components and data.

    When to Choose Next.js

    Next.js excels for content-driven websites where SEO matters. Blogs, e-commerce stores, marketing sites, and documentation portals benefit from server-side rendering or static generation. Search engines can read the full HTML content, improving rankings.

    Use Next.js when you need fast initial page loads. With SSR, the server sends a fully rendered page, so users see content immediately. With SSG, you can pre-render pages at build time and serve them from a CDN, which is incredibly fast.

    Next.js also shines for applications that need both static and dynamic content. You can combine static generation for public pages and server-side rendering for user-specific pages. The file-based routing simplifies navigation, and API routes let you build a backend without a separate server.

    If you are building a multi-page application with complex routing, Next.js saves time. The built-in support for layouts and dynamic routes makes development more straightforward. Additionally, features like automatic image optimization and code splitting boost performance out of the box.

    For large projects with many developers, Next.js enforces a consistent structure. The pages directory (or app directory in newer versions) defines routes clearly. This reduces decision fatigue and helps new team members onboard faster.

    Developers collaborating on a Next.js project with sticky notes and whiteboard
    Team developing with Next.js — Photo: truthseeker08 / Pixabay

    Performance Considerations

    Performance is a key factor when choosing between React and Next.js. React applications start with an empty HTML shell and load JavaScript to render content. This can lead to slower initial load times, especially on slow networks. However, once loaded, React apps can feel extremely responsive because only necessary components re-render.

    Next.js improves performance with server-side rendering. The server sends HTML that the browser can display immediately. Then React hydrates to make it interactive. For static pages, Next.js generates HTML at build time, so the server does minimal work per request. This pattern also helps with lazy loading for better web performance of images and scripts.

    Another aspect is bundle size. A plain React app with no extra libraries is smaller than a Next.js app. But Next.js automatically code-splits per route, so each page only loads what it needs. In practice, Next.js often wins on overall performance because users see content faster.

    Development Experience and Learning Curve

    Getting started with React is straightforward if you use Create React App. You write components and manage state. But as your project grows, you need to add routing, state management, and build configurations. This can become complex.

    Next.js provides a more opinionated setup. You follow its file-based routing and use its data fetching methods. The learning curve includes understanding SSR, SSG, and when to use each. However, once you learn these concepts, development becomes more predictable. The official documentation is excellent and includes step-by-step tutorials.

    If you are new to React, starting with Next.js can be beneficial because it teaches you patterns like SSR and static generation early. However, if you want to deeply understand React without abstractions, start with plain React.

    When to Combine Both

    You don’t have to choose one exclusively. Some projects use React for micro-frontends and Next.js for entry points. For example, a large e-commerce site might have a Next.js frontend for product pages (SEO) and an admin panel built with plain React. This gives you the best of both worlds.

    Also, you can embed React components inside a Next.js page. Next.js is fully compatible with React, so you can use any React library. Actually, Next.js is React under the hood. So switching from React to Next.js is often a gradual migration.

    Making the Decision

    The decision comes down to your project requirements. If you need a simple single-page app with no SEO concerns, go with React. If you need SEO, fast initial load, or a multi-page application, choose Next.js. Consider your team’s experience and the time you have for setup.

    Both are backed by strong communities and are excellent choices. The right framework is the one that solves your problems without adding unnecessary complexity. Start small, experiment, and learn from real projects. And if you run into layout issues, check out common CSS Grid mistakes and how to fix them for help.

    Whichever you choose, focus on writing clean components and managing state well. The framework is just a tool. Your code quality matters more.

    Frequently asked questions

    Can I use React and Next.js together in the same project?

    Yes, Next.js is built on top of React, so all React components work inside Next.js. You can gradually migrate an existing React app to Next.js or embed React widgets in a Next.js page. They are fully compatible.

    Which one is better for SEO, React or Next.js?

    Next.js is better for SEO because it supports server-side rendering and static site generation. These techniques send fully rendered HTML to search engine crawlers. React alone relies on client-side rendering, which search engines may not index as effectively.

    Is Next.js harder to learn than React?

    Next.js adds concepts like SSR, SSG, and file-based routing on top of React. If you already know React, learning Next.js is not very hard. For beginners, starting with React first might be simpler, but Next.js documentation is clear and helps you build complete applications quickly.

    Does Next.js replace React?

    No, Next.js is a framework that uses React as the view library. It does not replace React. You still write React components and use React hooks. Next.js adds extra features like routing, rendering modes, and API routes on top of React.

    Can I deploy a Next.js app without a server?

    Yes, Next.js supports static site generation (SSG), which produces static HTML files. You can deploy these files to any static hosting like Vercel, Netlify, or an S3 bucket. For dynamic features like SSR, you need a Node.js server, but Vercel handles that well.