DEV Community

React Is Not the Problem β€” Your Client-Side Mental Model Is

Modern Web Development in 2026

A practical series about building faster, cleaner, more maintainable web applications without chasing every shiny thing.

React gets blamed for a lot of slow websites.

Sometimes that is fair. A careless React app can ship too much JavaScript, hydrate too much UI, and re-render too often.

But React is rarely the whole problem. The deeper problem is a mental model that says:

If something appears on the screen, it must be a client-side component.

That assumption made sense for many single-page apps. It makes less sense for much of the web we are building now.

A better default

Try this default instead:

Render as much as possible before the browser receives the page. Hydrate only what must be interactive.

This does not mean every product should be static. It means interactivity should be intentional.

The three kinds of UI

Most screens contain three different kinds of UI.

1. Content UI

This is information the user reads:

  • product names,
  • documentation,
  • blog content,
  • pricing details,
  • profile summaries,
  • search results.

This usually does not need client-side state.

2. Navigation UI

This helps the user move:

  • menus,
  • tabs,
  • breadcrumbs,
  • pagination,
  • links.

Some of it needs JavaScript. Much of it does not.

3. Interaction UI

This changes based on user action:

  • forms,
  • filters,
  • carts,
  • editors,
  • dashboards,
  • command palettes.

This is where client-side React shines.

The mistake is treating all three categories the same.

The client component tax

A client component costs more than its source file.

It can pull in:

  • dependencies,
  • state management,
  • event handlers,
  • hydration work,
  • serialization boundaries,
  • re-render behavior,
  • test complexity.

So do not ask, β€œCan this be a client component?”

Ask:

What user interaction requires this to be a client component?

If you cannot name the interaction, the component is probably in the wrong place.

Keep islands small

A common pattern is to make the whole layout interactive because one child needs state.

Avoid this:

"use client";

export function ProductPage({ product }) {
  const [quantity, setQuantity] = useState(1);

  return (
    <main>
      <ProductHero product={product} />
      <ProductDescription product={product} />
      <QuantityPicker value={quantity} onChange={setQuantity} />
    </main>
  );
}
Enter fullscreen mode Exit fullscreen mode

Prefer this:

export function ProductPage({ product }) {
  return (
    <main>
      <ProductHero product={product} />
      <ProductDescription product={product} />
      <QuantityPicker productId={product.id} />
    </main>
  );
}
Enter fullscreen mode Exit fullscreen mode

Now the interactive island is the picker, not the page.

Stop fetching twice

Another slow pattern is server rendering a page and then immediately fetching the same data again in the browser.

That creates:

  • extra network requests,
  • loading flicker,
  • duplicated caching logic,
  • inconsistent error states.

If the server already has the data required for first render, use it. Fetch in the browser when the user’s interaction creates new information needs.

Memoization is not architecture

React developers sometimes reach for useMemo, useCallback, and memoized components when the real issue is that too much state lives too high in the tree.

Before memoizing, ask:

  • Can this state move closer to the component that uses it?
  • Can this work happen on the server?
  • Can this derived value be computed once before render?
  • Can this component stop receiving unstable props?

Memoization can help. It cannot fix a confused ownership model.

The decision checklist

For every component, ask:

## Component placement review

- Does it need browser-only APIs?
- Does it respond to user input before navigation?
- Does it manage local interactive state?
- Does it need real-time updates?
- Does it depend on viewport measurements?
- Could it render on the server and pass data to a smaller client child?
Enter fullscreen mode Exit fullscreen mode

If most answers are β€œno,” keep it server-rendered.

React is still useful

The point is not to write less React because React is bad.

The point is to use React where it creates value:

  • rich forms,
  • optimistic updates,
  • complex stateful widgets,
  • collaborative UI,
  • dashboards,
  • editors,
  • design systems.

But rendering a heading, a paragraph, and a price card does not require a client-side mental model.

Final thought

React did not make your app slow by itself.

Your architecture made the browser responsible for work the browser did not need to do.

Once you start treating client-side JavaScript as a budget instead of a default, React becomes easier to use well.

Sources


Thanks for reading.

You can find me here:

Top comments (0)