JavaScript Cost: Hydration, Long Tasks, and Shipping Less JS

How JS hurts INP and load performance, how to find long tasks, and practical patterns to reduce client JS in modern apps (especially Next.js).

F

Frontend Interview Team

March 01, 2026

2 min read
JavaScript Cost: Hydration, Long Tasks, and Shipping Less JS

What you’ll learn

  • Why “JS weight” is not just download size
  • What long tasks are and how they affect INP
  • Practical ways to ship less client-side JavaScript

30‑second interview answer

JS performance is dominated by main-thread work: parsing, compiling, executing, and rendering side effects. Big bundles can look fine on desktop but cause long tasks on mobile, which hurts INP. The best fixes are: reduce JS shipped to the client (server rendering/RSC, remove dependencies), code-split by route, and avoid expensive renders and heavy work in interaction handlers.


Case study (real): frontendinterview.in

Quick lab snapshot:

  • /blog LCP: ~1.0s
  • /blog TTFB: ~60ms

This is what “shipping less JS” looks like in practice:

  • fast server response (TTFB)
  • quick render of main content

The thing to protect as the site grows is interaction responsiveness (INP). As you add:

  • more interactive filters
  • animations
  • third-party scripts

…INP will usually degrade first.

Suggested guardrail: periodically record a Performance trace while rapidly typing in the blog search box and clicking filters. If you see long tasks clustering around input events, it’s time to reduce client JS and rerenders.


INP mini playbook (practical)

If you want one “senior engineer” workflow for INP, it’s this:

Step 1: Reproduce on a slower profile

  • Use DevTools CPU throttling (4× or 6×)
  • Keep cache disabled for the first run

Step 2: Record a Performance trace while interacting

Do 2–3 interactions that represent the real user path:

  • type quickly in the search box
  • click a filter
  • open a post and go back

Step 3: Look for the first bottleneck (don’t guess)

  • Long tasks around the interaction? → you’re main-thread bound
  • Lots of React renders? → reduce rerenders and client boundaries
  • Heavy JSON/looping? → move work off the hot path

Step 4: Fix in priority order

  1. Make less code run on interaction

    • keep handlers tiny
    • defer non-urgent work (requestIdleCallback, setTimeout, or scheduling)
  2. Reduce rerenders

    • memoize expensive derived values
    • avoid creating new objects/arrays in props unnecessarily
  3. Reduce hydration / client JS

    • keep "use client" low in the tree
    • prefer server rendering for static content
  4. Offload heavy work

    • web worker for CPU-heavy parsing/transforms

React pattern: keep typing responsive

If typing causes heavy UI updates, use concurrency tools:

import { useMemo, useState, useTransition } from "react";
 
export function SearchList({ items }: { items: string[] }) {
  const [query, setQuery] = useState("");
  const [isPending, startTransition] = useTransition();
 
  const filtered = useMemo(() => {
    const q = query.trim().toLowerCase();
    if (!q) return items;
    return items.filter((x) => x.toLowerCase().includes(q));
  }, [items, query]);
 
  return (
    <div>
      <input
        value={query}
        onChange={(e) => {
          const next = e.target.value;
          // keep input snappy
          startTransition(() => setQuery(next));
        }}
      />
      {isPending ? <div>Updating…</div> : null}
      <ul>{filtered.map((x) => <li key={x}>{x}</li>)}</ul>
    </div>
  );
}

This doesn’t magically fix everything, but it’s a strong tool when the UI update is expensive.


Mental model: JS has 4 costs

  1. Download
  2. Parse
  3. Compile
  4. Execute

Only #1 shows up clearly in Network; the rest show up in Performance.


Hydration (what it really means)

Hydration is making server-rendered HTML interactive by attaching event listeners and running client code.

Hydration can be expensive if:

  • you render huge component trees
  • you hydrate content that doesn’t need interactivity
  • you ship heavy client libraries

Long tasks and INP

A long task is typically 50ms+ of uninterrupted main-thread work.

INP gets worse when:

  • a user clicks/typing happens during long tasks
  • your handler does expensive work before the next paint

Debugging: DevTools Performance → find long tasks around interaction.


Practical strategies (high ROI)

1) Make fewer components client-side

If you’re using Next.js, keep as much as possible server-rendered.

2) Split code by route and by feature

Avoid loading heavy code on pages that don’t need it.

3) Defer non-urgent work

  • analytics
  • heavy computations
  • non-critical UI initialization

4) Kill dependency bloat

A single dependency can add 50–200KB and a ton of parse/execute time.


Common mistakes

  • Measuring only bundle size, not long tasks
  • Adding client-only libraries for minor features
  • Doing heavy JSON parsing or sorting on the main thread

Production rule of thumb

  • Optimize for low-end devices.
  • Reduce client JS before micro-optimizing code.
  • If INP is bad, find the long tasks first.

Quick recap

  • JS cost is mostly parse/execute on main thread.
  • Hydration can dominate INP.
  • Best fix is often shipping less JS.