<?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: Suraj Thallapelli</title>
    <description>The latest articles on DEV Community by Suraj Thallapelli (@suraj_thallapelli_55c6f59).</description>
    <link>https://dev.to/suraj_thallapelli_55c6f59</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%2F2394032%2Ff325632c-aae7-472f-a9ab-4560b08601d6.jpg</url>
      <title>DEV Community: Suraj Thallapelli</title>
      <link>https://dev.to/suraj_thallapelli_55c6f59</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/suraj_thallapelli_55c6f59"/>
    <language>en</language>
    <item>
      <title>When a "Simple Refresh" Broke Our Production App: Next.js Static Export on Azure</title>
      <dc:creator>Suraj Thallapelli</dc:creator>
      <pubDate>Thu, 04 Jun 2026 07:24:54 +0000</pubDate>
      <link>https://dev.to/suraj_thallapelli_55c6f59/when-a-simple-refresh-broke-our-production-app-nextjs-static-export-on-azure-2ano</link>
      <guid>https://dev.to/suraj_thallapelli_55c6f59/when-a-simple-refresh-broke-our-production-app-nextjs-static-export-on-azure-2ano</guid>
      <description>&lt;p&gt;We had one of those bugs that looked small from the outside, but exposed a much deeper architecture lesson.&lt;/p&gt;

&lt;p&gt;In our portal web application, top-level routes worked perfectly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;/registrations&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/messages&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/evaluations&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But the moment users clicked into deeper routes like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;/registrations/available/57&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/messages/123&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;...the entire app reloaded.&lt;/p&gt;

&lt;p&gt;The sidebar remounted.&lt;br&gt;
State was lost.&lt;br&gt;
The UX felt broken.&lt;/p&gt;

&lt;p&gt;What made this incident especially valuable was the contrast:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Locally, everything worked.&lt;/li&gt;
&lt;li&gt;In production, it failed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That gap turned a frustrating bug into a really useful lesson about how Next.js static export actually behaves on Azure.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Setup
&lt;/h2&gt;

&lt;p&gt;Our app was a &lt;strong&gt;Next.js App Router&lt;/strong&gt; application deployed to &lt;strong&gt;Azure Static Web Apps&lt;/strong&gt; using &lt;strong&gt;static export&lt;/strong&gt; mode.&lt;/p&gt;

&lt;p&gt;That meant:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No Next.js server runtime in production&lt;/li&gt;
&lt;li&gt;No SSR at request time&lt;/li&gt;
&lt;li&gt;No middleware execution at request time&lt;/li&gt;
&lt;li&gt;Everything served from generated static files&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We chose this model intentionally for simplicity, cost, and deployment speed.&lt;/p&gt;

&lt;p&gt;So this was not full server-mode Next.js.&lt;/p&gt;

&lt;p&gt;It was closer to a highly structured SPA that benefits from Next.js routing, layouts, and build output.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Symptom
&lt;/h2&gt;

&lt;p&gt;From the user's perspective:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Clicking into detail pages caused a full page refresh&lt;/li&gt;
&lt;li&gt;Shared layout was not preserved&lt;/li&gt;
&lt;li&gt;Sidebar reset&lt;/li&gt;
&lt;li&gt;App state disappeared&lt;/li&gt;
&lt;li&gt;Navigation felt unstable&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;From the engineering perspective, the real issue was this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Soft navigation was failing.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The Next.js router was falling back to hard browser navigation.&lt;/p&gt;

&lt;p&gt;That distinction ended up being the key.&lt;/p&gt;
&lt;h2&gt;
  
  
  What We Assumed First
&lt;/h2&gt;

&lt;p&gt;At first, this looked like a classic static hosting issue:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Maybe Azure Static Web Apps cannot resolve dynamic routes."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That was the obvious explanation.&lt;/p&gt;

&lt;p&gt;But it was not fully correct.&lt;/p&gt;

&lt;p&gt;Hard deep-linking often worked because rewrite shells were already in place.&lt;/p&gt;

&lt;p&gt;The real issue was more subtle:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hard navigation often worked&lt;/li&gt;
&lt;li&gt;Soft navigation failed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That completely changed the debugging direction.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Root Cause
&lt;/h2&gt;

&lt;p&gt;Next.js App Router in static export mode does not only rely on generated HTML files.&lt;/p&gt;

&lt;p&gt;For dynamic routes, it also depends on route payload files used during client-side transitions.&lt;/p&gt;

&lt;p&gt;In simple terms:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Hard navigation&lt;/strong&gt; needs an HTML shell&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Soft navigation&lt;/strong&gt; needs route payload data&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Our Azure rewrite rule for dynamic routes was too greedy.&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;registrations&lt;/span&gt;/* → /&lt;span class="n"&gt;registrations&lt;/span&gt;/&lt;span class="n"&gt;static_export&lt;/span&gt;.&lt;span class="n"&gt;html&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At first glance, that looked reasonable.&lt;/p&gt;

&lt;p&gt;But the wildcard was also catching requests meant for route payload files.&lt;/p&gt;

&lt;p&gt;So during soft navigation, when Next.js requested payload data, Azure returned HTML instead.&lt;/p&gt;

&lt;p&gt;The router expected payload data.&lt;br&gt;
It received HTML.&lt;br&gt;
It could not parse the transition response.&lt;br&gt;
So it fell back to full browser navigation.&lt;/p&gt;

&lt;p&gt;That is why the entire app reloaded.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Fix
&lt;/h2&gt;

&lt;p&gt;We solved it in two layers.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Correct routing behavior for payload requests
&lt;/h3&gt;

&lt;p&gt;We added rewrite rules so payload requests resolved to payload files, while normal page requests continued resolving to HTML shells.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Remove manual maintenance risk
&lt;/h3&gt;

&lt;p&gt;We stopped manually editing &lt;code&gt;staticwebapp.config&lt;/code&gt; every time a new dynamic route was added.&lt;/p&gt;

&lt;p&gt;Instead, we generated the rewrite rules automatically from the build output.&lt;/p&gt;

&lt;p&gt;The generator now:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Scans exported output for dynamic route artifacts&lt;/li&gt;
&lt;li&gt;Produces both HTML and payload rewrite rules&lt;/li&gt;
&lt;li&gt;Orders rules with the correct specificity&lt;/li&gt;
&lt;li&gt;Writes the final &lt;code&gt;staticwebapp.config&lt;/code&gt; into the deployment output&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That turned a fragile manual config problem into a deterministic build artifact.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Worked
&lt;/h2&gt;

&lt;p&gt;The issue was not:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Dynamic routes are impossible on static hosting."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The real issue was:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Runtime payload requests were being rewritten incorrectly."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Once the rewrite rules matched what Next.js App Router actually requests during client-side transitions, the experience became stable again.&lt;/p&gt;

&lt;p&gt;No full remount.&lt;br&gt;
No sidebar reset.&lt;br&gt;
No UX jump.&lt;/p&gt;

&lt;h2&gt;
  
  
  Other Valid Solutions
&lt;/h2&gt;

&lt;p&gt;There is no single best architecture here. There are tradeoffs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Option A: Stay with static export and generate rewrites
&lt;/h3&gt;

&lt;p&gt;This is what we chose.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best when:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SEO is not the main priority&lt;/li&gt;
&lt;li&gt;The app behaves more like a portal or dashboard&lt;/li&gt;
&lt;li&gt;You want predictable static hosting&lt;/li&gt;
&lt;li&gt;Data fetching can happen client-side&lt;/li&gt;
&lt;li&gt;You want lower hosting complexity&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fast deployments&lt;/li&gt;
&lt;li&gt;Lower cost&lt;/li&gt;
&lt;li&gt;No server lifecycle management&lt;/li&gt;
&lt;li&gt;Simple hosting model&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cons:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You must understand route shells and payload files&lt;/li&gt;
&lt;li&gt;No request-time SSR&lt;/li&gt;
&lt;li&gt;No runtime middleware&lt;/li&gt;
&lt;li&gt;More responsibility on rewrite correctness&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Option B: Move to full Next.js server or hybrid hosting
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Best when:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You need SSR&lt;/li&gt;
&lt;li&gt;You need ISR&lt;/li&gt;
&lt;li&gt;You need middleware at request time&lt;/li&gt;
&lt;li&gt;SEO and request-time personalization are important&lt;/li&gt;
&lt;li&gt;You want native Next.js runtime behavior&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Full Next.js runtime capabilities&lt;/li&gt;
&lt;li&gt;Fewer static rewrite workarounds&lt;/li&gt;
&lt;li&gt;Better fit for dynamic rendering&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cons:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;More operational complexity&lt;/li&gt;
&lt;li&gt;Higher hosting and runtime considerations&lt;/li&gt;
&lt;li&gt;Platform behavior matters a lot&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Option C: Run standalone Next.js on Azure App Service or Container Apps
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Best when:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You want full server runtime control in Azure&lt;/li&gt;
&lt;li&gt;You need predictable feature parity&lt;/li&gt;
&lt;li&gt;Your app depends heavily on SSR or other server behavior&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Full server feature set&lt;/li&gt;
&lt;li&gt;Better runtime control&lt;/li&gt;
&lt;li&gt;More flexibility&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cons:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;More moving parts&lt;/li&gt;
&lt;li&gt;More DevOps ownership&lt;/li&gt;
&lt;li&gt;More monitoring and scaling responsibility&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Biggest Lesson
&lt;/h2&gt;

&lt;p&gt;Static export is not less engineering.&lt;/p&gt;

&lt;p&gt;It is &lt;strong&gt;different engineering&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If you choose Next.js App Router with static export:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Treat rewrite rules as production-critical infrastructure&lt;/li&gt;
&lt;li&gt;Understand hard navigation vs soft navigation&lt;/li&gt;
&lt;li&gt;Understand HTML shells vs route payload files&lt;/li&gt;
&lt;li&gt;Automate config generation from build artifacts&lt;/li&gt;
&lt;li&gt;Do not rely on manual route mapping at scale&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Practical Checklist
&lt;/h2&gt;

&lt;p&gt;For teams using Next.js static export on Azure Static Web Apps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Test deep links for every dynamic route&lt;/li&gt;
&lt;li&gt;[ ] Test soft navigation from list pages to detail pages&lt;/li&gt;
&lt;li&gt;[ ] Inspect network responses during client transitions&lt;/li&gt;
&lt;li&gt;[ ] Confirm payload requests are not returning HTML&lt;/li&gt;
&lt;li&gt;[ ] Keep rewrite rule order intentional&lt;/li&gt;
&lt;li&gt;[ ] Generate &lt;code&gt;staticwebapp.config&lt;/code&gt; from build output&lt;/li&gt;
&lt;li&gt;[ ] Add CI checks for missing route artifacts&lt;/li&gt;
&lt;li&gt;[ ] Test production-like builds early&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Closing Thought
&lt;/h2&gt;

&lt;p&gt;This bug looked like a simple route refresh issue.&lt;/p&gt;

&lt;p&gt;But in reality, it was a contract mismatch between:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Next.js App Router export internals&lt;/li&gt;
&lt;li&gt;Azure Static Web Apps rewrite behavior&lt;/li&gt;
&lt;li&gt;Our deployment assumptions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once those three were aligned, the fix became stable, scalable, and future-proof.&lt;/p&gt;

&lt;p&gt;Sometimes the hardest frontend bugs are not inside the component.&lt;/p&gt;

&lt;p&gt;They live between build output, hosting rules, and runtime navigation.&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>azure</category>
      <category>webdev</category>
      <category>architecture</category>
    </item>
  </channel>
</rss>
