<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Paweł Sobolewski</title>
    <description>The latest articles on DEV Community by Paweł Sobolewski (@pawe_sobolewski_1dcc746a).</description>
    <link>https://dev.to/pawe_sobolewski_1dcc746a</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3312246%2Fca6ea4e0-c0a5-4d08-91cd-39e795ee2215.jpeg</url>
      <title>DEV Community: Paweł Sobolewski</title>
      <link>https://dev.to/pawe_sobolewski_1dcc746a</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/pawe_sobolewski_1dcc746a"/>
    <language>en</language>
    <item>
      <title>Building a Multilingual Shop with Next.js</title>
      <dc:creator>Paweł Sobolewski</dc:creator>
      <pubDate>Tue, 01 Sep 2026 22:00:00 +0000</pubDate>
      <link>https://dev.to/u11d/building-a-multilingual-shop-with-nextjs-47ck</link>
      <guid>https://dev.to/u11d/building-a-multilingual-shop-with-nextjs-47ck</guid>
      <description>&lt;p&gt;Adding multiple languages to a web application often starts with a simple step: replacing hardcoded strings with translation keys and adding a language switcher.&lt;/p&gt;

&lt;p&gt;In an e-commerce application, however, internationalization quickly becomes a broader architectural challenge.&lt;/p&gt;

&lt;p&gt;Language affects far more than the text displayed on the page: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It becomes part of the URL,&lt;/li&gt;
&lt;li&gt;it influences routing and redirects,&lt;/li&gt;
&lt;li&gt;it determines which translation resources are loaded,&lt;/li&gt;
&lt;li&gt;it affects server and client rendering,&lt;/li&gt;
&lt;li&gt;it changes how data such as prices and dates should be formatted.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this article, we’ll look at how to approach these problems in a &lt;a href="https://nextjs.org/" rel="noopener noreferrer"&gt;Next.js&lt;/a&gt; application using the App Router. Rather than focusing on the basics of translating strings, we’ll focus on the technical decisions behind the implementation: how the language is detected, how it’s propagated through the application, how translations are loaded on the server and client, and how localized routes are handled.&lt;/p&gt;

&lt;p&gt;The implementation presented here is based on a small e-commerce shop, with a product page as its main piece of domain content. It supports English, Polish, and German, and it intentionally stays focused on the internationalization layer rather than on building a complete commerce platform.&lt;/p&gt;

&lt;p&gt;The goal is to use this relatively simple application to explore the architecture behind multilingual Next.js applications and the trade-offs involved in choosing and implementing an i18n solution.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementation Remarks
&lt;/h2&gt;

&lt;p&gt;Before diving into the implementation, it’s worth noting a few deliberate simplifications in this project.&lt;/p&gt;

&lt;p&gt;The repository is intentionally designed to demonstrate the technical aspects of internationalization in Next.js, not to provide a complete, production-ready e-commerce application. As a result, some parts of the implementation are intentionally simplified.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/u11d-com/blog_nextjs-i18n" rel="noopener noreferrer"&gt;Repository link&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Product data is part of the translation resources
&lt;/h3&gt;

&lt;p&gt;To keep the example self-contained, product data such as names and descriptions is stored alongside the translation resources.&lt;/p&gt;

&lt;p&gt;In a real e-commerce application, this data would typically come from a domain-specific API or commerce backend:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Product page
    ↓
Commerce API
    ↓
Product data
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In that setup, the i18n layer would primarily handle UI translations, while localized product content would be part of the product data model.&lt;/p&gt;

&lt;h3&gt;
  
  
  All languages are stored in a single file
&lt;/h3&gt;

&lt;p&gt;The example keeps translations for all supported languages in one file to make the structure easier to explore.&lt;/p&gt;

&lt;p&gt;In a larger application, each language would typically have its own resource files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;languages/
├── en/
│   ├── common.json
│   ├── home.json
│   └── product.json
├── pl/
│   ├── common.json
│   ├── home.json
│   └── product.json
└── de/
    ├── common.json
    ├── home.json
    └── product.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This makes translation files easier to manage independently and fits better with external localization workflows.&lt;/p&gt;

&lt;h3&gt;
  
  
  No external translation management system
&lt;/h3&gt;

&lt;p&gt;The project uses local resources and an API route to demonstrate the resource-loading boundary.&lt;/p&gt;

&lt;p&gt;In a production application, you might instead use a CMS, a translation management platform, or another external service. The &lt;code&gt;resourceLoader&lt;/code&gt; abstraction is intentionally designed so the underlying source can be swapped out without changing the components that consume translations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Choosing an i18n Solution for Next.js
&lt;/h2&gt;

&lt;p&gt;Before implementing internationalization, the first step is choosing the right tool.&lt;/p&gt;

&lt;p&gt;Next.js provides the &lt;a href="https://nextjs.org/docs/app/guides/internationalization" rel="noopener noreferrer"&gt;routing primitives&lt;/a&gt; needed to build a multilingual application, but it doesn’t prescribe a translation library. That leaves several options: building a small custom solution, using a Next.js-focused library such as &lt;a href="https://github.com/amannn/next-intl" rel="noopener noreferrer"&gt;&lt;code&gt;next-intl&lt;/code&gt;&lt;/a&gt;, or adopting the broader i18next ecosystem through &lt;a href="https://github.com/i18next/next-i18next" rel="noopener noreferrer"&gt;&lt;code&gt;next-i18next&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For this project, I chose &lt;code&gt;next-i18next&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That decision wasn’t based solely on the number of translated strings. The application needs an i18n layer that works across Server and Client Components, supports namespaces, and gives us control over how translation resources are loaded. These are important capabilities for a shop where translations may eventually move from local files to an external CMS or another backend service.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;next-i18next&lt;/code&gt; is built on top of &lt;a href="https://www.i18next.com/" rel="noopener noreferrer"&gt;&lt;code&gt;i18next&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://react.i18next.com/" rel="noopener noreferrer"&gt;&lt;code&gt;react-i18next&lt;/code&gt;&lt;/a&gt;, while adding the Next.js-specific integration. The version used in this project supports the App Router, including Server Components, Client Components, and proxy-based language detection*&lt;em&gt;.&lt;/em&gt;* It also provides separate APIs such as &lt;code&gt;getT()&lt;/code&gt; for Server Components and &lt;code&gt;useT()&lt;/code&gt; for Client Components.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why not build it ourselves?
&lt;/h3&gt;

&lt;p&gt;A custom implementation might initially look attractive. With a small application, a simple dictionary and a React context can be enough:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;translations&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;en&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="na"&gt;pl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="na"&gt;de&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The problem is that translation lookup is only one part of the equation.&lt;/p&gt;

&lt;p&gt;Once the application needs server-side translations, client-side hydration, namespaces, fallback languages, language detection, or external translation resources, the custom solution starts accumulating infrastructure that an established i18n library already provides.&lt;/p&gt;

&lt;p&gt;There’s another important advantage: a mature i18n library gives us much more than a simple &lt;code&gt;key → string&lt;/code&gt; lookup.&lt;/p&gt;

&lt;p&gt;For example, i18next provides built-in support for &lt;strong&gt;interpolation&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nf"&gt;t&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;welcome&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;John&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It also supports &lt;a href="https://www.i18next.com/translation-function/plurals" rel="noopener noreferrer"&gt;pluralization&lt;/a&gt; without requiring us to implement language-specific rules ourselves, and &lt;code&gt;react-i18next&lt;/code&gt; provides the &lt;a href="https://react.i18next.com/latest/trans-component" rel="noopener noreferrer"&gt;&lt;code&gt;&amp;lt;Trans&amp;gt;&lt;/code&gt;&lt;/a&gt; component for translations that contain React elements or more complex markup.&lt;/p&gt;

&lt;p&gt;These features may seem like minor conveniences at first, but they quickly become important as the number and complexity of translations grow. Pluralization is particularly difficult to implement correctly because different languages can have completely different grammatical rules.&lt;/p&gt;

&lt;h2&gt;
  
  
  Language-Based Routing
&lt;/h2&gt;

&lt;p&gt;Once we have chosen the i18n library, the next decision is where the current language should live.&lt;/p&gt;

&lt;p&gt;For this application, the &lt;strong&gt;language is part of the URL&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/en
/pl
/de
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The same applies to nested routes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/en/product/1
/pl/product/1
/de/product/1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is implemented using a dynamic segment in the App Router:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app/
└── [lng]/
    ├── page.tsx
    └── product/
        └── [id]/
            └── page.tsx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;[lng]&lt;/code&gt; segment becomes a route parameter, so a product page receives both the language and the product identifier:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lng&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;A small App Router detail:&lt;/strong&gt; Starting with Next.js 16.3.0, you can also access route parameters via &lt;code&gt;next/root-params&lt;/code&gt;. For example, instead of passing &lt;code&gt;lng&lt;/code&gt; through multiple component levels, a component can use:&lt;/p&gt;


&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;lng&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;next/root-params&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;This can reduce prop drilling when deeper components need the language. It’s especially useful for components that rely on route parameters but don’t otherwise need props from their parent.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Why put the language in the URL?
&lt;/h3&gt;

&lt;p&gt;The first benefit is that each language has its own URL.&lt;/p&gt;

&lt;p&gt;A user can navigate directly to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/pl/product/1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and the server immediately knows which language to use to render the page.&lt;/p&gt;

&lt;p&gt;The URL is also shareable and bookmarkable. If someone sends a Polish product page to another user, they’re sharing the Polish version explicitly rather than relying on the recipient’s browser preferences.&lt;/p&gt;

&lt;p&gt;There is also a clear relationship between URLs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/en/product/1
/pl/product/1
/de/product/1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;They represent the same resource in different languages.&lt;/p&gt;

&lt;p&gt;This is especially useful for SEO, where language-specific URLs can be indexed and connected using mechanisms such as &lt;code&gt;hreflang&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Detecting the User's Language with Proxy
&lt;/h2&gt;

&lt;p&gt;Putting the language in the URL solves the routing problem, but it raises another question: what should happen when a user visits the application without specifying a language?&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;GET /
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is no language in the URL, so the application needs a way to determine the most appropriate one.&lt;/p&gt;

&lt;p&gt;In this project, that logic lives in the Next.js Proxy and follows a simple priority order:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;URL
 ↓
Cookie
 ↓
Accept-Language
 ↓
Fallback
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key point is that these sources are not treated equally.&lt;/p&gt;

&lt;h3&gt;
  
  
  URL comes first
&lt;/h3&gt;

&lt;p&gt;If the requested URL already contains a supported language, we treat it as an explicit user choice.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/pl/product/1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;should always be rendered in Polish, even if the user's browser prefers German.&lt;/p&gt;

&lt;p&gt;The Proxy first extracts the first segment of the pathname and checks whether it is one of the supported languages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;pathname&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nextUrl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;urlLanguage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;pathname&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;supportedLanguages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;urlLanguage&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When a valid language is found, the request can continue normally.&lt;/p&gt;

&lt;p&gt;The Proxy also updates the language cookie to remember the user's explicit choice for subsequent visits.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cookie as persistent preference
&lt;/h3&gt;

&lt;p&gt;If there is no language in the URL, the next source is the language cookie.&lt;/p&gt;

&lt;p&gt;The cookie allows us to remember a language selected during a previous visit without requiring the user to select it again.&lt;/p&gt;

&lt;p&gt;However, the cookie is deliberately lower in the priority chain than the URL.&lt;/p&gt;

&lt;p&gt;This means that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="n"&gt;URL&lt;/span&gt;:    /&lt;span class="n"&gt;de&lt;/span&gt;
&lt;span class="n"&gt;Cookie&lt;/span&gt;: &lt;span class="n"&gt;pl&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;results in German.&lt;/p&gt;

&lt;p&gt;The URL represents the current request, while the cookie represents a previously stored preference.&lt;/p&gt;

&lt;p&gt;Keeping this distinction prevents a stale cookie from overriding an explicit URL.&lt;/p&gt;

&lt;h3&gt;
  
  
  Falling back to &lt;code&gt;Accept-Language&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;If neither the URL nor the cookie provides a supported language, we can use the browser's &lt;code&gt;Accept-Language&lt;/code&gt; header.&lt;/p&gt;

&lt;p&gt;A browser might send something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pl-PL,pl;q=0.9,en-US;q=0.8,en;q=0.7
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The application extracts the language codes and looks for the first one it supports.&lt;/p&gt;

&lt;p&gt;For this project, the supported languages are:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;en
pl
de
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;so the example header would result in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pl
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is important to treat &lt;code&gt;Accept-Language&lt;/code&gt; as a &lt;strong&gt;preference&lt;/strong&gt;, rather than as a guaranteed language selection. The browser may request a language that the application does not support.&lt;/p&gt;

&lt;h3&gt;
  
  
  Finally, use a fallback
&lt;/h3&gt;

&lt;p&gt;There is always a case where none of the detected languages are supported.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="n"&gt;URL&lt;/span&gt;:    /
&lt;span class="n"&gt;Cookie&lt;/span&gt;: &lt;span class="n"&gt;fr&lt;/span&gt;
&lt;span class="n"&gt;Header&lt;/span&gt;: &lt;span class="n"&gt;ja&lt;/span&gt;-&lt;span class="n"&gt;JP&lt;/span&gt;,&lt;span class="n"&gt;ja&lt;/span&gt;;&lt;span class="n"&gt;q&lt;/span&gt;=&lt;span class="m"&gt;0&lt;/span&gt;.&lt;span class="m"&gt;9&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the application only supports English, Polish and German, there is no match.&lt;/p&gt;

&lt;p&gt;This is where the configured fallback language is used:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;en
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Translation Resources and Namespaces
&lt;/h2&gt;

&lt;p&gt;Once the language is established, the next question is how the application retrieves the translations.&lt;/p&gt;

&lt;p&gt;The project uses three namespaces:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;common
home
product
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rather than keeping every translation in a single large dictionary, namespaces let us group resources by responsibility.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;common
├── navigation
├── language switcher
└── shared UI

home
├── hero
└── product grid

product
├── product details
├── add to cart
└── stock information
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This becomes increasingly useful as the application grows. A single translation file works well at first, but over time it becomes harder to maintain and to understand which part of the application owns a particular key.&lt;/p&gt;

&lt;p&gt;Namespaces also provide a useful loading boundary. A product page doesn’t necessarily need every translation used by the homepage.&lt;/p&gt;

&lt;h3&gt;
  
  
  Loading resources through &lt;code&gt;resourceLoader&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The interesting part of this implementation is how those resources are loaded.&lt;/p&gt;

&lt;p&gt;Instead of importing the translation JSON directly into the application, &lt;code&gt;next-i18next&lt;/code&gt; is configured with a custom &lt;code&gt;resourceLoader&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;resourceLoader&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;language&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;namespace&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;baseUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NEXT_PUBLIC_BASE_URL&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http://localhost:3000&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;baseUrl&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/api/fetchTranslations?lang=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;language&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;ns=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;namespace&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="s2"&gt;`Failed to load translations: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;language&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;namespace&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Why introduce an API layer?
&lt;/h3&gt;

&lt;p&gt;For local JSON files, calling an API might seem unnecessary—we could simply import them.&lt;/p&gt;

&lt;p&gt;The API is included here to illustrate a more realistic setup, where translations are managed outside the application bundle.&lt;/p&gt;

&lt;p&gt;Imagine replacing the current implementation with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;resourceLoader
     ↓
    CMS
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;resourceLoader
     ↓
translation service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The rest of the application can remain unchanged.&lt;/p&gt;

&lt;p&gt;This separation is especially useful for e-commerce applications, where translations are often managed by content or localization teams rather than developers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Server and Client Components
&lt;/h2&gt;

&lt;p&gt;With the language and translation resources in place, we still need to answer one key question: how do Server Components and Client Components access translations?&lt;/p&gt;

&lt;p&gt;The App Router provides two execution environments, and &lt;code&gt;next-i18next&lt;/code&gt; offers an API for each.&lt;/p&gt;

&lt;p&gt;On the server, translations can be accessed with &lt;code&gt;getT()&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;product&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;t&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;title&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On the client, translations are accessed through &lt;code&gt;useT()&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;common&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;t&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;addToCart&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This distinction matters because using translations doesn’t automatically mean a component needs to become a Client Component.&lt;/p&gt;

&lt;h3&gt;
  
  
  Keep translated content on the server
&lt;/h3&gt;

&lt;p&gt;A product page is a good example.&lt;/p&gt;

&lt;p&gt;There’s usually no reason to move the entire page to the client just because it contains translated text. The product name, description, labels, and other static content can be translated as the page is rendered on the server.&lt;/p&gt;

&lt;p&gt;That way, the application can send translated HTML in the initial response instead of waiting for client-side JavaScript to initialize the translation layer.&lt;/p&gt;

&lt;h3&gt;
  
  
  Providing the resources from the layout
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;[lng]/layout.tsx&lt;/code&gt; file is where the server-loaded translation resources are passed into the client-side i18n context.&lt;/p&gt;

&lt;p&gt;The layout initializes the server-side i18n instance and obtains the loaded resources:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;i18n&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getT&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;resources&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getResources&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i18n&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Those resources are then passed to the &lt;strong&gt;provider&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;I18nProvider&lt;/span&gt;
  &lt;span class="nx"&gt;language&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;language&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nx"&gt;resources&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/I18nProvider&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The layout therefore establishes the translation context once for the entire language-specific part of the application.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why this separation matters
&lt;/h3&gt;

&lt;p&gt;With the App Router, we should generally default to Server Components.&lt;/p&gt;

&lt;p&gt;The reason is simple: turning a component into a Client Component moves its logic into the browser and can add JavaScript that isn’t otherwise necessary.&lt;/p&gt;

&lt;p&gt;Internationalization shouldn’t be the reason to do that.&lt;/p&gt;

&lt;p&gt;If a component only needs to render translated content, it can remain a Server Component and use &lt;code&gt;getT()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;useT()&lt;/code&gt; should be reserved for components that already need to run on the client — for example, because they handle user interaction, browser events, local state, or other client-side behavior.&lt;/p&gt;

&lt;h2&gt;
  
  
  Formatting Prices
&lt;/h2&gt;

&lt;p&gt;Internationalization is not limited to translating text. Numbers, dates, currencies, and other values often need to be formatted according to the user's language as well.&lt;/p&gt;

&lt;p&gt;Prices are a simple example.&lt;/p&gt;

&lt;p&gt;The same value:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1999.99
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;can be displayed differently depending on the language:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$1,999
1 999 zł
1.999 €
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this project, the language used in the URL is a short code such as &lt;code&gt;en&lt;/code&gt;, &lt;code&gt;pl&lt;/code&gt;, or &lt;code&gt;de&lt;/code&gt;. However, the &lt;code&gt;Intl&lt;/code&gt; APIs work with more specific locale identifiers, such as &lt;code&gt;en-US&lt;/code&gt; or &lt;code&gt;pl-PL&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;formatPrice&lt;/code&gt; utility handles this mapping:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;LOCALE_CURRENCY&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;en&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;USD&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;pl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;PLN&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;de&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;EUR&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;LOCALE_BCP47&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;en&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;en-US&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;pl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pl-PL&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;de&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;de-DE&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are two separate mappings here.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;LOCALE_CURRENCY&lt;/code&gt; determines which currency should be displayed, while &lt;code&gt;LOCALE_BCP47&lt;/code&gt; determines the regional formatting conventions used by &lt;code&gt;Intl.NumberFormat&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The actual formatting is then kept in a small utility:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;formatPrice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;language&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;currency&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;LOCALE_CURRENCY&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;language&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;USD&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bcp47&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;LOCALE_BCP47&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;language&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;en-US&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Intl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;NumberFormat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bcp47&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;currency&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;maximumFractionDigits&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;price&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives components a simple API:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;formatPrice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;price&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lng&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;without requiring them to know anything about currencies, BCP 47 locale identifiers, or &lt;code&gt;Intl.NumberFormat&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Localized Navigation
&lt;/h2&gt;

&lt;p&gt;As language becomes part of the URL, navigation is one of the first areas where i18n can start leaking into your application code.&lt;/p&gt;

&lt;p&gt;Without an abstraction, every link needs to be aware of the current language:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Link&lt;/span&gt; &lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;`/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;lng&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/product/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nx"&gt;Product&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Link&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This works, but it couples the component directly to the routing structure.&lt;/p&gt;

&lt;p&gt;It also gets repetitive: every developer has to remember that the first URL segment represents the current language.&lt;/p&gt;

&lt;p&gt;To avoid this, the project introduces a &lt;code&gt;LocalizedLink&lt;/code&gt; component.&lt;/p&gt;

&lt;h3&gt;
  
  
  LocalizedLink
&lt;/h3&gt;

&lt;p&gt;The idea is simple: components provide a path relative to the language root, and &lt;code&gt;LocalizedLink&lt;/code&gt; adds the current language automatically.&lt;/p&gt;

&lt;p&gt;Instead of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Link&lt;/span&gt; &lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;`/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;lng&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/product/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nx"&gt;Product&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Link&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;we can write:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;LocalizedLink&lt;/span&gt; &lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;`/product/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nx"&gt;Product&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/LocalizedLink&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the current language is Polish, the resulting URL is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/pl/product/1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For German, it becomes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/de/product/1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This way, the component hides a routing detail that’s irrelevant to the component using it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Switching between languages
&lt;/h3&gt;

&lt;p&gt;The language switcher is a slightly different case.&lt;/p&gt;

&lt;p&gt;Here, we want to change the language while preserving the current route.&lt;/p&gt;

&lt;p&gt;For example, if the user is currently on:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/pl/product/1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;switching to German should result in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/de/product/1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;rather than simply navigating to &lt;code&gt;/de&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The switcher can therefore treat the current pathname as a resource-independent path and replace only the language segment.&lt;/p&gt;

&lt;p&gt;Conceptually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Current URL
/pl/product/1
      │
      │ replace language
      ▼
/de/product/1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is important for product pages because changing the language should not interrupt the user's current context.&lt;/p&gt;

&lt;h2&gt;
  
  
  Production Considerations
&lt;/h2&gt;

&lt;p&gt;The implementation covers the core architecture, but a production application would still need to address a few additional concerns.&lt;/p&gt;

&lt;h3&gt;
  
  
  Caching translation resources
&lt;/h3&gt;

&lt;p&gt;Translation resources are usually much more stable than application data. Fetching the same translations on every request would therefore be wasteful.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;resourceLoader&lt;/code&gt; is a natural place to introduce caching and revalidation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Component
    ↓
next-i18next
    ↓
resourceLoader
    ↓
cache
    ↓
translation source
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The exact strategy depends on where the translations are stored. With a CMS or external translation service, the application could use Next.js caching and revalidation to avoid unnecessary requests while still allowing updated translations to reach users without rebuilding the entire application.&lt;/p&gt;

&lt;h3&gt;
  
  
  SEO and &lt;code&gt;hreflang&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Language-specific URLs also make SEO an important consideration.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/en/product/1
/pl/product/1
/de/product/1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These URLs represent different language versions of the same page. A production application should expose this relationship to search engines using appropriate &lt;code&gt;hreflang&lt;/code&gt; metadata.&lt;/p&gt;

&lt;p&gt;Canonical URLs should also be generated consistently so that each language version points to itself as the canonical URL, while the &lt;code&gt;hreflang&lt;/code&gt; links connect the available alternatives.&lt;/p&gt;

&lt;p&gt;The routing architecture already provides a clear URL for each language version. SEO metadata can build on top of that structure instead of inferring the language from cookies or client-side state.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Building a multilingual application with Next.js isn’t just about translating strings. Language affects routing, request handling, resource loading, rendering, and navigation, so internationalization quickly becomes an architectural concern.&lt;/p&gt;

&lt;p&gt;The implementation presented here addresses those concerns at well-defined boundaries. The language is part of the URL, Proxy handles language detection, &lt;code&gt;next-i18next&lt;/code&gt; provides the translation layer, and Server and Client Components use different APIs depending on where translations are needed. Application-specific abstractions such as &lt;code&gt;LocalizedLink&lt;/code&gt; then keep language-related details out of regular components.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>react</category>
      <category>nextjs</category>
      <category>programming</category>
    </item>
    <item>
      <title>Next.js Server Actions vs API Routes: Architecture, Performance, and Use Cases</title>
      <dc:creator>Paweł Sobolewski</dc:creator>
      <pubDate>Tue, 05 May 2026 22:00:00 +0000</pubDate>
      <link>https://dev.to/u11d/nextjs-server-actions-vs-api-routes-architecture-performance-and-use-cases-5foe</link>
      <guid>https://dev.to/u11d/nextjs-server-actions-vs-api-routes-architecture-performance-and-use-cases-5foe</guid>
      <description>&lt;p&gt;With the introduction of the App Router, &lt;strong&gt;Next.js&lt;/strong&gt; added two powerful server-side primitives:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://nextjs.org/docs/app/getting-started/mutating-data" rel="noopener noreferrer"&gt;&lt;strong&gt;Server Actions&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nextjs.org/docs/app/getting-started/route-handlers" rel="noopener noreferrer"&gt;&lt;strong&gt;Route Handlers&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At first glance, they might seem interchangeable—both run on the server. But they're designed for &lt;strong&gt;different responsibilities&lt;/strong&gt;, follow &lt;strong&gt;different execution models&lt;/strong&gt;, and come with &lt;strong&gt;different constraints&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Understanding these differences is essential to avoid architectural pitfalls and performance issues.&lt;/p&gt;

&lt;h1&gt;
  
  
  What Are Server Actions?
&lt;/h1&gt;

&lt;p&gt;According to the official documentation, &lt;strong&gt;Server Actions are a specific type of &lt;a href="https://react.dev/reference/rsc/server-functions" rel="noopener noreferrer"&gt;React Server Function&lt;/a&gt; used for mutations&lt;/strong&gt;. They are invoked from the client by sending a &lt;strong&gt;POST request to the current page’s route (the same URL context in which the action was triggered)&lt;/strong&gt;, where Next.js internally routes the request to the corresponding server-side function based on unique server action identifier.&lt;/p&gt;

&lt;p&gt;Here's an important clarification:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;We only call them &lt;em&gt;Server Actions&lt;/em&gt; when they're &lt;strong&gt;invoked from the client&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;From the server's perspective, they're just &lt;strong&gt;regular asynchronous functions&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;use server&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;updateUser&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// mutation logic&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Called from a &lt;strong&gt;Client Component&lt;/strong&gt; → acts as a &lt;strong&gt;Server Action&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Called from another &lt;strong&gt;server-side function (like server component)&lt;/strong&gt; → just a &lt;strong&gt;normal function call&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This distinction is crucial:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;strong&gt;network boundary only exists when invoked from the client,&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;serialization and execution constraints only apply in that context.&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Key characteristics
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Must be &lt;strong&gt;asynchronous&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Marked with the &lt;code&gt;"use server"&lt;/code&gt; directive&lt;/li&gt;
&lt;li&gt;Invoked via &lt;strong&gt;POST requests under the hood&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Mutations + Rendering = Sequential Constraints
&lt;/h1&gt;

&lt;p&gt;The official documentation states that React Server Functions are designed for &lt;strong&gt;server-side mutations&lt;/strong&gt;. This design choice is directly tied to one of their most important limitations.&lt;/p&gt;

&lt;p&gt;When a Server Action is invoked:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;It &lt;strong&gt;mutates server-side state&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;It triggers a &lt;strong&gt;React Server Component (RSC) re-render&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;It produces a &lt;strong&gt;new UI snapshot&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;The client merges the updated result&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This tight coupling between &lt;strong&gt;mutation and rendering&lt;/strong&gt; means React must ensure each update applies to a &lt;strong&gt;consistent version of the UI tree&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This creates a critical constraint described in the docs:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The client currently dispatches and awaits them one at a time&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What this means in practice
&lt;/h2&gt;

&lt;p&gt;Even if multiple Server Actions are triggered “at once”:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;actionA&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nf"&gt;actionB&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;They will execute &lt;strong&gt;sequentially&lt;/strong&gt;, not in parallel:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;actionA → re-render → actionB → re-render
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is not an implementation detail—it is a consequence of the architecture.&lt;/p&gt;

&lt;p&gt;Running them in parallel would risk:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;race conditions between mutations&lt;/li&gt;
&lt;li&gt;inconsistent UI snapshots&lt;/li&gt;
&lt;li&gt;stale data being rendered&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So React prioritizes &lt;strong&gt;consistency over concurrency&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Future note
&lt;/h2&gt;

&lt;p&gt;The documentation explicitly indicates that this behavior is &lt;strong&gt;current&lt;/strong&gt;, which means it may change in the future. However, today:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You should design your system assuming Server Actions are &lt;strong&gt;sequential&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  What Are Route Handlers?
&lt;/h1&gt;

&lt;p&gt;Route Handlers address a key limitation of Server Actions: &lt;strong&gt;the lack of true concurrent, independent calls&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Because Server Actions are tightly coupled with React's rendering lifecycle, they aren't suited for scenarios where multiple operations must run in parallel without UI-driven synchronization.&lt;/p&gt;

&lt;p&gt;Route Handlers provide a &lt;strong&gt;decoupled HTTP-based execution model&lt;/strong&gt; that restores full concurrency and independence between requests.&lt;/p&gt;

&lt;h2&gt;
  
  
  Definition
&lt;/h2&gt;

&lt;p&gt;Route Handlers are standard HTTP endpoints defined in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;route&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ts&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;NextResponse&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;next/server&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;POST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;productId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;NextResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;success&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Key characteristics
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Full &lt;strong&gt;HTTP interface&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Support for all HTTP methods (&lt;code&gt;GET&lt;/code&gt;, &lt;code&gt;POST&lt;/code&gt;, etc.)&lt;/li&gt;
&lt;li&gt;Publicly accessible endpoints&lt;/li&gt;
&lt;li&gt;Independent of React rendering lifecycle&lt;/li&gt;
&lt;li&gt;Fully &lt;strong&gt;parallelizable and concurrent by default&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Good to know
&lt;/h2&gt;

&lt;p&gt;In older versions of Next.js there were only Route Handlers, as Server Action concept came with React Server Components in Next.js 13. So they are improving specific use cases, not replacing them. You can still write the whole application using only Route Handlers and not writing a single Server Action at all.&lt;/p&gt;

&lt;h1&gt;
  
  
  Why Use Server Actions?
&lt;/h1&gt;

&lt;p&gt;At this point, you might ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If Route Handlers provide full concurrency, HTTP control, and fewer constraints—why use Server Actions at all?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Server Actions aren't meant to replace Route Handlers. They solve a &lt;strong&gt;different class of problems&lt;/strong&gt;: &lt;strong&gt;UI-driven mutations tightly integrated with React's rendering model&lt;/strong&gt;, not transport-layer API design.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. They eliminate the client–server API layer
&lt;/h2&gt;

&lt;p&gt;With Route Handlers, every mutation typically requires:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;defining an API endpoint&lt;/li&gt;
&lt;li&gt;handling request/response manually&lt;/li&gt;
&lt;li&gt;writing client-side &lt;code&gt;fetch&lt;/code&gt; calls&lt;/li&gt;
&lt;li&gt;managing loading and error states explicitly&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Server Actions remove this entire layer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;form&lt;/span&gt; &lt;span class="na"&gt;action&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;createPost&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Create&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;form&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No more:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;manual &lt;code&gt;fetch&lt;/code&gt; calls&lt;/li&gt;
&lt;li&gt;endpoint definitions&lt;/li&gt;
&lt;li&gt;request parsing boilerplate&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The mutation becomes a &lt;strong&gt;direct extension of the UI&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. They're first-class citizens of React Server Components
&lt;/h2&gt;

&lt;p&gt;Server Actions are deeply integrated with the RSC model:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;They trigger &lt;strong&gt;server component re-renders&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;They return &lt;strong&gt;updated UI state without manual client synchronization&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;They work seamlessly with &lt;strong&gt;Next.js caching and revalidation primitives&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This creates a unique property:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A mutation can immediately reflect in the UI—without explicitly managing state on the client.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Route Handlers can't do this. They only return data.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. They reduce client-side complexity
&lt;/h2&gt;

&lt;p&gt;With Route Handlers, a typical mutation flow looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/api/posts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nf"&gt;setState&lt;/span&gt;&lt;span class="p"&gt;(...);&lt;/span&gt;
&lt;span class="nf"&gt;handleLoading&lt;/span&gt;&lt;span class="p"&gt;(...);&lt;/span&gt;
&lt;span class="nf"&gt;handleError&lt;/span&gt;&lt;span class="p"&gt;(...);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With Server Actions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;no manual request lifecycle&lt;/li&gt;
&lt;li&gt;no fetch boilerplate&lt;/li&gt;
&lt;li&gt;no explicit client-state synchronization layer&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This shifts complexity &lt;strong&gt;from the client to the framework boundary&lt;/strong&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;Server Actions are powerful but intentionally constrained.&lt;/p&gt;

&lt;p&gt;They are:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;React-integrated mutation primitives designed for UI-driven updates—not general-purpose concurrency.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Choosing between Server Actions and Route Handlers isn't about preference—it's about understanding whether your problem belongs to the &lt;strong&gt;UI layer or the transport/API layer&lt;/strong&gt;.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>react</category>
      <category>nextjs</category>
      <category>programming</category>
    </item>
    <item>
      <title>React Server and Client Components in Next.js: Understanding the Difference</title>
      <dc:creator>Paweł Sobolewski</dc:creator>
      <pubDate>Tue, 30 Dec 2025 23:00:00 +0000</pubDate>
      <link>https://dev.to/u11d/react-server-and-client-components-in-nextjs-understanding-the-difference-4bml</link>
      <guid>https://dev.to/u11d/react-server-and-client-components-in-nextjs-understanding-the-difference-4bml</guid>
      <description>&lt;p&gt;The React team introduced &lt;a href="https://react.dev/reference/rsc/server-components" rel="noopener noreferrer"&gt;&lt;strong&gt;Server Components&lt;/strong&gt;&lt;/a&gt; as an experimental feature in &lt;a href="https://react.dev/blog/2022/03/29/react-v18#server-components-is-still-in-development" rel="noopener noreferrer"&gt;React 18&lt;/a&gt;. They allow parts of a React application to render on the server, reducing the JavaScript sent to the client and enabling more efficient data fetching.&lt;/p&gt;

&lt;p&gt;Next.js &lt;a href="https://nextjs.org/docs/app/getting-started/server-and-client-components" rel="noopener noreferrer"&gt;adopted this concept&lt;/a&gt; and integrated it into its &lt;a href="https://nextjs.org/docs/app" rel="noopener noreferrer"&gt;&lt;strong&gt;App Router&lt;/strong&gt;&lt;/a&gt; (introduced in version 13). The framework combines React's server-rendering capabilities with its own routing, data fetching and performance optimizations.&lt;/p&gt;

&lt;p&gt;Server Components let developers build apps that are faster, more secure and easier to maintain—while still adding interactivity through Client Components where needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Are Server Components?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Server Components&lt;/strong&gt; run exclusively on the &lt;strong&gt;server&lt;/strong&gt;—they never execute in the browser and cannot use client-only features like &lt;code&gt;useState&lt;/code&gt;, &lt;code&gt;useEffect&lt;/code&gt;, or &lt;code&gt;window&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Since they render on the server, they can securely fetch data from databases or APIs and send back only the resulting HTML to the client. This reduces bundle size and improves performance.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Characteristics:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Run only on the server&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Exclude JavaScript&lt;/strong&gt; from the client bundle&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fetch data securely&lt;/strong&gt; without needing API routes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cannot use React state hooks&lt;/strong&gt; or browser APIs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Can import Client Components&lt;/strong&gt;, but Client Components cannot import them&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Example:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// This is a Server Component (default)&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;ServerComponent&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;posts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetchPosts&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;ul&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;ul&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By default, all components in the &lt;strong&gt;Next.js App Router&lt;/strong&gt; (&lt;code&gt;app/&lt;/code&gt; directory) are &lt;strong&gt;Server Components&lt;/strong&gt;, unless specified otherwise.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Are Client Components?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://react.dev/reference/rsc/use-client" rel="noopener noreferrer"&gt;&lt;strong&gt;Client Components&lt;/strong&gt;&lt;/a&gt; are traditional React components that run in the &lt;strong&gt;browser&lt;/strong&gt;. They're necessary when you need &lt;strong&gt;interactivity&lt;/strong&gt;, &lt;strong&gt;state&lt;/strong&gt;, or &lt;strong&gt;effects&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;To mark a file as a Client Component, add this directive at the top: &lt;code&gt;"use client";&lt;/code&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Here's a common misconception: &lt;strong&gt;Client Components are still prerendered on the server&lt;/strong&gt;. During the initial render, Next.js generates HTML for them on the server—just like Server Components. Once the JavaScript bundle loads in the browser, React &lt;strong&gt;hydrates&lt;/strong&gt; these components, attaching event listeners and state to make them interactive.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Example:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;use client&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Counter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setCount&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setCount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      Clicked &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; times
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Important:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Hydration errors&lt;/strong&gt; occur when the server-rendered HTML doesn't match what React renders in the browser—for example, when using &lt;code&gt;Math.random()&lt;/code&gt; or &lt;code&gt;Date.now()&lt;/code&gt; during render.&lt;/li&gt;
&lt;li&gt;Client Components &lt;strong&gt;cannot import Server Components&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;However, &lt;strong&gt;you can pass Server Components as props&lt;/strong&gt; to Client Components. React renders them on the server and includes them in the client-rendered tree (see below).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Combining Server and Client Components
&lt;/h2&gt;

&lt;p&gt;Next.js's true power lies in combining both component types effectively. You can fetch data on the server, then pass it to a client component for interactive rendering.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Server Component&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Counter&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./Counter&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// ✅ allowed (Client Component)&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Page&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetchUser&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Hello, &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Counter&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Passing Server Components as Props
&lt;/h3&gt;

&lt;p&gt;While &lt;strong&gt;Client Components cannot import Server Components&lt;/strong&gt;, a &lt;strong&gt;Server Component can pass another Server Component &lt;a href="https://nextjs.org/docs/app/getting-started/server-and-client-components" rel="noopener noreferrer"&gt;as a prop&lt;/a&gt;&lt;/strong&gt; to a Client Component.&lt;/p&gt;

&lt;p&gt;This works because the server renders the server-side part before sending it to the browser.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Server Component A&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;ServerInfo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Server time: &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toISOString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Client Component&lt;/span&gt;
&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;use client&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Wrapper&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;info&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"wrapper"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h2&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Wrapped content:&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h2&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;info&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Server Page&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ServerInfo&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./ServerInfo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Wrapper&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./Wrapper&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Page&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Wrapper&lt;/span&gt; &lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ServerInfo&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's what happens:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Wrapper&lt;/code&gt; is a Client Component.&lt;/li&gt;
&lt;li&gt;It &lt;strong&gt;does not import&lt;/strong&gt; &lt;code&gt;ServerInfo&lt;/code&gt; directly.&lt;/li&gt;
&lt;li&gt;Instead, &lt;code&gt;ServerInfo&lt;/code&gt; is &lt;strong&gt;rendered on the server&lt;/strong&gt; and passed as a serialized React node through props.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This pattern is powerful for &lt;strong&gt;composition&lt;/strong&gt;, &lt;strong&gt;layout customization&lt;/strong&gt;, and &lt;strong&gt;partial interactivity&lt;/strong&gt; in server-driven views.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hydration and Common Pitfalls
&lt;/h2&gt;

&lt;p&gt;Hydration is the process of React attaching interactivity to the static HTML produced by the server.&lt;/p&gt;

&lt;p&gt;It’s an essential step — but also a common source of confusion and bugs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Common Causes of Hydration Errors:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Non-deterministic rendering&lt;/strong&gt; — e.g., using &lt;code&gt;Math.random()&lt;/code&gt;, &lt;code&gt;Date.now()&lt;/code&gt;, or random IDs directly in render.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Conditional rendering differences&lt;/strong&gt; between server and client.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mismatched props&lt;/strong&gt; — if server and client data diverge during hydration.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  How to Avoid Them:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Use deterministic data in components that render both server- and client-side.&lt;/li&gt;
&lt;li&gt;Avoid rendering dynamic values that differ between server and client.&lt;/li&gt;
&lt;li&gt;Keep interactivity isolated in small, well-defined Client Components.&lt;/li&gt;
&lt;li&gt;Keep dynamic parts in “client only scope”, like useEffect hook&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Quick Comparison
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Feature&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Server Component&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Client Component&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Execution&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Server only&lt;/td&gt;
&lt;td&gt;Server prerender + Browser hydration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Can use hooks (&lt;code&gt;useState&lt;/code&gt;, &lt;code&gt;useEffect&lt;/code&gt;)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Can fetch data securely&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;⚠️ via API routes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Imports direction&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅&amp;nbsp;Can import Client&lt;/td&gt;
&lt;td&gt;❌ Cannot import Server&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Can receive Server Components as props&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Bundle size impact&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Minimal&lt;/td&gt;
&lt;td&gt;Increases bundle size&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SEO&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Excellent&lt;/td&gt;
&lt;td&gt;Also good (since prerendered)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Typical use&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Static content, data fetching&lt;/td&gt;
&lt;td&gt;Interactive UI, forms, animations&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;React introduced the Server/Client component model, giving developers precise control over &lt;strong&gt;where and how&lt;/strong&gt; rendering happens. Next.js enhances this with routing, data fetching, and performance optimizations.&lt;/p&gt;

&lt;p&gt;To summarize:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;strong&gt;Server Components&lt;/strong&gt; for data-heavy or static content.&lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;Client Components&lt;/strong&gt; for interactivity.&lt;/li&gt;
&lt;li&gt;Client Components are &lt;strong&gt;prerendered&lt;/strong&gt; on the server, then &lt;strong&gt;hydrated&lt;/strong&gt; in the browser.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Import flow:&lt;/strong&gt; Server → Client ✅, Client → Server ❌.&lt;/li&gt;
&lt;li&gt;However, &lt;strong&gt;Server Components can be passed to Client Components via props&lt;/strong&gt; ✅.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When used correctly, this architecture makes your app &lt;strong&gt;faster&lt;/strong&gt;, &lt;strong&gt;more secure&lt;/strong&gt;, and &lt;strong&gt;easier to maintain&lt;/strong&gt;—a true win for modern React development.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>react</category>
      <category>nextjs</category>
    </item>
    <item>
      <title>Next.js Streaming Explained: Faster Rendering vs CSR &amp; SSR</title>
      <dc:creator>Paweł Sobolewski</dc:creator>
      <pubDate>Wed, 15 Oct 2025 21:21:46 +0000</pubDate>
      <link>https://dev.to/u11d/nextjs-streaming-explained-faster-rendering-vs-csr-ssr-1369</link>
      <guid>https://dev.to/u11d/nextjs-streaming-explained-faster-rendering-vs-csr-ssr-1369</guid>
      <description>&lt;p&gt;Modern web apps need to load fast - users won’t wait for entire pages to render before engaging with content. Traditional methods like client-side rendering often introduce delays, especially when data fetching depends on large JavaScript bundles. &lt;a href="https://nextjs.org/docs/app/getting-started/fetching-data#streaming" rel="noopener noreferrer"&gt;&lt;strong&gt;Next.js streaming&lt;/strong&gt;&lt;/a&gt;, powered by React Server Components and Suspense, solves this by progressively sending page content to the browser as it becomes ready. The result: faster time-to-first-content and a smoother user experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Use Streaming in Next.js?
&lt;/h2&gt;

&lt;p&gt;Streaming isn't just another performance optimization - it solves specific problems in modern frontend development. Users today demand immediate feedback, but traditional rendering approaches often create delays that make interfaces feel unresponsive.&lt;/p&gt;

&lt;p&gt;By allowing the server to send parts of a page to the browser as they become ready, streaming eliminates waiting for complete page rendering. Users can view and interact with meaningful content almost immediately. This approach not only enhances perceived performance but also eliminates the frustration of watching loading spinners or staring at empty placeholders.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Imagine this scenario:&lt;/strong&gt; You have a large web page where most content is either static or quickly retrieved. However, one component depends on a slow external API. Without streaming, the entire page would be delayed while waiting for that single request. With streaming, the majority of the page appears immediately, with only the slower component arriving later. For users, this creates a fast, responsive experience, even when certain elements take longer to load.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next.js Streaming vs Client-Side Rendering (CSR)
&lt;/h2&gt;

&lt;p&gt;At first glance, streaming and client-side rendering seem to share a common goal: delivering content to users quickly without waiting for the entire page to load. However, they solve this problem in fundamentally different ways.&lt;/p&gt;

&lt;h3&gt;
  
  
  Client-Side Rendering (CSR)
&lt;/h3&gt;

&lt;p&gt;With CSR, the server sends only a minimal HTML shell and a JavaScript bundle. Since &lt;strong&gt;client components cannot be asynchronous&lt;/strong&gt;, they can't effectively fetch data during rendering. Instead, developers must rely on &lt;a href="https://react.dev/reference/react/useEffect" rel="noopener noreferrer"&gt;&lt;code&gt;useEffect&lt;/code&gt;&lt;/a&gt; for any asynchronous operations.&lt;/p&gt;

&lt;p&gt;This approach has three major drawbacks:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Larger bundles&lt;/strong&gt; – more logic and components must be sent to the browser.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Delayed data fetching&lt;/strong&gt; – network requests can only start &lt;em&gt;after&lt;/em&gt; the JavaScript has been downloaded, parsed, executed, and the component reaches the &lt;code&gt;useEffect&lt;/code&gt; stage. This significantly delays data retrieval compared to server-side methods.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tricky security&lt;/strong&gt; - because data fetching happens on the client, sensitive information like API keys may need to be exposed or handled carefully.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Streaming
&lt;/h3&gt;

&lt;p&gt;Streaming takes the opposite approach. With rendering and data fetching happening on the server, the application can start retrieving data immediately. As soon as any part of the page is ready, it's sent to the browser and displayed - no need to wait for the entire UI. This approach reduces time to first meaningful paint and eliminates the "waterfall" effect common in CSR.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Next.js Streaming Works with React Server Components
&lt;/h2&gt;

&lt;p&gt;In Next.js, streaming leverages &lt;a href="https://react.dev/reference/rsc/server-components" rel="noopener noreferrer"&gt;&lt;strong&gt;React Server Components&lt;/strong&gt;&lt;/a&gt; and &lt;a href="https://react.dev/reference/react/Suspense" rel="noopener noreferrer"&gt;&lt;strong&gt;Suspense&lt;/strong&gt;&lt;/a&gt; to progressively send HTML to the browser instead of waiting for the entire page to render.&lt;/p&gt;

&lt;p&gt;Here's the process:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Initial render:&lt;/strong&gt; The server immediately sends HTML for ready-to-display elements (layout, header, placeholders, etc.).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Suspense boundaries:&lt;/strong&gt; Components requiring slower data (API calls, database queries) are wrapped in special &lt;code&gt;&amp;lt;Suspense /&amp;gt;&lt;/code&gt; component, showing fallbacks like spinners or skeletons rather than blocking the entire page.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Progressive hydration:&lt;/strong&gt; Once data resolves, the server streams the completed HTML for that component, seamlessly replacing the fallback in the browser.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;JS hydration (in parallel):&lt;/strong&gt; Simultaneously, client-side JavaScript gradually hydrates the page to make it fully interactive.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Using the React &lt;code&gt;use&lt;/code&gt; Hook for Streaming in Next.js
&lt;/h2&gt;

&lt;p&gt;Next.js also enables streaming through the &lt;a href="https://react.dev/reference/react/use" rel="noopener noreferrer"&gt;&lt;strong&gt;&lt;code&gt;use&lt;/code&gt;&lt;/strong&gt;&lt;/a&gt; hook, which lets React components directly unwrap promises inside Client Components.&lt;/p&gt;

&lt;p&gt;The common implementation pattern works like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;strong&gt;Server Component&lt;/strong&gt; initiates the async operation and passes the promise as a prop.&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;Client Component&lt;/strong&gt; utilizes the &lt;code&gt;use&lt;/code&gt; hook to consume the data, suspending itself until the data is ready.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Why this matters:&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;With traditional streaming, the streamed component must be a &lt;strong&gt;Server Component&lt;/strong&gt; that fetches data. Client Components, by default, cannot be asynchronous, which prevents them from directly participating in streaming.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;use&lt;/code&gt; hook solves this limitation: it enables &lt;strong&gt;Client Components&lt;/strong&gt; to consume promises and suspend. This means you can stream data &lt;strong&gt;and&lt;/strong&gt; immediately add interactivity (such as event handlers and UI state) without needing to split logic between server and client.&lt;/li&gt;
&lt;li&gt;This gives developers greater flexibility: data loading still begins early on the server, while the final rendering and interactive elements can reside in Client Components.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Does Next.js Streaming Require JavaScript?
&lt;/h2&gt;

&lt;p&gt;It's important to understand that &lt;strong&gt;streaming doesn't eliminate the need for JavaScript in the browser&lt;/strong&gt;. While the server handles data fetching and initial HTML rendering, the browser still needs JavaScript to replace temporary placeholders - like spinners or skeletons - with the actual content as it arrives.&lt;/p&gt;

&lt;p&gt;Without JavaScript, users would only see the static placeholders, preventing the progressive content delivery from functioning, even though the server has already generated this content.&lt;/p&gt;

&lt;p&gt;In essence, streaming speeds up content delivery, but JavaScript remains essential for swapping temporary elements with the final rendered components.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next.js Streaming Example with Code (SSR vs CSR vs Streaming)
&lt;/h2&gt;

&lt;p&gt;Repository link: &lt;a href="https://github.com/u11d-com/blog_nextjs-streaming" rel="noopener noreferrer"&gt;https://github.com/u11d-com/blog_nextjs-streaming&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To demonstrate streaming benefits in a real-world context, I've built a very simple Next.js demo application that fetches and displays a list of funny jokes. This project compares three rendering strategies side by side: traditional Server-Side Rendering (SSR), Client-Side Rendering (CSR), and the newer React Server Components with streaming (with server and client components approaches).&lt;/p&gt;

&lt;p&gt;The application features separate pages for each rendering approach:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;SSR Page:&lt;/strong&gt; Renders everything on the server and delivers complete HTML in a single response. Users see content only after the server processes the entire page.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CSR Page:&lt;/strong&gt; Delivers a minimal HTML shell while JavaScript handles all data fetching on the client side. Users see loading states until data fetching and rendering complete.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Streaming Page:&lt;/strong&gt; Leverages React Server Components to progressively send UI parts to the browser as they become ready. This allows users to interact with visible content while slower components continue loading.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Streaming Page with &lt;code&gt;use&lt;/code&gt; Hook:&lt;/strong&gt; Uses the React &lt;code&gt;use&lt;/code&gt; hook to stream data directly into Client Components. This enables not only progressive rendering but also immediate interactivity, since Client Components can both consume async data and attach event handlers without waiting for a full page load.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The application includes a built-in analytics system that tracks rendering times for each page section, helping visualize the performance differences between SSR, CSR, and streaming approaches.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Event&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;SSR (ms)&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;CSR (ms)&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;STREAMING (ms)&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;STREAMING WITH USE (ms)&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Fetching jokes started&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;281&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Joke list rendered&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;2451&lt;/td&gt;
&lt;td&gt;2545&lt;/td&gt;
&lt;td&gt;2255&lt;/td&gt;
&lt;td&gt;2418&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Page rendered&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;2722&lt;/td&gt;
&lt;td&gt;281&lt;/td&gt;
&lt;td&gt;272&lt;/td&gt;
&lt;td&gt;285&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;You can fetch and run the application Yourself, but let’s talk about results and what they mean.&lt;/p&gt;

&lt;h3&gt;
  
  
  Next.js Performance Benchmark: SSR vs CSR vs Streaming
&lt;/h3&gt;

&lt;p&gt;Our benchmark clearly shows how each rendering strategy differs in data fetching timing and content delivery.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SSR (Server-Side Rendering)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Fetching jokes started:&lt;/strong&gt; ~3 ms (almost immediately)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Joke list rendered:&lt;/strong&gt; ~2451 ms&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Page rendered:&lt;/strong&gt; ~2722 ms&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Analysis:&lt;/strong&gt; While data fetching begins instantly, users see nothing until all data is processed. This creates a slower &lt;em&gt;first paint&lt;/em&gt;, despite the server's early work.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CSR (Client-Side Rendering)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Fetching jokes started:&lt;/strong&gt; ~281 ms&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Page rendered:&lt;/strong&gt; ~281 ms (initial skeleton/spinner)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Joke list rendered:&lt;/strong&gt; ~2545 ms&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Analysis:&lt;/strong&gt; With CSR, data fetching waits for JavaScript to load (~281 ms). Users see a placeholder quickly but wait over 2.5 seconds for actual content.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; Larger, more complex pages mean bigger JS bundles that take longer to execute. This delays data fetching even further, increasing time to meaningful content and hurting user experience.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Streaming&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Fetching jokes started:&lt;/strong&gt; ~4 ms&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Page rendered:&lt;/strong&gt; ~272 ms&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Joke list rendered:&lt;/strong&gt; ~2255 ms&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Analysis:&lt;/strong&gt; Streaming offers the best experience: data fetching starts immediately, initial content appears in ~272 ms, and slower components load progressively.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key advantage over CSR:&lt;/strong&gt; Streaming &lt;strong&gt;doesn't depend on JavaScript bundle size&lt;/strong&gt; to begin fetching data. This allows even complex pages to start loading meaningful content almost instantly, dramatically improving perceived performance.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion: Why Streaming is Best for Next.js Performance
&lt;/h2&gt;

&lt;p&gt;Comparing SSR, CSR, and Streaming in Next.js reveals distinct trade-offs for each approach:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;SSR&lt;/strong&gt; begins data fetching immediately on the server, but delivers a slower &lt;em&gt;time-to-first-content&lt;/em&gt; for users.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CSR&lt;/strong&gt; postpones data fetching until after JavaScript bundle execution, with larger pages further extending this delay - resulting in the longest wait for meaningful content.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Streaming&lt;/strong&gt; offers the best of both worlds: nearly instant data fetching, quick initial content rendering, and progressive component delivery. Because it doesn't depend on bundle size to start loading data, streaming ensures fast perceived performance even with complex pages.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Bottom line:&lt;/strong&gt; For modern Next.js applications where user experience and speed are priorities, streaming provides the most responsive and scalable solution.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ Section
&lt;/h2&gt;

&lt;p&gt;Frequently Asked Questions (FAQ) about Next.js Streaming&lt;/p&gt;

&lt;p&gt;Q: What is streaming in Next.js?&lt;/p&gt;

&lt;p&gt;A: Streaming is a rendering technique where the server sends parts of a page to the browser as soon as they’re ready. Instead of waiting for the entire page to render, users see meaningful content almost immediately, improving perceived performance.&lt;/p&gt;

&lt;p&gt;Q: How is streaming different from client-side rendering (CSR)?&lt;/p&gt;

&lt;p&gt;A: CSR waits until the JavaScript bundle loads before fetching data, delaying meaningful content. Streaming starts data fetching on the server instantly and progressively streams components, making the page feel faster and more responsive.&lt;/p&gt;

&lt;p&gt;Q: Does streaming replace SSR (Server-Side Rendering)?&lt;/p&gt;

&lt;p&gt;A: No. Streaming builds on SSR by adding progressive delivery. SSR sends a complete page after rendering, while streaming sends it in chunks, letting users see content earlier.&lt;/p&gt;

&lt;p&gt;Q: Do I still need JavaScript with streaming?&lt;/p&gt;

&lt;p&gt;A: Yes. While the server handles data fetching and initial HTML, JavaScript is required to replace placeholders with the actual streamed content and hydrate the UI for interactivity.&lt;/p&gt;

&lt;p&gt;Q: What is the use hook in streaming?&lt;/p&gt;

&lt;p&gt;A: The use hook allows React Client Components to directly consume promises. This means you can stream data into interactive Client Components without splitting logic between server and client.&lt;/p&gt;

&lt;p&gt;Q: When should I use streaming in my Next.js app?&lt;/p&gt;

&lt;p&gt;A: Use streaming when your page contains a mix of fast-loading and slow-loading content. It ensures users can interact with available content immediately while slower components continue to load in the background.&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>webdev</category>
      <category>performance</category>
      <category>react</category>
    </item>
    <item>
      <title>SSG, ISR, SSR, CSR: which strategy should I use in my Next.js e-commerce platform?</title>
      <dc:creator>Paweł Sobolewski</dc:creator>
      <pubDate>Mon, 29 Sep 2025 09:53:02 +0000</pubDate>
      <link>https://dev.to/u11d/ssg-isr-ssr-csr-which-strategy-should-i-use-in-my-nextjs-e-commerce-platform-4kjh</link>
      <guid>https://dev.to/u11d/ssg-isr-ssr-csr-which-strategy-should-i-use-in-my-nextjs-e-commerce-platform-4kjh</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In today's evolving web development landscape, selecting the right rendering strategy is vital for creating fast, scalable, and user-friendly applications. &lt;a href="https://nextjs.org/" rel="noopener noreferrer"&gt;Next.js&lt;/a&gt;, a leading &lt;a href="https://react.dev/" rel="noopener noreferrer"&gt;React&lt;/a&gt; framework, offers four powerful major rendering options: &lt;strong&gt;Static Site Generation&lt;/strong&gt; (SSG), &lt;strong&gt;Incremental Static Regeneration&lt;/strong&gt; (ISR), &lt;strong&gt;Server-Side Rendering&lt;/strong&gt; (SSR), and &lt;strong&gt;Client-Side Rendering&lt;/strong&gt; (CSR). Each approach comes with its own set of features and trade-offs.&lt;/p&gt;

&lt;p&gt;For e-commerce platforms, where performance, SEO, and content freshness directly impact business success, choosing the optimal rendering method is critical. This choice can dramatically affect page load speeds, search engine visibility, and—by extension—user satisfaction and conversion rates.&lt;/p&gt;

&lt;p&gt;With Next.js 13, the introduction of the &lt;strong&gt;App Router&lt;/strong&gt; brings a new routing paradigm centered on &lt;a href="https://nextjs.org/docs/app/getting-started/server-and-client-components" rel="noopener noreferrer"&gt;&lt;strong&gt;React Server Components&lt;/strong&gt;&lt;/a&gt;. This update offers enhanced flexibility and finer control over rendering strategies. While SSG, ISR, SSR, and CSR remain as core concepts, their implementation now relies primarily on caching behavior and component-level dynamic data fetching.&lt;/p&gt;

&lt;p&gt;In this article, we explore how &lt;strong&gt;SSG&lt;/strong&gt;, &lt;strong&gt;ISR&lt;/strong&gt;, &lt;strong&gt;SSR&lt;/strong&gt;, and &lt;strong&gt;CSR&lt;/strong&gt; function in &lt;strong&gt;Next.js 13&lt;/strong&gt; and later versions. We'll examine their strengths and limitations to help you make informed decisions when building or improving e-commerce platforms.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Static Rendering vs Dynamic Rendering
&lt;/h2&gt;

&lt;p&gt;In Next.js, page rendering depends on &lt;strong&gt;when&lt;/strong&gt; and &lt;strong&gt;how&lt;/strong&gt; HTML is generated: either at build time (static) or at request time (dynamic). With the App Router, Next.js automatically selects the optimal rendering strategy based on how components fetch and handle data.&lt;/p&gt;

&lt;p&gt;By default, Next.js uses &lt;a href="https://nextjs.org/docs/app/getting-started/partial-prerendering#static-rendering" rel="noopener noreferrer"&gt;&lt;strong&gt;static rendering&lt;/strong&gt;&lt;/a&gt; when possible to maximize performance and scalability. For pages requiring fresh or user-specific data, it switches to &lt;a href="https://nextjs.org/docs/app/getting-started/partial-prerendering#dynamic-rendering" rel="noopener noreferrer"&gt;&lt;strong&gt;dynamic rendering&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  ⚙️ What triggers dynamic rendering?
&lt;/h3&gt;

&lt;p&gt;Next.js switches to dynamic rendering in these major cases:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;Effect&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;fetch()&lt;/code&gt; with &lt;code&gt;cache: 'no-store'&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Forces Server-Side Rendering (SSR) — no caching&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;fetch()&lt;/code&gt; with &lt;code&gt;next: { revalidate: 0 }&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Also disables caching — triggers SSR&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;export const dynamic = 'force-dynamic'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Explicitly instructs Next.js to use SSR&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Use of dynamic functions like &lt;code&gt;cookies()&lt;/code&gt;, &lt;code&gt;headers()&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Makes page request-specific — disables static rendering&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Use &lt;code&gt;params&lt;/code&gt; prop without &lt;code&gt;generateStaticParams()&lt;/code&gt; and proper &lt;code&gt;dynamicParams()&lt;/code&gt; settings&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;&lt;a href="https://nextjs.org/docs/app/api-reference/functions/generate-static-params" rel="noopener noreferrer"&gt;generateStaticParams()&lt;/a&gt;&lt;/code&gt; allows pre-rendering of specific parameter values at build time, while remaining paths will be handled via SSR or return a 404, depending on the &lt;code&gt;&lt;a href="https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config#dynamicparams" rel="noopener noreferrer"&gt;dynamicParams()&lt;/a&gt;&lt;/code&gt; configuration &lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  2. Static Site Generation (SSG)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Static Site Generation (SSG)&lt;/strong&gt; generates pages at build time and serves them as static HTML files. This approach delivers exceptionally fast load times, high reliability, and excellent scalability because content is prepared before any user requests and can be efficiently distributed through CDNs.&lt;/p&gt;

&lt;h3&gt;
  
  
  ✅ Key Advantages
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;⚡ &lt;strong&gt;Performance&lt;/strong&gt;: Pages load instantly from CDN or edge cache.&lt;/li&gt;
&lt;li&gt;🔒 &lt;strong&gt;Stability&lt;/strong&gt;: No server-side rendering needed, minimizing potential failures.&lt;/li&gt;
&lt;li&gt;🔍 &lt;strong&gt;SEO-ready&lt;/strong&gt;: Complete HTML available immediately for search engines.&lt;/li&gt;
&lt;li&gt;📈 &lt;strong&gt;Scalable&lt;/strong&gt;: Perfect for high-traffic pages due to pre-rendered content.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ⚠️ Limitations
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Content stays static between builds, updates requires additional application build.&lt;/li&gt;
&lt;li&gt;Not ideal for pages needing real-time updates or personalization.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Given these advantages, ideal candidates for SSG in &lt;strong&gt;e-commerce&lt;/strong&gt; include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;promotional or campaign landing pages,&lt;/li&gt;
&lt;li&gt;static marketing and brand content (e.g., about pages, policies, shipping info).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Product listing and details pages can also benefit from SSG when their content updates infrequently.&lt;/p&gt;

&lt;h3&gt;
  
  
  🛠️ Implementation
&lt;/h3&gt;

&lt;p&gt;Let's examine how to implement a static product details page.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./ProductPage.css&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Product&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mockProducts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;T-Shirt&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Comfortable cotton t-shirt.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Sneakers&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Lightweight running shoes.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;3&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Watch&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Elegant analog wristwatch.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;4&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Backpack&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Durable everyday backpack.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;5&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Sunglasses&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Polarized UV protection.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;6&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Hat&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Stylish summer hat.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;7&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Jacket&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Windproof outdoor jacket.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;8&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Jeans&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Slim-fit denim jeans.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;9&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Scarf&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Warm wool scarf.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;10&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Boots&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Waterproof hiking boots.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dynamicParams&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;generateStaticParams&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;mockProducts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;ProductPage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;product&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;mockProducts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Since dynamicParams = false, this will never be rendered unless id is valid.&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;product-page&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;product-card&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h1&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;product-title&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h1&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;product-description&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;product-button&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Add&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;cart&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/main&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;app/product/ssg/[id]/page.tsx&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  🧠 Explanation
&lt;/h3&gt;

&lt;p&gt;In the example above, we use &lt;strong&gt;Static Site Generation (SSG)&lt;/strong&gt; to pre-render individual product pages at build time. Let's explore the key elements of the implementation:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;generateStaticParams()&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This asynchronous function tells Next.js &lt;strong&gt;which dynamic routes to pre-render&lt;/strong&gt;. In our case, we hardcode 10 mock products, but it could be as well a call to Your database. Result would be the same: only these 10 product pages (e.g., &lt;code&gt;/product/ssg/1&lt;/code&gt;, &lt;code&gt;/product/ssg/2&lt;/code&gt;, ..., &lt;code&gt;/product/ssg/10&lt;/code&gt;) will be built statically.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;server
├── app
│&amp;nbsp;&amp;nbsp; ├── _not-found
│&amp;nbsp;&amp;nbsp; └── favicon.ico
│&amp;nbsp;&amp;nbsp; └── product
│&amp;nbsp;&amp;nbsp;        └── ssg
│&amp;nbsp;&amp;nbsp;             └── &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
│&amp;nbsp;&amp;nbsp;                  └── 1.html
│&amp;nbsp;&amp;nbsp;                  └── 2.html
│&amp;nbsp;&amp;nbsp;                  └── 3.html
│&amp;nbsp;&amp;nbsp;                  └── 4.html
│&amp;nbsp;&amp;nbsp;                  └── 5.html
│&amp;nbsp;&amp;nbsp;                  └── 6.html
│&amp;nbsp;&amp;nbsp;                  └── 7.html
│&amp;nbsp;&amp;nbsp;                  └── 8.html
│&amp;nbsp;&amp;nbsp;                  └── 9.html
│&amp;nbsp;&amp;nbsp;                  └── 10.html
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;dynamicParams = false&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;By setting this flag, we &lt;strong&gt;explicitly instruct Next.js to return a 404 error for any route that wasn't generated&lt;/strong&gt; by &lt;code&gt;generateStaticParams()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/product/ssg/3&lt;/code&gt; → ✅ built at build time&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/product/ssg/999&lt;/code&gt; → ❌ will automatically render the 404 page&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Although we use the dynamic &lt;code&gt;params&lt;/code&gt; prop, pairing it with &lt;code&gt;generateStaticParams()&lt;/code&gt; enables complete SSG functionality — eliminating runtime rendering and the need for manual product validation in the component.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Incremental Static Regeneration (ISR)
&lt;/h2&gt;

&lt;p&gt;What if our static product pages need frequent updates—daily or weekly? This is where ISR comes into play.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://nextjs.org/docs/app/guides/incremental-static-regeneration" rel="noopener noreferrer"&gt;&lt;strong&gt;Incremental Static Regeneration (ISR)&lt;/strong&gt;&lt;/a&gt; allows static pages to update in the background after deployment. It combines the performance benefits of Static Site Generation (SSG) with dynamic content updates—all without requiring a full rebuild.&lt;/p&gt;

&lt;p&gt;For e-commerce sites where product data changes regularly—such as prices, availability, or descriptions—ISR offers an ideal solution when updates are needed more frequently than full deployments allow.&lt;/p&gt;

&lt;h3&gt;
  
  
  ❓How it works
&lt;/h3&gt;

&lt;p&gt;ISR generates pages at build time (like SSG), but includes a revalidation period. After this period expires, the next user request triggers a background regeneration. The new version is then cached and served to subsequent visitors.&lt;/p&gt;

&lt;h3&gt;
  
  
  ✅ Key Advantages
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;🧠 &lt;strong&gt;Hybrid flexibility&lt;/strong&gt;: Combines SSG benefits with content freshness.&lt;/li&gt;
&lt;li&gt;🔁 &lt;strong&gt;On-demand freshness&lt;/strong&gt;: Updates pages without full redeployment.&lt;/li&gt;
&lt;li&gt;🚀 &lt;strong&gt;Performance first&lt;/strong&gt;: Serves pre-rendered static content to most users.&lt;/li&gt;
&lt;li&gt;🔧 &lt;strong&gt;Control via revalidation&lt;/strong&gt;: Customisable content staleness thresholds.&lt;/li&gt;
&lt;li&gt;🛠️ &lt;strong&gt;Programmatic control&lt;/strong&gt;: Immediate page updates using &lt;a href="https://nextjs.org/docs/app/api-reference/functions/revalidatePath" rel="noopener noreferrer"&gt;&lt;code&gt;revalidatePath()&lt;/code&gt;&lt;/a&gt;—perfect for admin panels or CMS triggers.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ⚠️ Limitations
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Slightly higher complexity compared to pure SSG.&lt;/li&gt;
&lt;li&gt;Regeneration occurs &lt;strong&gt;after&lt;/strong&gt; a stale page request.&lt;/li&gt;
&lt;li&gt;Revalidation cannot guarantee real-time consistency across all users.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  🛠️ Implementation
&lt;/h3&gt;

&lt;p&gt;Let's modify our previous product details page to implement ISR with hourly updates.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;notFound&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;next/navigation&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./ProductPage.css&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Product&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mockProducts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;T-Shirt&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Comfortable cotton t-shirt.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Sneakers&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Lightweight running shoes.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;3&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Watch&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Elegant analog wristwatch.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;4&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Backpack&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Durable everyday backpack.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;5&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Sunglasses&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Polarized UV protection.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;6&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Hat&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Stylish summer hat.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;7&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Jacket&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Windproof outdoor jacket.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;8&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Jeans&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Slim-fit denim jeans.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;9&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Scarf&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Warm wool scarf.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;10&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Boots&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Waterproof hiking boots.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;generateStaticParams&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;mockProducts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// ISR configuration: revalidate every 1 hour&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;revalidate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3600&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;ProductPage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;product&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;mockProducts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;notFound&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;product-page&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;product-card&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h1&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;product-title&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h1&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;product-description&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;product-button&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Add&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;cart&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/main&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;app/product/isr/[id]/page.tsx&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;export const revalidate = 3600&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This line enables &lt;strong&gt;ISR&lt;/strong&gt; with a 1-hour (3,600 seconds) revalidation period. During this time, pages are served from cache. Once the period expires, the next user request triggers a background page regeneration.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;generateStaticParams()&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;As in the SSG example, we pre-generate pages for our 10 core products. These pages are built at build time, served as static HTML, and updated through ISR when needed.&lt;/p&gt;

&lt;p&gt;Removed &lt;code&gt;dynamicParams = false&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Unlike our SSG example where we used &lt;code&gt;dynamicParams = false&lt;/code&gt; to limit access to statically generated routes, this constraint is now removed. This means &lt;strong&gt;Next.js will attempt to render any route matching the dynamic pattern&lt;/strong&gt; (&lt;code&gt;/product/isr/[id]&lt;/code&gt;), regardless of whether it was included in &lt;code&gt;generateStaticParams()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This enables us to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Serve product pages for new items &lt;strong&gt;without rebuilding the app&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Dynamically generate pages for long-tail or user-generated content on demand.&lt;/li&gt;
&lt;li&gt;Return &lt;code&gt;404&lt;/code&gt; conditionally when a product ID doesn't exist in the data source.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This flexibility makes the route perfect for hybrid/static-dynamic behavior—the essence of ISR.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Server Side Rendering (SSR)
&lt;/h2&gt;

&lt;p&gt;So far, we've looked at &lt;strong&gt;Static Site Generation (SSG)&lt;/strong&gt; — where pages are pre-built during deployment — and &lt;strong&gt;Incremental Static Regeneration (ISR)&lt;/strong&gt;, which adds scheduled or on-demand revalidation on top of SSG.&lt;/p&gt;

&lt;p&gt;But what happens when your data changes constantly, depends on user authentication, or simply can't be predicted at build time?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Server-Side Rendering (SSR)&lt;/strong&gt; is the answer.&lt;/p&gt;

&lt;p&gt;With SSR, pages are rendered on the server &lt;strong&gt;at request time&lt;/strong&gt; for each user visit. Rather than serving pre-rendered HTML from a cache, the server processes every incoming request individually, ensuring the data is always fresh.&lt;/p&gt;

&lt;h3&gt;
  
  
  ✅ Key Advantages
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;🔄 &lt;strong&gt;Real-time data&lt;/strong&gt;: Fresh content is fetched for each request, eliminating stale data.&lt;/li&gt;
&lt;li&gt;👤 &lt;strong&gt;Personalization&lt;/strong&gt;: Perfect for user-specific content like account pages and dashboards.&lt;/li&gt;
&lt;li&gt;🔍 &lt;strong&gt;SEO optimization&lt;/strong&gt;: Search engines receive fully rendered HTML with the latest content.&lt;/li&gt;
&lt;li&gt;🔧 &lt;strong&gt;Dynamic control&lt;/strong&gt;: Data fetching happens on demand — without rebuilds or revalidation.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ⚠️ Limitations
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;🐢 &lt;strong&gt;Performance impact&lt;/strong&gt;: Pages load more slowly than SSG/ISR since HTML must be generated for each request.&lt;/li&gt;
&lt;li&gt;🧱 &lt;strong&gt;Scaling complexity&lt;/strong&gt;: High traffic can strain server resources and increase response times.&lt;/li&gt;
&lt;li&gt;💸 &lt;strong&gt;Higher costs&lt;/strong&gt;: Increased server processing leads to greater infrastructure expenses.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  🛠️ Implementation
&lt;/h3&gt;

&lt;p&gt;Let's now modify our previous product details page to implement SSR, which will dynamically render the page each time an user requests it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;notFound&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;next/navigation&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./ProductPage.css&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Product&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mockProducts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;T-Shirt&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Comfortable cotton t-shirt.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Sneakers&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Lightweight running shoes.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;3&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Watch&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Elegant analog wristwatch.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;4&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Backpack&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Durable everyday backpack.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;5&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Sunglasses&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Polarized UV protection.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;6&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Hat&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Stylish summer hat.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;7&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Jacket&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Windproof outdoor jacket.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;8&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Jeans&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Slim-fit denim jeans.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;9&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Scarf&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Warm wool scarf.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;10&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Boots&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Waterproof hiking boots.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;ProductPage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;product&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;mockProducts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;notFound&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;product-page&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;product-card&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h1&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;product-title&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h1&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;product-description&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;product-button&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Add&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;cart&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/main&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;app/product/ssr/[id]/page.tsx&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  🧠 Explanation
&lt;/h3&gt;

&lt;p&gt;No &lt;code&gt;generateStaticParams()&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Unlike &lt;strong&gt;SSG&lt;/strong&gt; and &lt;strong&gt;ISR&lt;/strong&gt;, this implementation doesn’t use &lt;code&gt;generateStaticParams()&lt;/code&gt; at all. The route is fully dynamic — there's no list of valid product IDs generated at build time.&lt;/p&gt;

&lt;p&gt;No &lt;code&gt;dynamicParams&lt;/code&gt; or &lt;code&gt;revalidate&lt;/code&gt; flags&lt;/p&gt;

&lt;p&gt;Since this page is rendered on every request, there’s no need to configure &lt;code&gt;dynamicParams&lt;/code&gt; or &lt;code&gt;revalidate&lt;/code&gt;. Each time the page is accessed, Next.js will evaluate the component on the server.&lt;/p&gt;

&lt;h1&gt;
  
  
  5. Client-Side Rendering (CSR)
&lt;/h1&gt;

&lt;p&gt;We've now covered &lt;strong&gt;Static Site Generation (SSG)&lt;/strong&gt;, &lt;strong&gt;Incremental Static Regeneration (ISR)&lt;/strong&gt;, and &lt;strong&gt;Server-Side Rendering (SSR)&lt;/strong&gt;—all involving server-side logic or pre-rendering. But there's one more model to explore: &lt;strong&gt;Client-Side Rendering (CSR)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;With CSR, the server sends a minimal HTML shell and JavaScript bundle to the browser. The browser then renders the page content &lt;strong&gt;entirely on the client&lt;/strong&gt; by fetching data from an API or other source. This powers traditional Single Page Applications (SPAs) and remains a valid strategy in Next.js when used strategically.&lt;/p&gt;

&lt;p&gt;While Client-Side Rendering is fundamental to React development, it's &lt;strong&gt;generally not ideal as a default rendering strategy at the page level&lt;/strong&gt; in Next.js—particularly for content requiring strong SEO or fast initial visibility. Instead, CSR excels with &lt;strong&gt;specific, interactive components&lt;/strong&gt; like filters, carousels, modals, or client-specific widgets.&lt;/p&gt;

&lt;h3&gt;
  
  
  ✅ Key Advantages
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;🧠 &lt;strong&gt;Enhanced interactivity&lt;/strong&gt;: Perfect for dynamic, app-like experiences where user interaction is crucial.&lt;/li&gt;
&lt;li&gt;🧍 &lt;strong&gt;User-specific data&lt;/strong&gt;: Content personalizes directly in the browser without server-side processing.&lt;/li&gt;
&lt;li&gt;🚀 &lt;strong&gt;Seamless navigation&lt;/strong&gt;: Next.js' built-in routing and client caching enable instant page transitions.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ⚠️ Limitations
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;🐢 &lt;strong&gt;Slower initial load&lt;/strong&gt;: Users must wait for JavaScript to load and data fetching to complete before seeing content.&lt;/li&gt;
&lt;li&gt;🔍 &lt;strong&gt;SEO limitations&lt;/strong&gt;: Search engines may struggle to index client-rendered content, despite improvements in modern crawlers.&lt;/li&gt;
&lt;li&gt;🔌 &lt;strong&gt;JavaScript dependency&lt;/strong&gt;: The page becomes non-functional if JavaScript is disabled or fails to load.&lt;/li&gt;
&lt;li&gt;📦 &lt;strong&gt;Larger JS bundles&lt;/strong&gt;: Additional client-side logic increases bundle size, affecting performance on slower devices and networks.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ⚠️ &lt;strong&gt;Important Note&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Even though Client Components run primarily in the browser, they are &lt;strong&gt;still pre-rendered on the server&lt;/strong&gt; during the initial request. Next.js uses this approach to deliver a minimal HTML shell, which improves initial load performance and perceived speed.&lt;/p&gt;

&lt;p&gt;After the HTML arrives, &lt;strong&gt;JavaScript "&lt;a href="https://react.dev/reference/react-dom/client/hydrateRoot" rel="noopener noreferrer"&gt;hydrates&lt;/a&gt;" the components on the client&lt;/strong&gt;, making them fully interactive.&lt;/p&gt;

&lt;p&gt;🧠 &lt;strong&gt;Be careful:&lt;/strong&gt; When server-rendered markup doesn't match client-side JavaScript during hydration, you may encounter &lt;strong&gt;hydration errors&lt;/strong&gt;. These errors can break your UI or trigger console warnings. Common causes include non-deterministic values (like &lt;code&gt;Date.now()&lt;/code&gt;, &lt;code&gt;Math.random()&lt;/code&gt;) or mismatched state between server and client.&lt;/p&gt;

&lt;h3&gt;
  
  
  🛠️ Implementation
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;use client&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useParams&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;next/navigation&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../../ProductPage.css&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Product&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mockProducts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;T-Shirt&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Comfortable cotton t-shirt.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Sneakers&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Lightweight running shoes.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;3&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Watch&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Elegant analog wristwatch.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;4&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Backpack&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Durable everyday backpack.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;5&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Sunglasses&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Polarized UV protection.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;6&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Hat&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Stylish summer hat.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;7&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Jacket&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Windproof outdoor jacket.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;8&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Jeans&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Slim-fit denim jeans.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;9&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Scarf&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Warm wool scarf.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;10&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Boots&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Waterproof hiking boots.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;ProductPage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useParams&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setProduct&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Product&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setLoading&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;fetchProduct&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// Simulate network delay&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;product&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Product&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mockProducts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="nf"&gt;setProduct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nf"&gt;setLoading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nf"&gt;fetchProduct&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;product-page&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;loading-message&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Loading&lt;/span&gt; &lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/main&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;product-page&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;product-not-found&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Product&lt;/span&gt; &lt;span class="nx"&gt;not&lt;/span&gt; &lt;span class="nx"&gt;found&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/main&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;product-page&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;product-card&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h1&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;product-title&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h1&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;product-description&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;product-button&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Add&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;cart&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/main&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;app/product/csr/[id]/page.tsx&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  🧠 Explanation
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;All logic is inside &lt;code&gt;useEffect()&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Unlike SSG, ISR, or SSR where data fetching happens on the server, in CSR &lt;strong&gt;everything happens on the client&lt;/strong&gt;, and all logic related to fetching product data is encapsulated inside a &lt;code&gt;useEffect()&lt;/code&gt; hook. This hook triggers after the component mounts and retrieves the necessary data based on the URL parameter.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://nextjs.org/docs/app/api-reference/directives/use-client" rel="noopener noreferrer"&gt;**&lt;code&gt;"use client"&lt;/code&gt;&lt;/a&gt; directive**&lt;/p&gt;

&lt;p&gt;This directive tells Next.js to treat the component as a client component, allowing hooks like &lt;strong&gt;useState&lt;/strong&gt;, &lt;strong&gt;useEffect&lt;/strong&gt; and &lt;strong&gt;useParams&lt;/strong&gt; to function properly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Route handling with &lt;code&gt;useParams()&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Instead of receiving &lt;code&gt;params&lt;/code&gt; from the server (like in SSG/SSR), the &lt;code&gt;id&lt;/code&gt; is pulled from the client-side router using &lt;code&gt;useParams()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Custom loading state&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;As right now browser is not handling a loading state by itself (by loading the page content) there is a need to implement a custom one. To simulate real network latency &lt;code&gt;setTimeout&lt;/code&gt; delay has been used. This mimics how a real client would experience remote API requests.&lt;/p&gt;

&lt;h2&gt;
  
  
  🧾 6. Summary: Choosing the Right Rendering Strategy in Next.js for E-commerce
&lt;/h2&gt;

&lt;p&gt;Next.js offers four &lt;strong&gt;major&lt;/strong&gt; powerful rendering strategies—Static Site Generation (&lt;strong&gt;SSG&lt;/strong&gt;), Incremental Static Regeneration (&lt;strong&gt;ISR&lt;/strong&gt;), Server-Side Rendering (&lt;strong&gt;SSR&lt;/strong&gt;) and Client-Side Rendering (&lt;strong&gt;CSR&lt;/strong&gt;). Each strategy has unique benefits, and choosing the right one for each route or component lets you optimize for performance, SEO, interactivity, and scalability.&lt;/p&gt;

&lt;p&gt;Here's how they compare:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;th&gt;Rendering Strategy&lt;/th&gt;
&lt;th&gt;Render Time&lt;/th&gt;
&lt;th&gt;Best For&lt;/th&gt;
&lt;th&gt;Pros&lt;/th&gt;
&lt;th&gt;Cons&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SSG&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Build time&lt;/td&gt;
&lt;td&gt;Static pages, rarely updated product details&lt;/td&gt;
&lt;td&gt;
🔹 Fastest performance&lt;br&gt;
🔹 Great for SEO&lt;br&gt;
🔹 CDN-ready
&lt;/td&gt;
&lt;td&gt;
🔸 Requires rebuilds for updates&lt;br&gt;
🔸 Not suitable for live data
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ISR&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Build + Revalidate&lt;/td&gt;
&lt;td&gt;Product pages updated periodically or on demand&lt;/td&gt;
&lt;td&gt;
🔹 Balance of freshness &amp;amp; speed&lt;br&gt;
🔹 Supports invalidation
&lt;/td&gt;
&lt;td&gt;
🔸 Slightly stale content possible&lt;br&gt;
🔸 More setup complexity
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SSR&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Per request&lt;/td&gt;
&lt;td&gt;Dynamic content, personalization, stock/pricing&lt;/td&gt;
&lt;td&gt;
🔹 Always up-to-date&lt;br&gt;
🔹 Supports user-specific logic
&lt;/td&gt;
&lt;td&gt;
🔸 Slower first load&lt;br&gt;
🔸 Requires server infrastructure
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CSR&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Client-only&lt;/td&gt;
&lt;td&gt;Isolated, interactive components (e.g. cart)&lt;/td&gt;
&lt;td&gt;
🔹 Full interactivity&lt;br&gt;
🔹 Useful for client-only logic
&lt;/td&gt;
&lt;td&gt;
🔸 SEO limitations&lt;br&gt;
🔸 Slower initial render&lt;br&gt;
🔸 Larger JS bundle
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  🛒 E-commerce Use Case Recommendations
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Landing pages, policies, info pages:&lt;/strong&gt; → ✅ &lt;strong&gt;SSG&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Product listings/details with moderate updates:&lt;/strong&gt; → ✅ &lt;strong&gt;ISR&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Product listings/details with frequent updates, personalized prices, live inventory, and A/B tests:&lt;/strong&gt; → ✅ &lt;strong&gt;SSR&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Client-only interactions (cart, wishlist, filters):&lt;/strong&gt; → ✅ &lt;strong&gt;CSR&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🔭 7. What’s Next?
&lt;/h2&gt;

&lt;p&gt;The rendering strategies discussed in this article — &lt;strong&gt;SSG, ISR, SSR, and CSR&lt;/strong&gt; — form the foundation of Next.js, particularly for e-commerce applications where performance and SEO are crucial.&lt;/p&gt;

&lt;p&gt;But this is just the beginning.&lt;/p&gt;

&lt;p&gt;In upcoming articles, we'll explore &lt;strong&gt;more advanced techniques&lt;/strong&gt; from recent Next.js versions, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Streaming and Suspense&lt;/strong&gt; for progressive rendering and better UX,&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Partial Prerendering (PPR)&lt;/strong&gt; to efficiently combine static and dynamic elements on the same page,&lt;/li&gt;
&lt;li&gt;Techniques to optimize &lt;strong&gt;per-component rendering strategies&lt;/strong&gt; in large-scale applications.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These approaches provide more precise control over performance and user experience—especially valuable for highly dynamic, personalized, and large-scale e-commerce platforms.&lt;/p&gt;

&lt;p&gt;Stay tuned!&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Why Next.js is one of the best solutions for E-commerce?</title>
      <dc:creator>Paweł Sobolewski</dc:creator>
      <pubDate>Tue, 01 Jul 2025 08:43:05 +0000</pubDate>
      <link>https://dev.to/u11d/why-nextjs-is-one-of-the-best-solutions-for-e-commerce-178c</link>
      <guid>https://dev.to/u11d/why-nextjs-is-one-of-the-best-solutions-for-e-commerce-178c</guid>
      <description>&lt;p&gt;In the constantly changing world of web development, e-commerce poses a unique set of challenges: complex product data, dynamic content, SEO demands, fast page loads, secure checkout flows, integration with multiple external systems and many, many more.&lt;/p&gt;

&lt;p&gt;As developers, we’re constantly looking for tools that help us deliver all of this without compromising on performance or maintainability. &lt;a href="https://nextjs.org/" rel="noopener noreferrer"&gt;Next.js&lt;/a&gt; delivers on all fronts — and then some more.&lt;/p&gt;

&lt;p&gt;If you’re evaluating tech stacks for your next e-commerce project, here’s why Next.js deserves a spot at the top of your list.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Performance by Design
&lt;/h2&gt;

&lt;p&gt;E-commerce websites live and die by performance. According to &lt;a href="https://www.thinkwithgoogle.com/marketing-strategies/app-and-mobile/mobile-page-speed-conversion-data/" rel="noopener noreferrer"&gt;Google&lt;/a&gt;, even a one-second delay in page load time can reduce conversions by up to 20%. Next.js addresses this head-on by giving you multiple &lt;a href="https://nextjs.org/docs/app/building-your-application/rendering" rel="noopener noreferrer"&gt;rendering strategies&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Static Site Generation (SSG)&lt;/li&gt;
&lt;li&gt;Server-Side Rendering (SSR)&lt;/li&gt;
&lt;li&gt;Client Side Rendering (CSR)&lt;/li&gt;
&lt;li&gt;Incremental Static Regeneration (ISR)&lt;/li&gt;
&lt;li&gt;Partial Prerendering (PPR, currently experimental)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This per-route rendering flexibility lets you optimize each part of your store — from blazing-fast landing pages to real-time personalized carts.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Built-in SEO Superpowers
&lt;/h2&gt;

&lt;p&gt;Next.js delivers server-rendered HTML out of the box, making sure search engines like Google can immediately crawl meaningful content. For developers, this translates to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Full control of metadata with &lt;strong&gt;next/head&lt;/strong&gt;, &lt;strong&gt;metadata&lt;/strong&gt; object or even dynamic content with &lt;strong&gt;generateMetadata&lt;/strong&gt; function&lt;/li&gt;
&lt;li&gt;SEO-friendly dynamic routing&lt;/li&gt;
&lt;li&gt;Easily integrated structured data for rich search snippets&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When search engines properly index your category and product pages, you'll see a natural boost in organic traffic.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Smart Data Caching and Request Management for Better Performance
&lt;/h2&gt;

&lt;p&gt;In e-commerce, caching goes beyond storing page content. A major performance boost comes from &lt;strong&gt;efficient data fetching and preventing duplicate requests&lt;/strong&gt; — crucial when handling large product catalogs, filtering systems, and personalized content.&lt;/p&gt;

&lt;p&gt;Next.js provides complete control over &lt;strong&gt;data-layer &lt;a href="https://nextjs.org/docs/app/deep-dive/caching" rel="noopener noreferrer"&gt;caching&lt;/a&gt;&lt;/strong&gt;, from server to edge computing. Better yet, it works out of the box without any custom configuration, thanks to its extended node &lt;a href="https://nodejs.org/en/learn/getting-started/fetch" rel="noopener noreferrer"&gt;&lt;strong&gt;fetch&lt;/strong&gt;&lt;/a&gt; utility.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Request Memoization&lt;/strong&gt; - During server-side rendering, Next.js stores responses received via the &lt;strong&gt;fetch&lt;/strong&gt; API. This means that regardless of where and how many times you request the same data in &lt;a href="https://react.dev/reference/rsc/server-components" rel="noopener noreferrer"&gt;&lt;strong&gt;React Server Components (RSC)&lt;/strong&gt;&lt;/a&gt;, the actual request occurs only once.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data Cache&lt;/strong&gt; - Need persistent data across multiple page renders, sessions, and users? Next.js provides this functionality out of the box by storing responses in deployed application memory in a controlled way.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Full Route Cache&lt;/strong&gt; - By caching renderable pages generated through SSG, ISR, or forced caching, Next.js can deliver content to end users at blazing speeds.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Router Cache&lt;/strong&gt; - Thanks to Next.js's ability to utilize both server and client environments, subsequent client-side navigations can be stored and reused within a single user session to display previously visible content immediately.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  4. Streaming in Next.js: Faster Renders, Better UX, and SEO Gains
&lt;/h2&gt;

&lt;p&gt;With the App Router and React Server Components, &lt;strong&gt;Next.js supports HTTP streaming&lt;/strong&gt;, which allows the server to send HTML to the browser incrementally. Instead of waiting for the entire page to generate, content appears as soon as each part of the UI is ready.&lt;/p&gt;

&lt;p&gt;This approach delivers significant performance and user experience benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Time to First Byte (TTFB):&lt;/strong&gt; The server begins sending content instantly, reducing initial load time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Largest Contentful Paint (LCP):&lt;/strong&gt; Essential elements like product titles, images, and prices appear early, with secondary components streaming in afterward.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Perceived performance:&lt;/strong&gt; Users see content faster, creating a more responsive experience.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On the &lt;strong&gt;SEO side&lt;/strong&gt;, streaming offers two key advantages:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Search engine bots can begin parsing HTML immediately through the open connection, continuously receiving the page's complete structure.&lt;/li&gt;
&lt;li&gt;The streamed content remains &lt;strong&gt;server-rendered HTML&lt;/strong&gt; rather than client-side JS, ensuring all content—including personalized and dynamic elements—stays crawlable and indexable.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This delivers the &lt;strong&gt;best of both worlds&lt;/strong&gt;: rapid initial loads &lt;em&gt;and&lt;/em&gt; complete SEO visibility, regardless of page complexity or personalization.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Multiple Layouts for Complex E-commerce Experiences
&lt;/h2&gt;

&lt;p&gt;Modern e-commerce sites need various specialized layouts beyond basic shop pages, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Marketing landing pages&lt;/li&gt;
&lt;li&gt;Product listings and product detail pages (PDPs)&lt;/li&gt;
&lt;li&gt;Checkout and cart interfaces&lt;/li&gt;
&lt;li&gt;User dashboards for orders and subscriptions&lt;/li&gt;
&lt;li&gt;Admin and vendor portals&lt;/li&gt;
&lt;li&gt;Blog and content sections&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Next.js supports &lt;strong&gt;multiple layouts&lt;/strong&gt; out of the box - a significant advantage for e-commerce projects with diverse page types. You can define distinct layouts for different pages while maintaining shared elements like navigation and cart state across views. This approach keeps your code organized and helps scale a complex store into a modular, app-like experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Integrating Next.js with Advanced Backends like Medusa
&lt;/h2&gt;

&lt;p&gt;For developers building &lt;strong&gt;fully customizable commerce systems&lt;/strong&gt;, frameworks like &lt;a href="https://medusajs.com/" rel="noopener noreferrer"&gt;&lt;strong&gt;Medusa&lt;/strong&gt;&lt;/a&gt; have gained significant popularity. Medusa offers a modular, open-source backend that provides plug-and-play services for products, carts, orders, payments, and more.&lt;/p&gt;

&lt;p&gt;Next.js pairs exceptionally well with platforms like Medusa for several reasons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It seamlessly integrates with Medusa's REST API,&lt;/li&gt;
&lt;li&gt;You can leverage &lt;a href="https://nextjs.org/docs/pages/building-your-application/routing/api-routes" rel="noopener noreferrer"&gt;&lt;strong&gt;Next.js API routes&lt;/strong&gt;&lt;/a&gt; as middleware between Medusa and the frontend (for authentication or webhook handling),&lt;/li&gt;
&lt;li&gt;Medusa's modular services (inventory, discounts, fulfillment) align perfectly with Next.js's page-level rendering logic,&lt;/li&gt;
&lt;li&gt;When combined with &lt;strong&gt;ISR and caching&lt;/strong&gt;, you can scale dynamic product catalogs while maintaining peak performance.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Simply put, Medusa manages your backend business logic while Next.js provides complete control over the customer-facing experience — maintaining a clean separation of concerns.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Whether you're launching a new storefront or modernizing an existing one, Next.js delivers the performance, flexibility, and future-ready architecture that modern e-commerce platforms demand. It empowers teams to create lightning-fast shopping experiences, boost SEO performance, and integrate seamlessly with both legacy systems and modern headless commerce platforms.&lt;/p&gt;

&lt;p&gt;Next.js provides a robust foundation for building exceptional digital retail experiences that serve both current needs and future growth. Its capabilities enhance user experience while supporting sustainable, long-term development.&lt;/p&gt;

&lt;p&gt;Stay tuned for upcoming articles that will explore these topics in depth, complete with practical examples and real-world use cases.&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>webdev</category>
      <category>performance</category>
    </item>
  </channel>
</rss>
