Every frontend engineer knows the pain. Your team keeps requesting you to add new features to the website, and suddenly your LCP score takes a nose dive.
This is exactly where next/dynamic saves the day. I’ve used this method to rescue performance scores.
1. What is next/dynamic?
It’s a tool for Lazy Loading your React components. When you do a standard import like import Header from './Header', that component gets bundled into the main JavaScript file. This means the browser has to download, parse, and execute that code before the user can really interact with the page. This kills your LCP score.
next/dynamic lets you tell the bundler: "Don't load this yet. Wait until we actually need it." It splits the code into a separate "chunks" file.

2. Two Strategies for Using It
There are two main ways I use next/dynamic, depending on what I'm trying to fix.
Strategy 1: Optimizing for Timing (Fixing LCP)
This is for things the user doesn't see immediately, like heavy components or a section way down the page. By lazy loading these, you let the browser focus 100% of its energy on rendering the Hero section. This makes the perceived load time much faster.
const HeavyComponent = dynamic(() => import('../components/Heavy'),
{
loading: () => <p>Loading...</p>,
})
Strategy 2: Optimizing for Location (Server vs. Browser)
Sometimes, you have code that simply cannot run on the server. Maybe it uses window, document, or a library. By using the ssr: false option, you force the component to be rendered entirely on the client side.
const MapComponent = dynamic(() => import('../components/Map'),
{
ssr: false, // Skip the server entirely!
})
3. next/dynamic vs. 'use client'
This is where I got confused. If I import a component with ssr: true, but that component has 'use client', does that make it CSR?
The answer is No. It is still SSR first.
To clear up this confusion, we need to correct our mental model.
Method vs. Declaration
We must distinguish between the Method and the Declaration.
- SSR/CSR is a Method. It describes when and where the rendering happens.
-
'use client'is a Declaration. It signals what features (like hooks or event listeners) are being used.
It can be confusing that adding 'use client' opts out of SSR. This is wrong. In the Next.js App Router, even if you mark a component with 'use client', it is still rendered on the server initially.
Then how does the 'use client' work with SSR/ CSR?
SSR with
'use client'
The server pre-renders the HTML Shell (the tags like<button>inside the server box). The'use client'acts as Hydration to make the existing button interactive after it arrives in the browser. The HTML is born on the Server. The user sees the content immediately.CSR with
'use client'
The server skips rendering and sends an Empty Shell. The server skips rendering and sends an Empty Shell. Here, the JavaScript bundle must build the entire UI from scratch inside the Browser. The user waits for the JS to load before seeing any content.
Performance is all about control.You need to know what is rendering, where it's rendering, and when.
Want to improve LCP?
→ Use next/dynamic to split up your heavy JS bundles.
Fixing a "window is undefined" error?
→ Use ssr: false to skip the server execution entirely.
Need a button to click?
→ Use 'use client' to add interactivity (while keeping SSR).
But remember, with'use client', the HTML is still born on the server. Learning this distinction is the key to building Next.js apps that feel instant.

Top comments (0)