{"id":23268,"date":"2026-04-22T13:36:42","date_gmt":"2026-04-22T11:36:42","guid":{"rendered":"https:\/\/pagepro.co\/blog\/?p=23268"},"modified":"2026-04-22T13:36:44","modified_gmt":"2026-04-22T11:36:44","slug":"nextjs-production-issues","status":"publish","type":"post","link":"https:\/\/pagepro.co\/blog\/nextjs-production-issues\/","title":{"rendered":"Hidden Cost of AI-Generated Code: What Breaks When You Go to Production"},"content":{"rendered":"\n<h2 class=\"wp-block-heading\" id=\"tldr\">TL;DR<\/h2>\n\n\n\n<ul>\n<li>The majority of Next.js production issues are self-inflicted patterns \u2014 CSS chunk ordering, bloated bundles, sequential fetches, duplicate queries, and hydration mismatches all have straightforward fixes once you know what to look for<\/li>\n\n\n\n<li>Run the bundle analyzer before touching any code \u2014 it will show you which server-safe libraries leaked into your client bundle and exactly how much they cost you<\/li>\n\n\n\n<li>A single misplaced use client on a parent component ships your entire component tree to the browser; push it to leaf nodes only and keep data fetching, layout, and formatting on the server<\/li>\n\n\n\n<li>Sequential await calls in server components create accidental <a href=\"https:\/\/pagepro.co\/blog\/next-js-pre-rendering-and-data-fetching\">data waterfalls<\/a> \u2014 wrapping independent fetches in Promise.all() or Suspense boundaries can cut page load time from 5+ seconds to under 2<\/li>\n\n\n\n<li>Hydration errors only surface in production because the dev server masks mismatches \u2014 the three causes are browser API access during render, non-deterministic values, and invalid HTML nesting; each has a one-line fix<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"introduction\">Introduction<\/h2>\n\n\n\n<p>Recently<a href=\"http:\/\/reddit.com\/r\/nextjs\/comments\/1q5tgu8\/i_analyzed_the_top_500_active_nextjs_issues_the\/\"> users on reddit&nbsp; analyzed 500 active Next.js GitHub issues<\/a> and posted their findings. The most uncomfortable conclusion: the majority of Next.js production issues aren&#8217;t framework bugs. They&#8217;re patterns \u2014 <a href=\"https:\/\/pagepro.co\/blog\/common-nextjs-mistakes-core-web-vitals\">the same five or six mistakes<\/a>, reproduced across hundreds of codebases by developers who learned React in a client-side world and never fully adjusted their mental model.<\/p>\n\n\n\n<p>Next.js gives you server components by default, built-in caching, streaming, and a bundle pipeline that can ship almost nothing to the browser. Most teams use about 20% of that. The rest gets accidentally disabled, worked around, or never touched \u2014 and production pays the price.<\/p>\n\n\n\n<p>This guide covers the five Next.js production issues that show up most often and hurt most.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"diagnose-before-you-fix-run-the-bundle-analyzer-first\">Diagnose Before You Fix \u2014 Run the Bundle Analyzer First<\/h2>\n\n\n\n<p>Before changing a single line, you need to know what your production build is actually shipping to the browser. Guessing wastes time. The <a href=\"http:\/\/nextjs.org\/docs\/app\/building-your-application\/optimizing\/bundle-analyzer\">bundle analyzer<\/a> tells you exactly <a href=\"https:\/\/pagepro.co\/blog\/nextjs-performance-optimization-in-9-steps\">which libraries are in your client bundle <\/a>and how large they are.<\/p>\n\n\n\n<p>If you&#8217;re on Turbopack (Next.js v16.1+):<\/p>\n\n\n\n<pre class=\"wp-block-code-mind-code c-code\"><code class=\"javascript\">ANALYZE=true npx next build<\/code><\/pre>\n\n\n\n<p>If you&#8217;re on webpack:<\/p>\n\n\n\n<pre class=\"wp-block-code-mind-code c-code\"><code class=\"javascript\">npm install @next\/bundle-analyzer\n\n\/\/ next.config.js\n\nconst withBundleAnalyzer = require('@next\/bundle-analyzer')({\n\n\u00a0enabled: process.env.ANALYZE === 'true',\n\n})\n\nmodule.exports = withBundleAnalyzer({})<\/code><\/pre>\n\n\n\n<p>Both generate an <a href=\"http:\/\/npmjs.com\/package\/@next\/bundle-analyzer\">interactive visual map of your compiled output<\/a>. Open it and look for two things: what&#8217;s there that shouldn&#8217;t be, and what&#8217;s larger than you expected. That map is your diagnostic baseline for every problem in this guide.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"what-a-healthy-bundle-looks-like-vs-a-bloated-one\"><span class=\"underline-accent\">What a Healthy Bundle Looks Like vs. a Bloated One<\/span><\/h3>\n\n\n\n<p>A healthy bundle has one large, expected block \u2014 ReactDOM client. It&#8217;s heavy because it has to be; it&#8217;s what runs your React code in the browser. Everything else should be small, scoped UI components and their direct dependencies.<\/p>\n\n\n\n<p>Here&#8217;s what a problem looks like:<\/p>\n\n\n\n<pre class=\"wp-block-code-mind-code c-code\"><code class=\"javascript\">Page: \/dashboard\n\n\u00a0ReactDOM client \u00a0 \u00a0 \u00a0 \u00a0 \u2014 280KB\u00a0 \u2713 expected\n\n\u00a0date-fns\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u2014 16.98KB\u00a0 \u2717 should not be here\n\n\u00a0Total (compressed): \u00a0 \u00a0 \u00a0 317KB<\/code><\/pre>\n\n\n\n<p><strong>date-fns<\/strong> is a date formatting library. It has no browser APIs, no event listeners, nothing that requires the client. If it&#8217;s in your bundle, it means the component using it was marked use client \u2014 or is a child of one that was. Removing that single misplacement drops the compressed bundle to 304KB.<\/p>\n\n\n\n<p>That&#8217;s the pattern you&#8217;re looking for: server-safe code that leaked into the client. Once you can see it, fixing it is straightforward. The next four sections show you exactly where these leaks come from.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"problem-1-production-css-breaks-when-dev-css-looks-fine\">Problem 1 \u2014 Production CSS Breaks When Dev CSS Looks Fine<\/h2>\n\n\n\n<p>This is the single most-reported category in an analysis of 500 active Next.js GitHub issues. Not hydration errors. Not build failures. CSS that works perfectly in development and breaks the moment you deploy.<\/p>\n\n\n\n<p>The frustrating part: nothing in your code changed between environments. The bug isn&#8217;t in your styles \u2014 it&#8217;s in how production builds handle them.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"why-css-import-order-is-non-deterministic-in-production\"><span class=\"underline-accent\">Why CSS Import Order Is Non-Deterministic in Production<\/span><\/h3>\n\n\n\n<p>The Next.js dev server loads stylesheets sequentially, in the order your components mount. Predictable, consistent, easy to reason about. Production builds work differently \u2014 <a href=\"http:\/\/nextjs.org\/docs\/app\/building-your-application\/routing\/layouts-and-pages\">webpack splits your code into chunks<\/a> and optimises them for parallel loading. <\/p>\n\n\n\n<p>That process doesn&#8217;t guarantee the order your CSS files are evaluated, which means styles that override each other correctly in dev can load in the wrong sequence in production.<\/p>\n\n\n\n<p>The most common trigger: importing global CSS from inside a component rather than from the root layout.<\/p>\n\n\n\n<pre class=\"wp-block-code-mind-code c-code\"><code class=\"javascript\">\/* styles\/button.css *\/\n\n.btn { background: blue; }\n\n\/* styles\/overrides.css *\/\n\n.btn { background: red; } \/* intended to win *\/<\/code><\/pre>\n\n\n\n<p>In dev, overrides.css loads after button.css \u2014 red wins. In production, chunk ordering puts button.css last \u2014 blue wins. The styles didn&#8217;t change. The load order did.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"three-fixes-ranked-by-permanence\"><span class=\"underline-accent\">Three Fixes, Ranked by Permanence<\/span><\/h3>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"fix-1-scope-your-styles-with-css-modules-permanent\">Fix 1 \u2014 Scope your styles with CSS Modules (permanent)<\/h4>\n\n\n\n<p>CSS Modules generate unique class names per component, eliminating cascade conflicts entirely. No two components can accidentally override each other&#8217;s styles because the class names are never shared.<\/p>\n\n\n\n<pre class=\"wp-block-code-mind-code c-code\"><code class=\"javascript\">\/* button.module.css *\/\n\n.btn { background: blue; }\n\nimport styles from '.\/button.module.css'\n\nexport function Button() {\n\n\u00a0return &lt;button className={styles.btn}>Click&lt;\/button>\n\n}<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"fix-2-move-all-global-css-imports-to-your-root-layout-immediate\">Fix 2 \u2014 Move all global CSS imports to your root layout (immediate)<\/h4>\n\n\n\n<p>Global stylesheets should only be imported once, at the top of the tree. If you&#8217;re importing globals.css anywhere other than app\/layout.tsx, <a href=\"http:\/\/nextjs.org\/docs\/app\/building-your-application\/styling\/css#importing-styles\">move it there now<\/a>.<\/p>\n\n\n\n<pre class=\"wp-block-code-mind-code c-code\"><code class=\"javascript\">\/\/ app\/layout.tsx \u2014 correct\n\nimport '.\/globals.css'\n\n\/\/ SomeComponent.tsx \u2014 wrong, causes ordering issues\n\nimport '..\/styles\/globals.css'<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"fix-3-check-tailwinds-content-paths-diagnostic\">Fix 3 \u2014 Check Tailwind&#8217;s content paths (diagnostic)<\/h4>\n\n\n\n<p>If you&#8217;re using Tailwind and classes are missing in production but present in dev, your content config isn&#8217;t covering all component directories. Tailwind purges unused classes at build time \u2014 if a path is missing, the class gets stripped.<\/p>\n\n\n\n<pre class=\"wp-block-code-mind-code c-code\"><code class=\"javascript\">\/\/ tailwind.config.js\n\nmodule.exports = {\n\n\u00a0content: [\n\n\u00a0\u00a0\u00a0'.\/app\/**\/*.{js,ts,jsx,tsx}',\n\n\u00a0\u00a0\u00a0'.\/components\/**\/*.{js,ts,jsx,tsx}', \/\/ missing this = classes disappearing\n\n\u00a0],\n\n}<\/code><\/pre>\n\n\n\n<p>Start with Fix 3 to rule out Tailwind purging. If that&#8217;s not the cause, Fix 2 costs five minutes. Fix 1 is the permanent solution \u2014 migrate to CSS Modules as you touch components.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"problem-2-the-use-client-virus-is-bloating-your-javascript-bundle\">Problem 2 \u2014 The &#8220;use client&#8221; Virus Is Bloating Your JavaScript Bundle<\/h2>\n\n\n\n<p>By default, every component in the <a href=\"https:\/\/pagepro.co\/blog\/app-router-vs-page-router-comparison\">Next.js App Router<\/a>&nbsp; is a server component. Its JavaScript never reaches the browser. No bundle cost, no hydration overhead \u2014 the server handles it and sends HTML. That&#8217;s the default you want to preserve as much as possible.<\/p>\n\n\n\n<p>Here&#8217;s how developers accidentally throw it away.<\/p>\n\n\n\n<p>A page is working fine as a <a href=\"http:\/\/nextjs.org\/docs\/app\/building-your-application\/rendering\/server-components\">server component<\/a>. Someone needs to add a like button \u2014 a single piece of interactivity. React throws an error: useState only works in a client component, add use client. The developer adds it to the top of the page file, the error disappears, the button works. <\/p>\n\n\n\n<p>What they didn&#8217;t notice: every component in that page&#8217;s subtree now ships its JavaScript to the browser. The date library, the card layout, the data formatting utilities \u2014 all of it, bundled and sent to the client for no reason.<\/p>\n\n\n\n<p>That&#8217;s the virus. One directive in the wrong place, silently inflating your bundle.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"how-to-spot-it-in-your-bundle-analyzer-output\"><span class=\"underline-accent\">How to Spot It in Your Bundle Analyzer Output<\/span><\/h3>\n\n\n\n<p>Open your bundle analyzer and navigate to the affected route. If you see libraries that have no business running in a browser \u2014 date formatters, markdown parsers, database clients, server-side utilities \u2014 they&#8217;ve leaked through a misplaced use client. <\/p>\n\n\n\n<p>In the example from our codebase, date-fns appeared at 16.98KB in the client bundle on a page that only displayed a formatted date. Nothing on that page required the browser at all.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"the-leaf-node-pattern-correct-extraction-with-code\"><span class=\"underline-accent\">The Leaf Node Pattern \u2014 Correct Extraction with Code<\/span><\/h3>\n\n\n\n<p>The fix is to push use client <a href=\"http:\/\/nextjs.org\/docs\/app\/building-your-application\/rendering\/client-components#moving-client-components-down-the-tree\">as far down the component tree as possible<\/a> \u2014 to the specific component that actually needs interactivity, not the page that contains it.<\/p>\n\n\n\n<p>Before \u2014 <strong>virus pattern<\/strong>:<\/p>\n\n\n\n<pre class=\"wp-block-code-mind-code c-code\"><code class=\"javascript\">'use client' \/\/ infects the entire page tree\n\nimport { format } from 'date-fns'\n\nimport { useState } from 'react'\n\nimport { Heart } from 'lucide-react'\n\nexport default function BlogPost({ post }) {\n\n\u00a0const [liked, setLiked] = useState(false)\n\n\u00a0return (\n\n\u00a0\u00a0\u00a0&lt;article>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0&lt;h1>{post.title}&lt;\/h1>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0&lt;time>{format(post.date, 'PPP')}&lt;\/time>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0&lt;button onClick={() => setLiked(!liked)}>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&lt;Heart \/>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0&lt;\/button>\n\n\u00a0\u00a0\u00a0&lt;\/article>\n\n\u00a0)\n\n}<\/code><\/pre>\n\n\n\n<p>After \u2014 leaf node pattern:<\/p>\n\n\n\n<pre class=\"wp-block-code-mind-code c-code\"><code class=\"javascript\">\/\/ app\/blog\/[slug]\/like-button.tsx\n\n'use client' \/\/ isolated to the one component that needs it\n\nimport { useState } from 'react'\n\nimport { Heart } from 'lucide-react'\n\nexport function LikeButton() {\n\n\u00a0const [liked, setLiked] = useState(false)\n\n\u00a0return &lt;button onClick={() => setLiked(!liked)}>&lt;Heart \/>&lt;\/button>\n\n}\n\n\/\/ app\/blog\/[slug]\/page.tsx \u2014 stays a server component\n\nimport { format } from 'date-fns' \/\/ no longer in client bundle\n\nimport { LikeButton } from '.\/like-button'\n\nexport default function BlogPost({ post }) {\n\n\u00a0return (\n\n\u00a0\u00a0\u00a0&lt;article>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0&lt;h1>{post.title}&lt;\/h1>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0&lt;time>{format(post.date, 'PPP')}&lt;\/time>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0&lt;LikeButton \/>\n\n\u00a0\u00a0\u00a0&lt;\/article>\n\n\u00a0)\n\n}<\/code><\/pre>\n\n\n\n<p><strong>date-fns <\/strong>disappears from the client bundle entirely. The page stays a server component. The only thing the browser receives is the LikeButton \u2014 which is the only thing that ever needed to be there.<\/p>\n\n\n\n<p>The rule is simple: if a component doesn&#8217;t use browser APIs, event handlers, or React hooks, it has no business being a client component.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"problem-3-sequential-data-fetches-are-adding-seconds-to-every-page-load\">Problem 3 \u2014 Sequential Data Fetches Are Adding Seconds to Every Page Load<\/h2>\n\n\n\n<p>Here&#8217;s a real timing breakdown from a course platform page with four data fetches:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><tbody><tr><td>Fetch<\/td><td>Time<\/td><\/tr><tr><td>Featured course<\/td><td>1.5s<\/td><\/tr><tr><td>All courses<\/td><td>1.5s<\/td><\/tr><tr><td>Instructors<\/td><td>1.21s<\/td><\/tr><tr><td>Reviews<\/td><td>1.0s<\/td><\/tr><tr><td>Total<\/td><td>5.2s<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p>None of these depend on each other. The reviews don&#8217;t need the instructors. The courses don&#8217;t need the featured course. Yet because each await blocks the next, the page waits the full 5.2 seconds before rendering anything. That&#8217;s a <a href=\"http:\/\/nextjs.org\/docs\/app\/building-your-application\/routing\/loading-ui-and-streaming\">data waterfall <\/a>\u2014 and it&#8217;s entirely accidental.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"how-waterfalls-form-in-server-components\"><span class=\"underline-accent\">How Waterfalls Form in Server Components<\/span><\/h3>\n\n\n\n<p>The pattern is easy to write without realising the cost:<\/p>\n\n\n\n<pre class=\"wp-block-code-mind-code c-code\"><code class=\"javascript\">export default async function CoursePage() {\n\n\u00a0const featured = await getFeaturedCourse() \u00a0 \/\/ 1.5s\n\n\u00a0const courses = await getAllCourses()\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \/\/ starts at 1.5s\n\n\u00a0const instructors = await getInstructors()\u00a0 \u00a0 \/\/ starts at 3.0s\n\n\u00a0const reviews = await getReviews()\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \/\/ starts at 4.21s\n\n\u00a0return &lt;>{\/* all data available at 5.2s *\/}&lt;\/>\n\n}<\/code><\/pre>\n\n\n\n<p>Each await resolves before the next line executes. Sequential by design, waterfall by accident.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"promise-all-vs-suspense-boundaries-decision-table\"><span class=\"underline-accent\">Promise.all vs. Suspense Boundaries \u2014 Decision Table<\/span><\/h3>\n\n\n\n<p>Two fixes. Which one to use depends on whether you want streaming.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"fix-1-promise-all-parallel-no-streaming\">Fix 1 \u2014 Promise.all (parallel, no streaming):<\/h4>\n\n\n\n<pre class=\"wp-block-code-mind-code c-code\"><code class=\"javascript\">export default async function CoursePage() {\n\n\u00a0const [featured, courses, instructors, reviews] = await Promise.all([\n\n\u00a0\u00a0\u00a0getFeaturedCourse(),\n\n\u00a0\u00a0\u00a0getAllCourses(),\n\n\u00a0\u00a0\u00a0getInstructors(),\n\n\u00a0\u00a0\u00a0getReviews(),\n\n\u00a0])\n\n\u00a0return &lt;>{\/* all data available at ~1.5s *\/}&lt;\/>\n\n}<\/code><\/pre>\n\n\n\n<p><a href=\"http:\/\/nextjs.org\/docs\/app\/building-your-application\/data-fetching\/fetching#parallel-and-sequential-data-fetching\">All four fetches<\/a> fire simultaneously. Total wait time drops to the slowest individual fetch \u2014 roughly 1.5 seconds. The page still waits for all data before rendering anything, but the wall clock time is cut by more than half.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"fix-2-suspense-boundaries-parallel-streaming\">Fix 2 \u2014 Suspense boundaries (parallel + streaming):<\/h4>\n\n\n\n<pre class=\"wp-block-code-mind-code c-code\"><code class=\"javascript\">export default function CoursePage() {\n\n\u00a0return (\n\n\u00a0\u00a0\u00a0&lt;>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0&lt;Suspense fallback={&lt;Skeleton \/>}>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&lt;FeaturedCourse \/> \u00a0 {\/* fetches and renders as soon as ready *\/}\n\n\u00a0\u00a0\u00a0\u00a0\u00a0&lt;\/Suspense>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0&lt;Suspense fallback={&lt;Skeleton \/>}>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&lt;CourseList \/> \u00a0 \u00a0 \u00a0 {\/* independent, doesn't wait for above *\/}\n\n\u00a0\u00a0\u00a0\u00a0\u00a0&lt;\/Suspense>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0&lt;Suspense fallback={&lt;Skeleton \/>}>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&lt;Reviews \/>\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 {\/* streams in at ~1s *\/}\n\n\u00a0\u00a0\u00a0\u00a0\u00a0&lt;\/Suspense>\n\n\u00a0\u00a0\u00a0&lt;\/>\n\n\u00a0)\n\n}<\/code><\/pre>\n\n\n\n<p>Each component fetches its own data and renders the moment it&#8217;s ready. Reviews stream in at 1 second without waiting for courses. Users see content progressively instead of a blank page for 5 seconds.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><tbody><tr><td><\/td><td>Promise.all<\/td><td>Suspense boundaries<\/td><\/tr><tr><td>Setup complexity<\/td><td>Low<\/td><td>Medium<\/td><\/tr><tr><td>Streaming<\/td><td>No<\/td><td>Yes<\/td><\/tr><tr><td>Perceived performance<\/td><td>Good<\/td><td>Best<\/td><\/tr><tr><td>Best for<\/td><td>Simple pages, equal-speed fetches<\/td><td>Mixed-speed sections, long pages<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p>Default to Suspense boundaries in the App Router. Use Promise.all when the page is simple enough that streaming adds no meaningful UX benefit.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"problem-4-generatemetadata-and-your-page-are-both-hitting-the-database\">Problem 4 \u2014 generateMetadata and Your Page Are Both Hitting the Database<\/h2>\n\n\n\n<p>This one doesn&#8217;t throw an error. There&#8217;s no warning in the console, no performance alarm. Your page works correctly. You just don&#8217;t realise you&#8217;re paying for every database query twice.<\/p>\n\n\n\n<p>Here&#8217;s the scenario: you have a course page that needs the course data for two things \u2014 the <a href=\"https:\/\/pagepro.co\/blog\/nextjs-seo\">SEO metadata<\/a> (title, description) and the page itself (rendering the content). So you fetch it in generateMetadata and fetch it again in the page component.<\/p>\n\n\n\n<pre class=\"wp-block-code-mind-code c-code\"><code class=\"javascript\">export async function generateMetadata({ params }) {\n\n\u00a0const course = await getCourse(params.id) \/\/ DB hit #1\n\n\u00a0return { title: course.title, description: course.description }\n\n}\n\nexport default async function CoursePage({ params }) {\n\n\u00a0const course = await getCourse(params.id) \/\/ DB hit #2 \u2014 same row\n\n\u00a0return &lt;CourseContent course={course} \/>\n\n}<\/code><\/pre>\n\n\n\n<p>Check your server logs and you&#8217;ll see the same query firing twice on every page load. At scale, that doubles your database read volume for no reason.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"using-react-cache-to-deduplicate-server-fetches\"><span class=\"underline-accent\">Using React cache() to Deduplicate Server Fetches<\/span><\/h3>\n\n\n\n<p>React.cache() <a href=\"http:\/\/react.dev\/reference\/react\/cache\">memoizes a function&#8217;s return value<\/a> for the duration of a single server request. Wrap your fetch function once \u2014 every call to it within the same request returns the cached result instead of hitting the database again.<\/p>\n\n\n\n<pre class=\"wp-block-code-mind-code c-code\"><code class=\"javascript\">import { cache } from 'react'\n\nexport const getCourse = cache(async (id: string) => {\n\n\u00a0return await db.course.findUnique({ where: { id } })\n\n})\n\n<\/code><\/pre>\n\n\n\n<p>Now both generateMetadata and the page component can call <strong>getCourse() <\/strong>freely. The database is queried once. The second call costs nothing.<\/p>\n\n\n\n<pre class=\"wp-block-code-mind-code c-code\"><code class=\"javascript\">export async function generateMetadata({ params }) {\n\n\u00a0const course = await getCourse(params.id) \/\/ DB hit #1\n\n\u00a0return { title: course.title, description: course.description }\n\n}\n\nexport default async function CoursePage({ params }) {\n\n\u00a0const course = await getCourse(params.id) \/\/ returns cached result\n\n\u00a0return\n\n}<\/code><\/pre>\n\n\n\n<p>One important distinction: React.cache() is <a href=\"http:\/\/nextjs.org\/docs\/app\/building-your-application\/caching#request-memoization\">per-request, not global<\/a>. The cache is discarded after each server render cycle. There&#8217;s no risk of one user seeing another user&#8217;s data.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"problem-5-hydration-errors-that-only-surface-in-production\">Problem 5 \u2014 Hydration Errors That Only Surface in Production<\/h2>\n\n\n\n<p>Hydration is the process where React takes the<a href=\"https:\/\/pagepro.co\/blog\/ssr-csr-ssg\"> HTML rendered by the server <\/a>and <a href=\"http:\/\/react.dev\/reference\/react-dom\/client\/hydrateRoot\">attaches event listeners and state to it <\/a>on the client. For this to work, the client render must produce output identical to the server render. When it doesn&#8217;t, you get a <a href=\"http:\/\/react.dev\/reference\/react\/useId\">hydration error<\/a>.<\/p>\n\n\n\n<p>In development, Next.js is forgiving \u2014 hot module reloading masks many mismatches. In production, React is strict. The mismatch surfaces as a console error, a visible UI flash, or in severe cases, a broken page.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"the-three-most-common-hydration-mismatch-causes\"><span class=\"underline-accent\">The Three Most Common Hydration Mismatch Causes<\/span><\/h3>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"1-browser-apis-accessed-during-render\">1. Browser APIs accessed during render<\/h4>\n\n\n\n<p>window, localStorage, navigator \u2014 none of these exist on the server. If your component reads them during the render phase, the server produces different HTML than the client.<\/p>\n\n\n\n<pre class=\"wp-block-code-mind-code c-code\"><code class=\"javascript\">\/\/ breaks \u2014 window doesn't exist on server\n\nexport function Banner() {\n\n\u00a0return &lt;p>Your OS: {window.navigator.platform}&lt;\/p>\n\n}\n\n\/\/ fix \u2014 move to useEffect, runs client-only\n\nexport function Banner() {\n\n\u00a0const [platform, setPlatform] = useState('')\n\n\u00a0useEffect(() => { setPlatform(window.navigator.platform) }, [])\n\n\u00a0return &lt;p>Your OS: {platform}&lt;\/p>\n\n}<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"2-non-deterministic-values\">2. Non-deterministic values<\/h4>\n\n\n\n<p><strong>Math.random(), Date.now(), and new Date()<\/strong> produce different values on the server and client, causing guaranteed mismatches.<\/p>\n\n\n\n<pre class=\"wp-block-code-mind-code c-code\"><code class=\"javascript\">\/\/ breaks \u2014 different value server vs. client\n\n&lt;div id={`item-${Math.random()}`}>\n\n\/\/ fix \u2014 use React's stable ID hook\n\nconst id = useId()\n\n&lt;div id={id}><\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"3-invalid-html-nesting\">3. Invalid HTML nesting<\/h4>\n\n\n\n<p>Browsers silently auto-correct invalid nesting during HTML parsing \u2014 for example, a &lt;div> inside a &lt;p> gets moved outside it. React sees the corrected DOM and finds it doesn&#8217;t match its virtual DOM, triggering a mismatch.<\/p>\n\n\n\n<pre class=\"wp-block-code-mind-code c-code\"><code class=\"javascript\">\/\/ breaks \u2014 div cannot be a child of p\n\n&lt;p>&lt;div>Some content&lt;\/div>&lt;\/p>\n\n\/\/ fix\n\n&lt;div>&lt;div>Some content&lt;\/div>&lt;\/div><\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"how-to-debug-the-react-hydration-error-message\"><span class=\"underline-accent\">How to Debug the React Hydration Error Message<\/span><\/h3>\n\n\n\n<p>React 18 tells you exactly what mismatched. The console error shows the component stack and a diff of expected vs. received output:<\/p>\n\n\n\n<p>Error: Hydration failed because the server rendered HTML didn&#8217;t match the client.<\/p>\n\n\n\n<p>Expected server HTML to contain a matching &lt;div&gt; in &lt;p&gt;<\/p>\n\n\n\n<p>Work up the component stack from the flagged element until you find where server and client diverge. In most cases it&#8217;s one of the three causes above.<\/p>\n\n\n\n<p>One escape hatch exists: <strong>suppressHydrationWarning={true}<\/strong> on an element tells React to ignore mismatches on that node. Use it only for genuinely unavoidable cases \u2014 browser-injected attributes like class added by extensions. Using it to silence real bugs just hides the problem.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"frequently-asked-questions\">Frequently Asked Questions<\/h2>\n\n\n\n<div class=\"schema-faq wp-block-yoast-faq-block\"><div class=\"schema-faq-section\" id=\"faq-question-1776857315829\"><strong class=\"schema-faq-question\">Why does my CSS look different in Next.js production vs. development?<br\/><\/strong> <p class=\"schema-faq-answer\">The Next.js dev server loads stylesheets sequentially, but production builds use webpack chunk splitting which doesn&#8217;t guarantee CSS load order. If your styles rely on cascade order between files, production can load them in a different sequence and break your overrides. The fix is to use CSS Modules for component-level styles and import all global CSS exclusively from your root layout.tsx.<\/p> <\/div> <div class=\"schema-faq-section\" id=\"faq-question-1776857330825\"><strong class=\"schema-faq-question\">What is the &#8220;use client&#8221; virus in Next.js and how do I fix it?<br\/><\/strong> <p class=\"schema-faq-answer\">The &#8220;use client&#8221; virus happens when a developer adds use client to a parent or page-level component to enable one interactive element, which silently marks the entire component subtree as client-side code \u2014 shipping all of it to the browser. The fix is to extract interactive elements into isolated leaf node components with their own use client directive, leaving the rest of the page as a server component.<\/p> <\/div> <div class=\"schema-faq-section\" id=\"faq-question-1776857346379\"><strong class=\"schema-faq-question\">How do I run data fetches in parallel in Next.js server components?<\/strong> <p class=\"schema-faq-answer\">Replace sequential await calls with Promise.all() to fire all independent fetches simultaneously, or co-locate each fetch inside its own component and wrap it in a &lt;Suspense> boundary to enable streaming. The Suspense approach is preferred in the App Router because it lets each section render as soon as its data is ready, rather than waiting for all fetches to complete.<\/p> <\/div> <div class=\"schema-faq-section\" id=\"faq-question-1776857366174\"><strong class=\"schema-faq-question\">Why is Next.js calling my database twice per request?<br\/><\/strong> <p class=\"schema-faq-answer\">If you fetch the same data in both generateMetadata and your page component, Next.js executes both functions in separate contexts with no automatic deduplication \u2014 resulting in two identical database queries per page load. Wrap your fetch function in React&#8217;s cache() utility to memoize the result for the duration of the request, so both functions share a single database call.<\/p> <\/div> <div class=\"schema-faq-section\" id=\"faq-question-1776857382430\"><strong class=\"schema-faq-question\">How do I fix a hydration error in Next.js App Router?<br\/><\/strong> <p class=\"schema-faq-answer\">Hydration errors are caused by a mismatch between the HTML the server renders and what React produces on the client. The most common causes are accessing browser APIs like window during render, using non-deterministic values like Math.random(), and invalid HTML nesting. Move browser API access into useEffect, use useId() for stable IDs, and validate your HTML structure to resolve the mismatch.<\/p> <\/div> <div class=\"schema-faq-section\" id=\"faq-question-1776857408855\"><strong class=\"schema-faq-question\">How do I reduce JavaScript bundle size in Next.js?<br\/><\/strong> <p class=\"schema-faq-answer\">Start by running the bundle analyzer to identify which libraries are in your client bundle that don&#8217;t need to be there. The most common cause of bundle bloat is a misplaced use client directive that pulls server-safe libraries \u2014 date formatters, data utilities, markdown parsers \u2014 into the browser unnecessarily. Moving those components back to the server or extracting only the interactive parts into leaf node client components typically produces the largest gains.<\/p> <\/div> <\/div>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"the-pattern-is-always-the-same\">The Pattern Is Always the Same<\/h2>\n\n\n\n<p>The fastest place to start is the bundle analyzer. Run it against your production build today and you&#8217;ll likely find at least one library that has no business being in the browser.<\/p>\n\n\n\n<p>If you want to go deeper, the <a href=\"https:\/\/pagepro.co\/ebook\/ebook-nextjs-performance\">Next.js performance optimization ebook<\/a> covers these patterns in detail with more complete code walkthroughs. And if you&#8217;re dealing with these issues at scale, our <a href=\"https:\/\/pagepro.co\/services\/nextjs-performance-optimization\">Next.js performance optimization service<\/a> is worth a look.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>TL;DR Introduction Recently users on reddit&nbsp; analyzed 500 active Next.js GitHub issues and posted their findings. The most uncomfortable conclusion: the majority of Next.js production issues aren&#8217;t framework bugs. They&#8217;re patterns \u2014 the same five or six mistakes, reproduced across hundreds of codebases by developers who learned React in a client-side world and never fully [&hellip;]<\/p>\n","protected":false},"author":52,"featured_media":23270,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[659,609,76],"tags":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v21.3 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Next.js Production Issues: 5 Common Problems &amp; How to Fix Them<\/title>\n<meta name=\"description\" content=\"From CSS breaking in production to use client viruses and data waterfalls. A data-backed guide to the most common Next.js production failures\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/pagepro.co\/blog\/nextjs-production-issues\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Next.js Production Issues: 5 Common Problems &amp; How to Fix Them\" \/>\n<meta property=\"og:description\" content=\"From CSS breaking in production to use client viruses and data waterfalls. A data-backed guide to the most common Next.js production failures\" \/>\n<meta property=\"og:url\" content=\"https:\/\/pagepro.co\/blog\/nextjs-production-issues\/\" \/>\n<meta property=\"og:site_name\" content=\"Pagepro\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/thisispagepro\" \/>\n<meta property=\"article:published_time\" content=\"2026-04-22T11:36:42+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2026-04-22T11:36:44+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/pagepro.co\/blog\/wp-content\/uploads\/2026\/04\/React-Native-0.83-Zero-Breaking-Changes-8.png\" \/>\n\t<meta property=\"og:image:width\" content=\"1024\" \/>\n\t<meta property=\"og:image:height\" content=\"582\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Alina Ramanenkava\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Alina Ramanenkava\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"11 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/pagepro.co\/blog\/nextjs-production-issues\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/pagepro.co\/blog\/nextjs-production-issues\/\"},\"author\":{\"name\":\"Alina Ramanenkava\",\"@id\":\"https:\/\/pagepro.co\/blog\/#\/schema\/person\/8c62fcae68748c6b7f55ab13578fb299\"},\"headline\":\"Hidden Cost of AI-Generated Code: What Breaks When You Go to Production\",\"datePublished\":\"2026-04-22T11:36:42+00:00\",\"dateModified\":\"2026-04-22T11:36:44+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/pagepro.co\/blog\/nextjs-production-issues\/\"},\"wordCount\":2524,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/pagepro.co\/blog\/#organization\"},\"articleSection\":[\"Next js\",\"Nextjs\",\"Product Development\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/pagepro.co\/blog\/nextjs-production-issues\/#respond\"]}]},{\"@type\":[\"WebPage\",\"FAQPage\"],\"@id\":\"https:\/\/pagepro.co\/blog\/nextjs-production-issues\/\",\"url\":\"https:\/\/pagepro.co\/blog\/nextjs-production-issues\/\",\"name\":\"Next.js Production Issues: 5 Common Problems & How to Fix Them\",\"isPartOf\":{\"@id\":\"https:\/\/pagepro.co\/blog\/#website\"},\"datePublished\":\"2026-04-22T11:36:42+00:00\",\"dateModified\":\"2026-04-22T11:36:44+00:00\",\"description\":\"From CSS breaking in production to use client viruses and data waterfalls. A data-backed guide to the most common Next.js production failures\",\"breadcrumb\":{\"@id\":\"https:\/\/pagepro.co\/blog\/nextjs-production-issues\/#breadcrumb\"},\"mainEntity\":[{\"@id\":\"https:\/\/pagepro.co\/blog\/nextjs-production-issues\/#faq-question-1776857315829\"},{\"@id\":\"https:\/\/pagepro.co\/blog\/nextjs-production-issues\/#faq-question-1776857330825\"},{\"@id\":\"https:\/\/pagepro.co\/blog\/nextjs-production-issues\/#faq-question-1776857346379\"},{\"@id\":\"https:\/\/pagepro.co\/blog\/nextjs-production-issues\/#faq-question-1776857366174\"},{\"@id\":\"https:\/\/pagepro.co\/blog\/nextjs-production-issues\/#faq-question-1776857382430\"},{\"@id\":\"https:\/\/pagepro.co\/blog\/nextjs-production-issues\/#faq-question-1776857408855\"}],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/pagepro.co\/blog\/nextjs-production-issues\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/pagepro.co\/blog\/nextjs-production-issues\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/pagepro.co\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Blog\",\"item\":\"https:\/\/pagepro.co\/blog\/\"},{\"@type\":\"ListItem\",\"position\":3,\"name\":\"Hidden Cost of AI-Generated Code: What Breaks When You Go to Production\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/pagepro.co\/blog\/#website\",\"url\":\"https:\/\/pagepro.co\/blog\/\",\"name\":\"Pagepro\",\"description\":\"Frictionless Next.js, Expo &amp; Sanity Development Blog\",\"publisher\":{\"@id\":\"https:\/\/pagepro.co\/blog\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/pagepro.co\/blog\/?s={search_term_string}\"},\"query-input\":\"required name=search_term_string\"}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/pagepro.co\/blog\/#organization\",\"name\":\"Pagepro\",\"url\":\"https:\/\/pagepro.co\/blog\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/pagepro.co\/blog\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/pagepro.co\/blog\/wp-content\/uploads\/2020\/08\/logo_pagepro-b66d228a1e-1.png\",\"contentUrl\":\"https:\/\/pagepro.co\/blog\/wp-content\/uploads\/2020\/08\/logo_pagepro-b66d228a1e-1.png\",\"width\":440,\"height\":200,\"caption\":\"Pagepro\"},\"image\":{\"@id\":\"https:\/\/pagepro.co\/blog\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/www.facebook.com\/thisispagepro\"]},{\"@type\":\"Person\",\"@id\":\"https:\/\/pagepro.co\/blog\/#\/schema\/person\/8c62fcae68748c6b7f55ab13578fb299\",\"name\":\"Alina Ramanenkava\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/pagepro.co\/blog\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/6a0f2716dcb70589077c1e962347f3ce?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/6a0f2716dcb70589077c1e962347f3ce?s=96&d=mm&r=g\",\"caption\":\"Alina Ramanenkava\"},\"description\":\"Growth Marketing Manager with 9+ years of experience in B2B SaaS. Specializes in SEO, content strategy, and inbound growth, with a focus on turning organic traffic into qualified pipeline.\",\"sameAs\":[\"https:\/\/www.linkedin.com\/in\/alina-shamich\/\"],\"url\":\"https:\/\/pagepro.co\/blog\/author\/alina-ramanenkava\/\"},{\"@type\":\"Question\",\"@id\":\"https:\/\/pagepro.co\/blog\/nextjs-production-issues\/#faq-question-1776857315829\",\"position\":1,\"url\":\"https:\/\/pagepro.co\/blog\/nextjs-production-issues\/#faq-question-1776857315829\",\"name\":\"Why does my CSS look different in Next.js production vs. development?\",\"answerCount\":1,\"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"The Next.js dev server loads stylesheets sequentially, but production builds use webpack chunk splitting which doesn't guarantee CSS load order. If your styles rely on cascade order between files, production can load them in a different sequence and break your overrides. The fix is to use CSS Modules for component-level styles and import all global CSS exclusively from your root layout.tsx.\",\"inLanguage\":\"en-US\"},\"inLanguage\":\"en-US\"},{\"@type\":\"Question\",\"@id\":\"https:\/\/pagepro.co\/blog\/nextjs-production-issues\/#faq-question-1776857330825\",\"position\":2,\"url\":\"https:\/\/pagepro.co\/blog\/nextjs-production-issues\/#faq-question-1776857330825\",\"name\":\"What is the \\\"use client\\\" virus in Next.js and how do I fix it?\",\"answerCount\":1,\"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"The \\\"use client\\\" virus happens when a developer adds use client to a parent or page-level component to enable one interactive element, which silently marks the entire component subtree as client-side code \u2014 shipping all of it to the browser. The fix is to extract interactive elements into isolated leaf node components with their own use client directive, leaving the rest of the page as a server component.\",\"inLanguage\":\"en-US\"},\"inLanguage\":\"en-US\"},{\"@type\":\"Question\",\"@id\":\"https:\/\/pagepro.co\/blog\/nextjs-production-issues\/#faq-question-1776857346379\",\"position\":3,\"url\":\"https:\/\/pagepro.co\/blog\/nextjs-production-issues\/#faq-question-1776857346379\",\"name\":\"How do I run data fetches in parallel in Next.js server components?\",\"answerCount\":1,\"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"Replace sequential await calls with Promise.all() to fire all independent fetches simultaneously, or co-locate each fetch inside its own component and wrap it in a &lt;Suspense> boundary to enable streaming. The Suspense approach is preferred in the App Router because it lets each section render as soon as its data is ready, rather than waiting for all fetches to complete.\",\"inLanguage\":\"en-US\"},\"inLanguage\":\"en-US\"},{\"@type\":\"Question\",\"@id\":\"https:\/\/pagepro.co\/blog\/nextjs-production-issues\/#faq-question-1776857366174\",\"position\":4,\"url\":\"https:\/\/pagepro.co\/blog\/nextjs-production-issues\/#faq-question-1776857366174\",\"name\":\"Why is Next.js calling my database twice per request?\",\"answerCount\":1,\"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"If you fetch the same data in both generateMetadata and your page component, Next.js executes both functions in separate contexts with no automatic deduplication \u2014 resulting in two identical database queries per page load. Wrap your fetch function in React's cache() utility to memoize the result for the duration of the request, so both functions share a single database call.\",\"inLanguage\":\"en-US\"},\"inLanguage\":\"en-US\"},{\"@type\":\"Question\",\"@id\":\"https:\/\/pagepro.co\/blog\/nextjs-production-issues\/#faq-question-1776857382430\",\"position\":5,\"url\":\"https:\/\/pagepro.co\/blog\/nextjs-production-issues\/#faq-question-1776857382430\",\"name\":\"How do I fix a hydration error in Next.js App Router?\",\"answerCount\":1,\"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"Hydration errors are caused by a mismatch between the HTML the server renders and what React produces on the client. The most common causes are accessing browser APIs like window during render, using non-deterministic values like Math.random(), and invalid HTML nesting. Move browser API access into useEffect, use useId() for stable IDs, and validate your HTML structure to resolve the mismatch.\",\"inLanguage\":\"en-US\"},\"inLanguage\":\"en-US\"},{\"@type\":\"Question\",\"@id\":\"https:\/\/pagepro.co\/blog\/nextjs-production-issues\/#faq-question-1776857408855\",\"position\":6,\"url\":\"https:\/\/pagepro.co\/blog\/nextjs-production-issues\/#faq-question-1776857408855\",\"name\":\"How do I reduce JavaScript bundle size in Next.js?\",\"answerCount\":1,\"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"Start by running the bundle analyzer to identify which libraries are in your client bundle that don't need to be there. The most common cause of bundle bloat is a misplaced use client directive that pulls server-safe libraries \u2014 date formatters, data utilities, markdown parsers \u2014 into the browser unnecessarily. Moving those components back to the server or extracting only the interactive parts into leaf node client components typically produces the largest gains.\",\"inLanguage\":\"en-US\"},\"inLanguage\":\"en-US\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Next.js Production Issues: 5 Common Problems & How to Fix Them","description":"From CSS breaking in production to use client viruses and data waterfalls. A data-backed guide to the most common Next.js production failures","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/pagepro.co\/blog\/nextjs-production-issues\/","og_locale":"en_US","og_type":"article","og_title":"Next.js Production Issues: 5 Common Problems & How to Fix Them","og_description":"From CSS breaking in production to use client viruses and data waterfalls. A data-backed guide to the most common Next.js production failures","og_url":"https:\/\/pagepro.co\/blog\/nextjs-production-issues\/","og_site_name":"Pagepro","article_publisher":"https:\/\/www.facebook.com\/thisispagepro","article_published_time":"2026-04-22T11:36:42+00:00","article_modified_time":"2026-04-22T11:36:44+00:00","og_image":[{"width":1024,"height":582,"url":"https:\/\/pagepro.co\/blog\/wp-content\/uploads\/2026\/04\/React-Native-0.83-Zero-Breaking-Changes-8.png","type":"image\/png"}],"author":"Alina Ramanenkava","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Alina Ramanenkava","Est. reading time":"11 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/pagepro.co\/blog\/nextjs-production-issues\/#article","isPartOf":{"@id":"https:\/\/pagepro.co\/blog\/nextjs-production-issues\/"},"author":{"name":"Alina Ramanenkava","@id":"https:\/\/pagepro.co\/blog\/#\/schema\/person\/8c62fcae68748c6b7f55ab13578fb299"},"headline":"Hidden Cost of AI-Generated Code: What Breaks When You Go to Production","datePublished":"2026-04-22T11:36:42+00:00","dateModified":"2026-04-22T11:36:44+00:00","mainEntityOfPage":{"@id":"https:\/\/pagepro.co\/blog\/nextjs-production-issues\/"},"wordCount":2524,"commentCount":0,"publisher":{"@id":"https:\/\/pagepro.co\/blog\/#organization"},"articleSection":["Next js","Nextjs","Product Development"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/pagepro.co\/blog\/nextjs-production-issues\/#respond"]}]},{"@type":["WebPage","FAQPage"],"@id":"https:\/\/pagepro.co\/blog\/nextjs-production-issues\/","url":"https:\/\/pagepro.co\/blog\/nextjs-production-issues\/","name":"Next.js Production Issues: 5 Common Problems & How to Fix Them","isPartOf":{"@id":"https:\/\/pagepro.co\/blog\/#website"},"datePublished":"2026-04-22T11:36:42+00:00","dateModified":"2026-04-22T11:36:44+00:00","description":"From CSS breaking in production to use client viruses and data waterfalls. A data-backed guide to the most common Next.js production failures","breadcrumb":{"@id":"https:\/\/pagepro.co\/blog\/nextjs-production-issues\/#breadcrumb"},"mainEntity":[{"@id":"https:\/\/pagepro.co\/blog\/nextjs-production-issues\/#faq-question-1776857315829"},{"@id":"https:\/\/pagepro.co\/blog\/nextjs-production-issues\/#faq-question-1776857330825"},{"@id":"https:\/\/pagepro.co\/blog\/nextjs-production-issues\/#faq-question-1776857346379"},{"@id":"https:\/\/pagepro.co\/blog\/nextjs-production-issues\/#faq-question-1776857366174"},{"@id":"https:\/\/pagepro.co\/blog\/nextjs-production-issues\/#faq-question-1776857382430"},{"@id":"https:\/\/pagepro.co\/blog\/nextjs-production-issues\/#faq-question-1776857408855"}],"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/pagepro.co\/blog\/nextjs-production-issues\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/pagepro.co\/blog\/nextjs-production-issues\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/pagepro.co\/"},{"@type":"ListItem","position":2,"name":"Blog","item":"https:\/\/pagepro.co\/blog\/"},{"@type":"ListItem","position":3,"name":"Hidden Cost of AI-Generated Code: What Breaks When You Go to Production"}]},{"@type":"WebSite","@id":"https:\/\/pagepro.co\/blog\/#website","url":"https:\/\/pagepro.co\/blog\/","name":"Pagepro","description":"Frictionless Next.js, Expo &amp; Sanity Development Blog","publisher":{"@id":"https:\/\/pagepro.co\/blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/pagepro.co\/blog\/?s={search_term_string}"},"query-input":"required name=search_term_string"}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/pagepro.co\/blog\/#organization","name":"Pagepro","url":"https:\/\/pagepro.co\/blog\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/pagepro.co\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/pagepro.co\/blog\/wp-content\/uploads\/2020\/08\/logo_pagepro-b66d228a1e-1.png","contentUrl":"https:\/\/pagepro.co\/blog\/wp-content\/uploads\/2020\/08\/logo_pagepro-b66d228a1e-1.png","width":440,"height":200,"caption":"Pagepro"},"image":{"@id":"https:\/\/pagepro.co\/blog\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/thisispagepro"]},{"@type":"Person","@id":"https:\/\/pagepro.co\/blog\/#\/schema\/person\/8c62fcae68748c6b7f55ab13578fb299","name":"Alina Ramanenkava","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/pagepro.co\/blog\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/6a0f2716dcb70589077c1e962347f3ce?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/6a0f2716dcb70589077c1e962347f3ce?s=96&d=mm&r=g","caption":"Alina Ramanenkava"},"description":"Growth Marketing Manager with 9+ years of experience in B2B SaaS. Specializes in SEO, content strategy, and inbound growth, with a focus on turning organic traffic into qualified pipeline.","sameAs":["https:\/\/www.linkedin.com\/in\/alina-shamich\/"],"url":"https:\/\/pagepro.co\/blog\/author\/alina-ramanenkava\/"},{"@type":"Question","@id":"https:\/\/pagepro.co\/blog\/nextjs-production-issues\/#faq-question-1776857315829","position":1,"url":"https:\/\/pagepro.co\/blog\/nextjs-production-issues\/#faq-question-1776857315829","name":"Why does my CSS look different in Next.js production vs. development?","answerCount":1,"acceptedAnswer":{"@type":"Answer","text":"The Next.js dev server loads stylesheets sequentially, but production builds use webpack chunk splitting which doesn't guarantee CSS load order. If your styles rely on cascade order between files, production can load them in a different sequence and break your overrides. The fix is to use CSS Modules for component-level styles and import all global CSS exclusively from your root layout.tsx.","inLanguage":"en-US"},"inLanguage":"en-US"},{"@type":"Question","@id":"https:\/\/pagepro.co\/blog\/nextjs-production-issues\/#faq-question-1776857330825","position":2,"url":"https:\/\/pagepro.co\/blog\/nextjs-production-issues\/#faq-question-1776857330825","name":"What is the \"use client\" virus in Next.js and how do I fix it?","answerCount":1,"acceptedAnswer":{"@type":"Answer","text":"The \"use client\" virus happens when a developer adds use client to a parent or page-level component to enable one interactive element, which silently marks the entire component subtree as client-side code \u2014 shipping all of it to the browser. The fix is to extract interactive elements into isolated leaf node components with their own use client directive, leaving the rest of the page as a server component.","inLanguage":"en-US"},"inLanguage":"en-US"},{"@type":"Question","@id":"https:\/\/pagepro.co\/blog\/nextjs-production-issues\/#faq-question-1776857346379","position":3,"url":"https:\/\/pagepro.co\/blog\/nextjs-production-issues\/#faq-question-1776857346379","name":"How do I run data fetches in parallel in Next.js server components?","answerCount":1,"acceptedAnswer":{"@type":"Answer","text":"Replace sequential await calls with Promise.all() to fire all independent fetches simultaneously, or co-locate each fetch inside its own component and wrap it in a &lt;Suspense> boundary to enable streaming. The Suspense approach is preferred in the App Router because it lets each section render as soon as its data is ready, rather than waiting for all fetches to complete.","inLanguage":"en-US"},"inLanguage":"en-US"},{"@type":"Question","@id":"https:\/\/pagepro.co\/blog\/nextjs-production-issues\/#faq-question-1776857366174","position":4,"url":"https:\/\/pagepro.co\/blog\/nextjs-production-issues\/#faq-question-1776857366174","name":"Why is Next.js calling my database twice per request?","answerCount":1,"acceptedAnswer":{"@type":"Answer","text":"If you fetch the same data in both generateMetadata and your page component, Next.js executes both functions in separate contexts with no automatic deduplication \u2014 resulting in two identical database queries per page load. Wrap your fetch function in React's cache() utility to memoize the result for the duration of the request, so both functions share a single database call.","inLanguage":"en-US"},"inLanguage":"en-US"},{"@type":"Question","@id":"https:\/\/pagepro.co\/blog\/nextjs-production-issues\/#faq-question-1776857382430","position":5,"url":"https:\/\/pagepro.co\/blog\/nextjs-production-issues\/#faq-question-1776857382430","name":"How do I fix a hydration error in Next.js App Router?","answerCount":1,"acceptedAnswer":{"@type":"Answer","text":"Hydration errors are caused by a mismatch between the HTML the server renders and what React produces on the client. The most common causes are accessing browser APIs like window during render, using non-deterministic values like Math.random(), and invalid HTML nesting. Move browser API access into useEffect, use useId() for stable IDs, and validate your HTML structure to resolve the mismatch.","inLanguage":"en-US"},"inLanguage":"en-US"},{"@type":"Question","@id":"https:\/\/pagepro.co\/blog\/nextjs-production-issues\/#faq-question-1776857408855","position":6,"url":"https:\/\/pagepro.co\/blog\/nextjs-production-issues\/#faq-question-1776857408855","name":"How do I reduce JavaScript bundle size in Next.js?","answerCount":1,"acceptedAnswer":{"@type":"Answer","text":"Start by running the bundle analyzer to identify which libraries are in your client bundle that don't need to be there. The most common cause of bundle bloat is a misplaced use client directive that pulls server-safe libraries \u2014 date formatters, data utilities, markdown parsers \u2014 into the browser unnecessarily. Moving those components back to the server or extracting only the interactive parts into leaf node client components typically produces the largest gains.","inLanguage":"en-US"},"inLanguage":"en-US"}]}},"acf":[],"_links":{"self":[{"href":"https:\/\/pagepro.co\/blog\/wp-json\/wp\/v2\/posts\/23268"}],"collection":[{"href":"https:\/\/pagepro.co\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/pagepro.co\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/pagepro.co\/blog\/wp-json\/wp\/v2\/users\/52"}],"replies":[{"embeddable":true,"href":"https:\/\/pagepro.co\/blog\/wp-json\/wp\/v2\/comments?post=23268"}],"version-history":[{"count":1,"href":"https:\/\/pagepro.co\/blog\/wp-json\/wp\/v2\/posts\/23268\/revisions"}],"predecessor-version":[{"id":23269,"href":"https:\/\/pagepro.co\/blog\/wp-json\/wp\/v2\/posts\/23268\/revisions\/23269"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/pagepro.co\/blog\/wp-json\/wp\/v2\/media\/23270"}],"wp:attachment":[{"href":"https:\/\/pagepro.co\/blog\/wp-json\/wp\/v2\/media?parent=23268"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/pagepro.co\/blog\/wp-json\/wp\/v2\/categories?post=23268"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/pagepro.co\/blog\/wp-json\/wp\/v2\/tags?post=23268"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}