Skip to main content
Skip to content

Resources

The `use client` overreach: how boilerplates quietly slow Next.js apps

Last updated 26 May 2026 · Dan Tinsley, Halbon Labs

The use client directive is not bad. Using it everywhere is.

React Server Components changed how Next.js applications can be structured. Server components let you render UI on the server, fetch data closer to the source, and reduce the amount of JavaScript sent to the browser. Client components are still essential for interactive UI: state, event handlers, effects, browser APIs, and rich controls.

The mistake is turning an entire application into client components because it feels easier.

In SaaS boilerplates, this mistake can quietly erode performance, maintainability, and the buyer’s ability to reason about the app.

What use client actually means

In Next.js App Router, adding use client at the top of a file declares a client component entry point. That component and the client-side code it imports become part of the browser-side interactive bundle.

You need client components for things like:

  • buttons with local state;
  • dropdown menus;
  • modals;
  • form interactions;
  • charts that use browser APIs;
  • drag-and-drop;
  • animations;
  • useEffect;
  • useState;
  • window or document access.

You do not need client components for every layout, heading, card, static table row, marketing section, or server-fetched page.

The common boilerplate anti-pattern

A rushed template often hits an error like “hooks can only be used in client components.” The quick fix is to add use client to the file. Then another imported component breaks, so use client moves upward. Soon, the page, layout, and most of the dashboard are client-rendered.

This creates several problems:

  • larger JavaScript bundles;
  • slower interaction readiness;
  • more hydration work;
  • weaker server/data boundaries;
  • accidental exposure of client-incompatible logic;
  • harder caching decisions;
  • more global state pressure.

The app still works. It just works less like a modern Next.js app.

Put client components at the leaves

A useful rule:

Keep server components high in the tree and client components at the interactive leaves.

For example, a dashboard page can be a server component that fetches data and passes serialisable props into smaller client components for interaction.

Better shape:

tsx
// Server component
export default async function DashboardPage() {
  const metrics = await getMetrics()
  const projects = await getProjects()

  return (
    <DashboardShell>
      <MetricsGrid metrics={metrics} />
      <ProjectTable initialRows={projects} />
    </DashboardShell>
  )
}

ProjectTable might be a client component if it handles sorting, filtering, or row menus. The whole page does not need to be.

Design systems should not force client rendering

Some component libraries make it tempting to wrap everything in client components. That is not always necessary.

A card, badge, heading, separator, sidebar shell, or static layout component can often remain server-compatible. Interactive primitives like popovers, dropdowns, and tabs may need client behaviour.

A good SaaS template separates:

  • static structure;
  • data fetching;
  • interactive controls;
  • animation wrappers;
  • browser-only integrations.

This keeps the design system flexible instead of forcing every buyer into a large client bundle.

Forms deserve deliberate architecture

Forms are a common source of unnecessary client state.

Some forms need rich client interaction. Others can use server actions, progressive enhancement, or light client wrappers.

Ask:

  • Does this form need instant local validation?
  • Does it need conditional fields?
  • Does it need autosave?
  • Does it need drag-and-drop?
  • Could the submission be handled on the server?
  • Is the client state only tracking a loading boolean?

Do not import a heavy global state pattern just to submit a settings form.

Charts and animations need boundaries

Charts, WebGL, GSAP, Motion, Lenis, maps, editors, and drag interfaces often require client-side code. That is fine.

The key is containment.

A server-rendered analytics page can pass prepared data into a client chart. A marketing page can render static content on the server and isolate scroll animation in a small client component. A dashboard can use client-side controls without making the whole application client-only.

Interactive polish should not erase architectural discipline.

Watch for accidental client imports

A server component becomes difficult to keep server-side if it imports a client-only module. Common culprits:

  • hooks;
  • browser APIs;
  • animation libraries;
  • chart libraries;
  • global stores;
  • components with event handlers;
  • utilities that read window;
  • provider wrappers placed too high.

A clean template keeps server-safe utilities separate from browser-only utilities.

Performance is not only Lighthouse

Lighthouse scores are useful, but buyers should also inspect architecture.

Questions to ask:

  • How many top-level components use use client?
  • Are layouts server-compatible?
  • Are data-fetching pages server components?
  • Are provider wrappers minimal?
  • Are interactive components isolated?
  • Is the bundle split by route where possible?
  • Are heavy libraries loaded only where needed?

A template can pass a simple marketing-page audit while still bloating the app dashboard.

use client audit checklist

Before buying or extending a Next.js SaaS template, check:

  • App layouts are not client components unless necessary.
  • Pages that only fetch and render data remain server components.
  • Client components are isolated around interaction.
  • Browser-only libraries are dynamically or locally contained.
  • Design primitives are server-compatible where possible.
  • Forms use client state only where it adds value.
  • Global providers are not placed higher than necessary.
  • Heavy animation/chart/editor code is route-local.
  • Client bundles are inspected during release.
  • The docs explain the component architecture.

Template Empire angle

Empire UI uses rich motion and polished product surfaces, but the architectural goal is still restraint: use client-side JavaScript where it improves interaction, not as a blanket fix for design-system complexity. Premium UI should feel fast as well as impressive.

Further reading