Server Component Rendering
Traditional React components, known as client components, re-render when their own state or their parent's state changes. However, hooks cannot be used in server components. This means server components do not have state. As shown in the example below, even if the parent's state changes, the server component does not re-render. So when do server components re-render?
Server Component Rendering Strategies
According to the Next.js official documentation, rendering proceeds with three strategies: Static, Dynamic, and Streaming.
Static Rendering
Static Rendering is Next.js's default rendering strategy. The key feature of this strategy is that components are rendered once at build time, and the results are reused.
- Build Time Rendering
- Server components are rendered at application build time
- The rendered results are cached in CDN and provided to all users
- Data Immutability
- The data of a once-rendered component basically does not change
- The same data is displayed even after refreshing the page
- Client state changes do not affect server component data
- Revalidation Mechanism
- Manual re-rendering is possible through
revalidateTag()
orrevalidatePath()
- Manual re-rendering is possible through
-
When refreshing the screen
You can see that even if you change the state and reload, the data does not change.
-
When revalidating
You can see that when Next.js's
revalidateTag
is executed, it invalidates the server component's cache, fetches the data again, and re-renders.
Dynamic Rendering
Dynamic Rendering is Next.js's rendering strategy that renders components anew on the server for each user request.
Server components automatically switch to Dynamic Rendering if they satisfy either of the following two conditions:
- Using Dynamic API
- Finding
{ cache: 'no-store' }
in fetch options
Rendering Strategy Decision Table
Using Dynamic API | Data Caching | Rendering Result |
---|---|---|
❌ No | ✅ Cached | Static |
✅ Yes | ✅ Cached | Dynamic |
❌ No | ❌ Not Cached | Dynamic |
✅ Yes | ❌ Not Cached | Dynamic |
In other words, Static Rendering only occurs when not using Dynamic API and applying caching to fetch.
Dynamic APIs
Dynamic APIs refer to APIs that depend on the context at the time of request. Let's look at the main Dynamic APIs.
1. cookies()
import { cookies } from 'next/headers';
async function Component() {
const cookieStore = cookies();
const theme = cookieStore.get('theme');
return <div>Current theme: {theme?.value}</div>;
}
2. headers()
import { headers } from 'next/headers';
async function Component() {
const headersList = headers();
const userAgent = headersList.get('user-agent');
return <div>Accessing from: {userAgent}</div>;
}
3. searchParams
// app/page.tsx
export default function Page({
searchParams,
}: {
searchParams: { query: string };
}) {
return <div>Search query: {searchParams.query}</div>;
}
Precautions When Using with Parallel Routes
When controlling modals using searchParams as in the example below, problems can occur due to Dynamic Rendering:
- Server component re-renders when searchParams change
- Modal delay due to data fetching
- Data changes simultaneously with modal opening
// app/@modal/default.tsx
'use client'
import {useSearchParams} from "next/navigation";
import {Modal} from "@/app/@modal/(.)post/[id]/modal";
function Default() {
const searchParam = useSearchParams();
const isModalOpen = searchParam.get('modal') === 'true';
if (!isModalOpen) {
return null;
}
return (
<Modal>modal!!</Modal>
);
}
export default Default;
Solution
The simplest and most effective method is to bypass Next.js's routing system and directly use the browser's built-in History API:
'use client';
function ProductList() {
const openModal = () => {
// Directly change URL without going through Next.js routing
history.pushState(null, '', `?modal=true`);
};
return (
<div>
<button onClick={openModal}>Open Modal</button>
</div>
);
}
Using Link
or useRouter
triggers navigation for the changed URL, causing the rendering logic to run anew. However, using the History API changes only the URL without going through Next's routing system, so the rendering logic doesn't run, but Next supports synchronization with useSearchParams
and usePathname
, allowing detection of URL changes and display of the modal.
Conclusion
Server components are an important rendering optimization strategy in Next.js, but if used incorrectly, they can cause unintended re-rendering or performance degradation. To effectively use server components, it seems important to understand these rendering strategies well and utilize them appropriately.