<?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>Forem: Atena / Full stack developer</title>
    <description>The latest articles on Forem by Atena / Full stack developer (@atena).</description>
    <link>https://forem.com/atena</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1032225%2Fecefe356-c79f-4de2-84fc-99ce8c12af5c.png</url>
      <title>Forem: Atena / Full stack developer</title>
      <link>https://forem.com/atena</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/atena"/>
    <language>en</language>
    <item>
      <title>Why Next.js Navigation Doesn’t Work as Expected</title>
      <dc:creator>Atena / Full stack developer</dc:creator>
      <pubDate>Thu, 12 Feb 2026 21:44:01 +0000</pubDate>
      <link>https://forem.com/atena/why-nextjs-navigation-doesnt-work-as-expected-h3c</link>
      <guid>https://forem.com/atena/why-nextjs-navigation-doesnt-work-as-expected-h3c</guid>
      <description>&lt;p&gt;We ran into an issue where clicking a menu item sometimes did not navigate to the expected page.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Firzc3cwhfdnlm5e78qe4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Firzc3cwhfdnlm5e78qe4.png" alt=" " width="800" height="59"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The URL in the address bar would change correctly, but the content on the screen would not update. If we kept clicking the menu, navigation would occasionally work.&lt;strong&gt;The issue did not appear in local development, but only in the production environment&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It took some time to figure out the cause and solution, I would like to share what we learned.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reason
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Next.js Sends an RSC Request When a &lt;code&gt;&amp;lt;Link&amp;gt;&lt;/code&gt; Is Clicked
&lt;/h3&gt;

&lt;p&gt;In the App Router, clicking a &lt;code&gt;&amp;lt;Link&amp;gt;&lt;/code&gt; component triggers a client-side navigation instead of a full page reload. To render the next page, Next.js sends an internal request related to React Server Components (RSC) to fetch the server-rendered data required for the transition.&lt;/p&gt;

&lt;p&gt;It allows the page to update without reloading the entire document.&lt;/p&gt;

&lt;h3&gt;
  
  
  What We Found in the Network Tab
&lt;/h3&gt;

&lt;p&gt;While inspecting the Network tab during navigation, we noticed the following response headers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;x-middleware-rewrite: /rate-limit-error?retryAfter=1&amp;amp;_rsc=xxxx
x-nextjs-rewritten-path: /rate-limit-error
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;x-middleware-rewrite&lt;/code&gt;:&lt;br&gt;
The middleware rewrote the internal RSC request to the &lt;code&gt;rate-limit error&lt;/code&gt; route.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;x-nextjs-rewritten-path&lt;/code&gt;:&lt;br&gt;
Next.js ultimately processed the request as &lt;code&gt;/rate-limit-error&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This means that when an internal RSC request was triggered, the middleware rewrote it to the rate-limit error route, and Next.js ultimately processed the request as /rate-limit-error.&lt;/p&gt;
&lt;h3&gt;
  
  
  Flow of the Issue
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;The user clicks a &lt;code&gt;&amp;lt;Link&amp;gt;&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Next.js sends an internal RSC request to fetch data for the next page.&lt;/li&gt;
&lt;li&gt;The rate-limiting middleware incorrectly applies rate limiting to this internal request.&lt;/li&gt;
&lt;li&gt;The middleware rewrites the request to /rate-limit-error.&lt;/li&gt;
&lt;li&gt;The browser URL remains correct, but the fetched content comes from a different route.&lt;/li&gt;
&lt;li&gt;This mismatch causes the page to appear broken or not update as expected.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This mismatch causes the page to appear broken or not update as expected.&lt;/p&gt;
&lt;h3&gt;
  
  
  Let's fix
&lt;/h3&gt;
&lt;h4&gt;
  
  
  Before
&lt;/h4&gt;

&lt;p&gt;Our rate-limiting middleware was too broad and could apply to internal Next.js requests. As a result, RSC requests triggered by  navigation were sometimes rate-limited and rewritten to &lt;code&gt;/rate-limit-error&lt;/code&gt;, causing the URL and rendered content to mismatch.&lt;br&gt;
Before (problem)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Rate limit applied too broadly
const shouldRateLimit = true; ←too broad

// Incomplete RSC detection
const hasRscParam = requestUrl.includes('_rsc=');
const hasRscHeader = request.headers.get('rsc') === '1';
const isRSCRequest = hasRscParam || hasRscHeader;

// Internal requests were not excluded
if (shouldRateLimit &amp;amp;&amp;amp; isRateLimited) {
  return NextResponse.rewrite(
    new URL('/rate-limit-error', request.url)
  );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Problem:&lt;br&gt;
Internal RSC requests triggered by  navigation could be rate-limited and rewritten.&lt;/p&gt;
&lt;h4&gt;
  
  
  After
&lt;/h4&gt;

&lt;p&gt;We restricted rate limiting to API routes and non-GET requests only, and explicitly excluded internal Next.js requests (RSC/router requests). This prevented the middleware from rewriting navigation-related requests and restored stable client-side routing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Rate limit only APIs and non-GET requests
const isApiRoute = pathname.startsWith('/api/');
const isNonGetRequest = request.method !== 'GET';
const shouldRateLimit = isApiRoute || isNonGetRequest;

// Accurate RSC detection
const hasRscParam = requestUrl.includes('_rsc=');
const hasRscHeader = request.headers.get('rsc') === '1';
const hasRscAccept =
  request.headers.get('accept')?.includes('text/x-component');
const isRSCRequest = hasRscParam || hasRscHeader || hasRscAccept;

// Exclude internal navigation requests
if (shouldRateLimit &amp;amp;&amp;amp; !isRSCRequest &amp;amp;&amp;amp; isRateLimited) {
  return NextResponse.rewrite(
    new URL('/rate-limit-error', request.url)
  );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Exclude internal RSC requests from rate limiting and apply rate limits only to API and non-GET requests.&lt;/p&gt;

&lt;h2&gt;
  
  
  To avoid similar issues in the future
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Apply rate limiting only to API routes and non-GET requests.&lt;/li&gt;
&lt;li&gt;Explicitly exclude internal Next.js requests such as RSC and router-related requests from middleware logic.&lt;/li&gt;
&lt;li&gt;Be cautious when using rewrites in middleware, especially with client-side navigation.&lt;/li&gt;
&lt;li&gt;When navigation behaves inconsistently, inspect the Network tab and response headers to check for unexpected rewrites.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Keeping these points in mind helps ensure stable navigation and avoids hard-to-debug issues in production.&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>projectcollegecms</category>
    </item>
    <item>
      <title>Boosting LCP: Next.js Performance with next/dynamic</title>
      <dc:creator>Atena / Full stack developer</dc:creator>
      <pubDate>Thu, 12 Feb 2026 21:37:37 +0000</pubDate>
      <link>https://forem.com/atena/boosting-lcp-nextjs-performance-with-nextdynamic-3100</link>
      <guid>https://forem.com/atena/boosting-lcp-nextjs-performance-with-nextdynamic-3100</guid>
      <description>&lt;p&gt;Every frontend engineer knows the pain. Your team keeps requesting you to add new features to the website, and suddenly your LCP score takes a nose dive.&lt;/p&gt;

&lt;p&gt;This is exactly where &lt;code&gt;next/dynamic&lt;/code&gt; saves the day. I’ve used this method to rescue performance scores. &lt;/p&gt;

&lt;h2&gt;
  
  
  1. What is &lt;code&gt;next/dynamic&lt;/code&gt;?
&lt;/h2&gt;

&lt;p&gt;It’s a tool for &lt;strong&gt;Lazy Loading your React components&lt;/strong&gt;. When you do a standard import like import Header from &lt;code&gt;'./Header'&lt;/code&gt;, that component gets bundled into the main JavaScript file. This means the browser has to download, parse, and execute that code before the user can really interact with the page. This kills your LCP score. &lt;br&gt;
&lt;code&gt;next/dynamic&lt;/code&gt; lets you tell the bundler: "Don't load this yet. Wait until we actually need it." It splits the code into a separate "chunks" file.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F80g2ip0wn04yzbnsbhqa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F80g2ip0wn04yzbnsbhqa.png" alt="Explanation of next/dynamic" width="800" height="436"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  2. Two Strategies for Using It
&lt;/h2&gt;

&lt;p&gt;There are two main ways I use &lt;code&gt;next/dynamic&lt;/code&gt;, depending on what I'm trying to fix.&lt;/p&gt;
&lt;h3&gt;
  
  
  Strategy 1: Optimizing for Timing (Fixing LCP)
&lt;/h3&gt;

&lt;p&gt;This is for things the user doesn't see immediately, like heavy components or a section way down the page. By lazy loading these, you let the browser focus 100% of its energy on rendering the Hero section. This makes the perceived load time much faster.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const HeavyComponent = dynamic(() =&amp;gt; import('../components/Heavy'),
 {
  loading: () =&amp;gt; &amp;lt;p&amp;gt;Loading...&amp;lt;/p&amp;gt;,
 })
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Strategy 2: Optimizing for Location (Server vs. Browser)
&lt;/h3&gt;

&lt;p&gt;Sometimes, you have code that simply cannot run on the server. Maybe it uses window, document, or a library. By using the &lt;code&gt;ssr: false&lt;/code&gt; option, you force the component to be rendered entirely on the client side.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const MapComponent = dynamic(() =&amp;gt; import('../components/Map'),
 {
  ssr: false, // Skip the server entirely!
 })
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  3. &lt;code&gt;next/dynamic&lt;/code&gt; vs. &lt;code&gt;'use client'&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;This is where I got confused. If I import a component with &lt;code&gt;ssr: true&lt;/code&gt;, but that component has &lt;code&gt;'use client'&lt;/code&gt;, does that make it CSR?&lt;/p&gt;

&lt;p&gt;The answer is No. It is still SSR first.&lt;br&gt;
To clear up this confusion, we need to correct our mental model.&lt;/p&gt;

&lt;h3&gt;
  
  
  Method vs. Declaration
&lt;/h3&gt;

&lt;p&gt;We must distinguish between the Method and the Declaration. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SSR/CSR is a Method. It describes when and where the rendering happens.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;'use client'&lt;/code&gt; is a Declaration. It signals what features (like hooks or event listeners) are being used.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It can be confusing that adding &lt;code&gt;'use client'&lt;/code&gt; opts out of SSR. This is wrong. In the Next.js App Router, even if you mark a component with &lt;code&gt;'use client'&lt;/code&gt;, it is still rendered on the server initially.&lt;/p&gt;

&lt;p&gt;Then how does the  &lt;code&gt;'use client'&lt;/code&gt; work with SSR/ CSR?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;SSR with &lt;code&gt;'use client'&lt;/code&gt;&lt;br&gt;
The server pre-renders the HTML Shell (the tags like &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; inside the server box).  The &lt;code&gt;'use client'&lt;/code&gt; acts as &lt;strong&gt;Hydration&lt;/strong&gt;  to make the existing button interactive after it arrives in the browser. The HTML is born on the Server. The user sees the content immediately.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;CSR with &lt;code&gt;'use client'&lt;/code&gt;&lt;br&gt;
The server skips rendering and sends an Empty Shell. The server skips rendering and sends an Empty Shell. Here, the JavaScript bundle must build the entire UI from scratch inside the &lt;strong&gt;Browser&lt;/strong&gt;. The user waits for the JS to load before seeing any content.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbc7a54h8lrnu393bdvui.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbc7a54h8lrnu393bdvui.png" alt="Explanation of CSR vs SSR with use cleint" width="800" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Performance is all about control.You need to know what is rendering, where it's rendering, and when.&lt;/p&gt;

&lt;p&gt;Want to improve LCP?&lt;br&gt;
→ Use next/dynamic to split up your heavy JS bundles.&lt;/p&gt;

&lt;p&gt;Fixing a "window is undefined" error?&lt;br&gt;
→ Use ssr: false to skip the server execution entirely.&lt;/p&gt;

&lt;p&gt;Need a button to click?&lt;br&gt;
→ Use 'use client' to add interactivity (while keeping SSR).&lt;/p&gt;

&lt;p&gt;But remember, with&lt;code&gt;'use client'&lt;/code&gt;, the HTML is still born on the server. Learning this distinction is the key to building Next.js apps that feel instant.&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>performance</category>
      <category>lcp</category>
      <category>projectcollegecms</category>
    </item>
  </channel>
</rss>
