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>
);
}
Prefer this:
export function ProductPage({ product }) {
return (
<main>
<ProductHero product={product} />
<ProductDescription product={product} />
<QuantityPicker productId={product.id} />
</main>
);
}
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?
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
- InfoQ, βState of JavaScript 2025: Survey Reveals a Maturing Ecosystem with TypeScript Cementing Dominance,β published March 20, 2026: https://www.infoq.com/news/2026/03/state-of-js-survey-2025/
- Next.js, βNext.js 16,β published October 21, 2025: https://nextjs.org/blog/next-16
Thanks for reading.
You can find me here:
Top comments (0)