[{"data":1,"prerenderedAt":5360},["ShallowReactive",2],{"blog-nextjs-bundle-size/":3},{"id":4,"title":5,"body":6,"description":5324,"extension":5325,"faqs":5326,"image":5337,"imageAlt":5338,"imageCaption":5339,"meta":5340,"navigation":221,"path":5341,"publishedAt":5342,"readingTime":1062,"references":5343,"seo":5351,"stem":5352,"tags":5353,"updatedAt":5342,"__hash__":5359},"blog/blog/nextjs-bundle-size.md","Next.js Bundle Size: Find and Fix What's Bloating Your App",{"type":7,"value":8,"toc":5279},"minimark",[9,18,28,31,34,37,115,125,130,137,142,165,321,324,345,348,352,355,402,405,408,412,415,419,494,509,513,668,671,675,682,750,757,761,823,829,832,836,839,846,849,2001,2004,2007,2632,3147,3155,3158,3223,3229,3232,3236,3239,3242,3245,3298,3301,3304,3311,3930,3933,4200,4206,4209,4250,4256,4259,4263,4266,4270,4277,4387,4499,4506,4510,4516,4529,4535,4545,4548,4595,4601,4611,4615,4618,4622,4625,4787,4791,4794,4797,4832,4843,4846,4884,4890,4894,4897,5000,5003,5084,5088,5091,5095,5098,5121,5134,5138,5141,5145,5182,5194,5198,5202,5209,5213,5219,5223,5236,5240,5254,5258,5275],[10,11,12,13,17],"p",{},"You ran ",[14,15,16],"code",{},"next build"," and saw the red text:",[19,20,25],"pre",{"className":21,"code":23,"language":24},[22],"language-text","Route (app)                              Size     First Load JS\n┌ ○ /                                    5.2 kB         412 kB\n├ ○ /dashboard                           12.1 kB        419 kB\n├ ○ /projects                            8.4 kB         415 kB\n└ ○ /settings                            3.1 kB         410 kB\n+ First Load JS shared by all routes     407 kB\n  ├ chunks/framework-2a5b6c.js           85 kB\n  ├ chunks/main-app-d4e5f6.js            35 kB\n  └ other shared chunks (total)          287 kB\n\n○ (Static)  prerendered as static content\n","text",[14,26,23],{"__ignoreMap":27},"",[10,29,30],{},"412kb of JavaScript before your users see anything. On a mid-tier Android phone over 4G, that's nearly 5 seconds of staring at a blank screen.",[10,32,33],{},"This is the story of TaskFlow — a project management dashboard built with Next.js 16, MUI, Recharts, lodash, and moment.js. A stack that's common, powerful, and heavy. We took it from a 412kb First Load JS and a PageSpeed score of 38 to 185kb and a score of 82. Every fix is documented with exact numbers so you can apply the same process to your own app.",[10,35,36],{},"Here's what we started with:",[38,39,40,53],"table",{},[41,42,43],"thead",{},[44,45,46,50],"tr",{},[47,48,49],"th",{},"Metric",[47,51,52],{},"Value",[54,55,56,65,73,81,93,104],"tbody",{},[44,57,58,62],{},[59,60,61],"td",{},"First Load JS",[59,63,64],{},"412kb",[44,66,67,70],{},[59,68,69],{},"Total bundle (uncompressed)",[59,71,72],{},"1.8MB",[44,74,75,78],{},[59,76,77],{},"PageSpeed mobile",[59,79,80],{},"38",[44,82,83,90],{},[59,84,85],{},[86,87,89],"a",{"href":88},"/blog/how-to-improve-lcp/","LCP",[59,91,92],{},"4.8s",[44,94,95,101],{},[59,96,97],{},[86,98,100],{"href":99},"/blog/improve-inp-score/","INP",[59,102,103],{},"340ms",[44,105,106,112],{},[59,107,108],{},[86,109,111],{"href":110},"/blog/fix-cumulative-layout-shift/","CLS",[59,113,114],{},"0.02",[10,116,117,118,124],{},"According to a ",[86,119,123],{"href":120,"rel":121},"https://www.catchmetrics.io/blog/nextjs-bundle-sizes-insights-from-300000-domains",[122],"nofollow","study of 300,000 Next.js production sites",", the median app ships over 1MB of JavaScript. The lightest 10% manage around 350kb. We're going to beat both.",[126,127,129],"h2",{"id":128},"step-1-see-where-the-bytes-are-going","Step 1: See where the bytes are going",[10,131,132,133,136],{},"Before fixing anything, you need to see what's in the bundle. Guessing wastes time. The ",[14,134,135],{},"@next/bundle-analyzer"," plugin generates a treemap that shows every package and its size.",[138,139,141],"h3",{"id":140},"set-up-the-analyzer","Set up the analyzer",[19,143,147],{"className":144,"code":145,"language":146,"meta":27,"style":27},"language-bash shiki shiki-themes github-light github-dark","npm install @next/bundle-analyzer\n","bash",[14,148,149],{"__ignoreMap":27},[150,151,154,158,162],"span",{"class":152,"line":153},"line",1,[150,155,157],{"class":156},"sScJk","npm",[150,159,161],{"class":160},"sZZnC"," install",[150,163,164],{"class":160}," @next/bundle-analyzer\n",[19,166,170],{"className":167,"code":168,"language":169,"meta":27,"style":27},"language-typescript shiki shiki-themes github-light github-dark","// next.config.ts\nimport type { NextConfig } from 'next';\nimport bundleAnalyzer from '@next/bundle-analyzer';\n\nconst withBundleAnalyzer = bundleAnalyzer({\n  enabled: process.env.ANALYZE === 'true',\n});\n\nconst nextConfig: NextConfig = {\n  // your existing config\n};\n\nexport default withBundleAnalyzer(nextConfig);\n","typescript",[14,171,172,178,201,216,223,242,260,266,271,290,296,302,307],{"__ignoreMap":27},[150,173,174],{"class":152,"line":153},[150,175,177],{"class":176},"sJ8bj","// next.config.ts\n",[150,179,181,185,188,192,195,198],{"class":152,"line":180},2,[150,182,184],{"class":183},"szBVR","import",[150,186,187],{"class":183}," type",[150,189,191],{"class":190},"sVt8B"," { NextConfig } ",[150,193,194],{"class":183},"from",[150,196,197],{"class":160}," 'next'",[150,199,200],{"class":190},";\n",[150,202,204,206,209,211,214],{"class":152,"line":203},3,[150,205,184],{"class":183},[150,207,208],{"class":190}," bundleAnalyzer ",[150,210,194],{"class":183},[150,212,213],{"class":160}," '@next/bundle-analyzer'",[150,215,200],{"class":190},[150,217,219],{"class":152,"line":218},4,[150,220,222],{"emptyLinePlaceholder":221},true,"\n",[150,224,226,229,233,236,239],{"class":152,"line":225},5,[150,227,228],{"class":183},"const",[150,230,232],{"class":231},"sj4cs"," withBundleAnalyzer",[150,234,235],{"class":183}," =",[150,237,238],{"class":156}," bundleAnalyzer",[150,240,241],{"class":190},"({\n",[150,243,245,248,251,254,257],{"class":152,"line":244},6,[150,246,247],{"class":190},"  enabled: process.env.",[150,249,250],{"class":231},"ANALYZE",[150,252,253],{"class":183}," ===",[150,255,256],{"class":160}," 'true'",[150,258,259],{"class":190},",\n",[150,261,263],{"class":152,"line":262},7,[150,264,265],{"class":190},"});\n",[150,267,269],{"class":152,"line":268},8,[150,270,222],{"emptyLinePlaceholder":221},[150,272,274,276,279,282,285,287],{"class":152,"line":273},9,[150,275,228],{"class":183},[150,277,278],{"class":231}," nextConfig",[150,280,281],{"class":183},":",[150,283,284],{"class":156}," NextConfig",[150,286,235],{"class":183},[150,288,289],{"class":190}," {\n",[150,291,293],{"class":152,"line":292},10,[150,294,295],{"class":176},"  // your existing config\n",[150,297,299],{"class":152,"line":298},11,[150,300,301],{"class":190},"};\n",[150,303,305],{"class":152,"line":304},12,[150,306,222],{"emptyLinePlaceholder":221},[150,308,310,313,316,318],{"class":152,"line":309},13,[150,311,312],{"class":183},"export",[150,314,315],{"class":183}," default",[150,317,232],{"class":156},[150,319,320],{"class":190},"(nextConfig);\n",[10,322,323],{},"Run it:",[19,325,327],{"className":144,"code":326,"language":146,"meta":27,"style":27},"ANALYZE=true next build\n",[14,328,329],{"__ignoreMap":27},[150,330,331,333,336,339,342],{"class":152,"line":153},[150,332,250],{"class":190},[150,334,335],{"class":183},"=",[150,337,338],{"class":160},"true",[150,340,341],{"class":156}," next",[150,343,344],{"class":160}," build\n",[10,346,347],{},"A browser tab opens with an interactive treemap. Each rectangle is a file or package, sized proportionally to its contribution to the bundle.",[138,349,351],{"id":350},"what-taskflows-treemap-revealed","What TaskFlow's treemap revealed",[10,353,354],{},"The treemap told us everything we needed to know:",[356,357,358,374,380,386,396],"ul",{},[359,360,361,365,366,369,370,373],"li",{},[362,363,364],"strong",{},"lodash"," — 73kb. We used ",[14,367,368],{},"groupBy"," and ",[14,371,372],{},"debounce",". Two functions. 73kb.",[359,375,376,379],{},[362,377,378],{},"@mui/icons-material"," — 41kb. Barrel imports were pulling in the entire icon set. We used 12 icons.",[359,381,382,385],{},[362,383,384],{},"recharts"," — 45kb. Loaded on every page. Charts only appear on the analytics tab.",[359,387,388,391,392,395],{},[362,389,390],{},"moment.js"," — 67kb. Used for three ",[14,393,394],{},"format()"," calls. Doesn't tree-shake.",[359,397,398,401],{},[362,399,400],{},"framer-motion"," — 32kb. An old animation experiment. Still imported, never rendered.",[10,403,404],{},"That's 258kb of JavaScript that either shouldn't be there or shouldn't load upfront. More than half the total bundle.",[10,406,407],{},"The treemap doesn't lie. Every large rectangle is a specific fix waiting to happen. Let's start with the easiest win.",[126,409,411],{"id":410},"fix-1-barrel-imports-and-tree-shaking-saved-64kb","Fix 1: Barrel imports and tree-shaking (saved 64kb)",[10,413,414],{},"Barrel imports are the most common cause of bundle bloat in Next.js apps using component libraries. One import statement can pull in an entire library.",[138,416,418],{"id":417},"the-problem","The problem",[19,420,422],{"className":167,"code":421,"language":169,"meta":27,"style":27},"// This processes the entire MUI library — every component, every export\nimport { Button, TextField, Card, Chip } from '@mui/material';\n\n// This is even worse — thousands of icons, all bundled\nimport { Dashboard, Settings, Person, Delete, Edit,\n         Save, Close, Search, Add, Remove, Check, Warning } from '@mui/icons-material';\n\n// And lodash — 73kb for two functions\nimport { groupBy, debounce } from 'lodash';\n",[14,423,424,429,443,447,452,459,471,475,480],{"__ignoreMap":27},[150,425,426],{"class":152,"line":153},[150,427,428],{"class":176},"// This processes the entire MUI library — every component, every export\n",[150,430,431,433,436,438,441],{"class":152,"line":180},[150,432,184],{"class":183},[150,434,435],{"class":190}," { Button, TextField, Card, Chip } ",[150,437,194],{"class":183},[150,439,440],{"class":160}," '@mui/material'",[150,442,200],{"class":190},[150,444,445],{"class":152,"line":203},[150,446,222],{"emptyLinePlaceholder":221},[150,448,449],{"class":152,"line":218},[150,450,451],{"class":176},"// This is even worse — thousands of icons, all bundled\n",[150,453,454,456],{"class":152,"line":225},[150,455,184],{"class":183},[150,457,458],{"class":190}," { Dashboard, Settings, Person, Delete, Edit,\n",[150,460,461,464,466,469],{"class":152,"line":244},[150,462,463],{"class":190},"         Save, Close, Search, Add, Remove, Check, Warning } ",[150,465,194],{"class":183},[150,467,468],{"class":160}," '@mui/icons-material'",[150,470,200],{"class":190},[150,472,473],{"class":152,"line":262},[150,474,222],{"emptyLinePlaceholder":221},[150,476,477],{"class":152,"line":268},[150,478,479],{"class":176},"// And lodash — 73kb for two functions\n",[150,481,482,484,487,489,492],{"class":152,"line":273},[150,483,184],{"class":183},[150,485,486],{"class":190}," { groupBy, debounce } ",[150,488,194],{"class":183},[150,490,491],{"class":160}," 'lodash'",[150,493,200],{"class":190},[10,495,496,497,500,501,503,504,508],{},"When your bundler sees ",[14,498,499],{},"from '@mui/material'",", it has to process the barrel file that re-exports every component. For ",[14,502,378],{},", named imports can be ",[86,505,507],{"href":506},"/blog/mui-material-ui-performance/","6x slower than path imports"," because there are thousands of icons.",[138,510,512],{"id":511},"the-fix","The fix",[19,514,516],{"className":167,"code":515,"language":169,"meta":27,"style":27},"// MUI components — import from the specific path\nimport Button from '@mui/material/Button';\nimport TextField from '@mui/material/TextField';\nimport Card from '@mui/material/Card';\nimport Chip from '@mui/material/Chip';\n\n// MUI icons — each icon is ~1kb instead of 41kb for all\nimport Dashboard from '@mui/icons-material/Dashboard';\nimport Settings from '@mui/icons-material/Settings';\nimport Person from '@mui/icons-material/Person';\n\n// lodash — import individual functions\nimport groupBy from 'lodash/groupBy';\nimport debounce from 'lodash/debounce';\n",[14,517,518,523,537,551,565,579,583,588,602,616,630,634,639,653],{"__ignoreMap":27},[150,519,520],{"class":152,"line":153},[150,521,522],{"class":176},"// MUI components — import from the specific path\n",[150,524,525,527,530,532,535],{"class":152,"line":180},[150,526,184],{"class":183},[150,528,529],{"class":190}," Button ",[150,531,194],{"class":183},[150,533,534],{"class":160}," '@mui/material/Button'",[150,536,200],{"class":190},[150,538,539,541,544,546,549],{"class":152,"line":203},[150,540,184],{"class":183},[150,542,543],{"class":190}," TextField ",[150,545,194],{"class":183},[150,547,548],{"class":160}," '@mui/material/TextField'",[150,550,200],{"class":190},[150,552,553,555,558,560,563],{"class":152,"line":218},[150,554,184],{"class":183},[150,556,557],{"class":190}," Card ",[150,559,194],{"class":183},[150,561,562],{"class":160}," '@mui/material/Card'",[150,564,200],{"class":190},[150,566,567,569,572,574,577],{"class":152,"line":225},[150,568,184],{"class":183},[150,570,571],{"class":190}," Chip ",[150,573,194],{"class":183},[150,575,576],{"class":160}," '@mui/material/Chip'",[150,578,200],{"class":190},[150,580,581],{"class":152,"line":244},[150,582,222],{"emptyLinePlaceholder":221},[150,584,585],{"class":152,"line":262},[150,586,587],{"class":176},"// MUI icons — each icon is ~1kb instead of 41kb for all\n",[150,589,590,592,595,597,600],{"class":152,"line":268},[150,591,184],{"class":183},[150,593,594],{"class":190}," Dashboard ",[150,596,194],{"class":183},[150,598,599],{"class":160}," '@mui/icons-material/Dashboard'",[150,601,200],{"class":190},[150,603,604,606,609,611,614],{"class":152,"line":273},[150,605,184],{"class":183},[150,607,608],{"class":190}," Settings ",[150,610,194],{"class":183},[150,612,613],{"class":160}," '@mui/icons-material/Settings'",[150,615,200],{"class":190},[150,617,618,620,623,625,628],{"class":152,"line":292},[150,619,184],{"class":183},[150,621,622],{"class":190}," Person ",[150,624,194],{"class":183},[150,626,627],{"class":160}," '@mui/icons-material/Person'",[150,629,200],{"class":190},[150,631,632],{"class":152,"line":298},[150,633,222],{"emptyLinePlaceholder":221},[150,635,636],{"class":152,"line":304},[150,637,638],{"class":176},"// lodash — import individual functions\n",[150,640,641,643,646,648,651],{"class":152,"line":309},[150,642,184],{"class":183},[150,644,645],{"class":190}," groupBy ",[150,647,194],{"class":183},[150,649,650],{"class":160}," 'lodash/groupBy'",[150,652,200],{"class":190},[150,654,656,658,661,663,666],{"class":152,"line":655},14,[150,657,184],{"class":183},[150,659,660],{"class":190}," debounce ",[150,662,194],{"class":183},[150,664,665],{"class":160}," 'lodash/debounce'",[150,667,200],{"class":190},[10,669,670],{},"More verbose, but the bundle impact is dramatic.",[138,672,674],{"id":673},"let-nextjs-handle-it-automatically","Let Next.js handle it automatically",[10,676,677,678,681],{},"Next.js has a built-in solution. Add ",[14,679,680],{},"optimizePackageImports"," to your config, and barrel imports are automatically converted to path imports during the build:",[19,683,685],{"className":167,"code":684,"language":169,"meta":27,"style":27},"// next.config.ts\nconst nextConfig: NextConfig = {\n  experimental: {\n    optimizePackageImports: [\n      '@mui/material',\n      '@mui/icons-material',\n      'lodash',\n    ],\n  },\n};\n",[14,686,687,691,705,710,715,722,729,736,741,746],{"__ignoreMap":27},[150,688,689],{"class":152,"line":153},[150,690,177],{"class":176},[150,692,693,695,697,699,701,703],{"class":152,"line":180},[150,694,228],{"class":183},[150,696,278],{"class":231},[150,698,281],{"class":183},[150,700,284],{"class":156},[150,702,235],{"class":183},[150,704,289],{"class":190},[150,706,707],{"class":152,"line":203},[150,708,709],{"class":190},"  experimental: {\n",[150,711,712],{"class":152,"line":218},[150,713,714],{"class":190},"    optimizePackageImports: [\n",[150,716,717,720],{"class":152,"line":225},[150,718,719],{"class":160},"      '@mui/material'",[150,721,259],{"class":190},[150,723,724,727],{"class":152,"line":244},[150,725,726],{"class":160},"      '@mui/icons-material'",[150,728,259],{"class":190},[150,730,731,734],{"class":152,"line":262},[150,732,733],{"class":160},"      'lodash'",[150,735,259],{"class":190},[150,737,738],{"class":152,"line":268},[150,739,740],{"class":190},"    ],\n",[150,742,743],{"class":152,"line":273},[150,744,745],{"class":190},"  },\n",[150,747,748],{"class":152,"line":292},[150,749,301],{"class":190},[10,751,752,753,756],{},"With this config, you can keep writing ",[14,754,755],{},"import { Button } from '@mui/material'"," and Next.js will transform it to the path import at build time. Best of both worlds.",[138,758,760],{"id":759},"results","Results",[38,762,763,779],{},[41,764,765],{},[44,766,767,770,773,776],{},[47,768,769],{},"Package",[47,771,772],{},"Before",[47,774,775],{},"After",[47,777,778],{},"Saved",[54,780,781,794,807],{},[44,782,783,785,788,791],{},[59,784,364],{},[59,786,787],{},"73kb",[59,789,790],{},"9kb",[59,792,793],{},"64kb",[44,795,796,798,801,804],{},[59,797,378],{},[59,799,800],{},"41kb",[59,802,803],{},"3kb",[59,805,806],{},"38kb",[44,808,809,814,816,818],{},[59,810,811],{},[362,812,813],{},"Subtotal",[59,815],{},[59,817],{},[59,819,820,822],{},[362,821,793],{}," (after dedup with shared chunks)",[19,824,827],{"className":825,"code":826,"language":24},[22],"First Load JS: 412kb → 348kb\n",[14,828,826],{"__ignoreMap":27},[10,830,831],{},"64kb gone. One config change and some import cleanup. This is almost always the biggest win per effort in any Next.js app using third-party component libraries.",[126,833,835],{"id":834},"fix-2-move-data-work-to-server-components-saved-58kb","Fix 2: Move data work to Server Components (saved 58kb)",[10,837,838],{},"TaskFlow's dashboard page had a single large Client Component. It fetched project data, transformed it into chart-ready formats, computed summary statistics, and rendered everything — including parts that had zero interactivity.",[10,840,841,842,845],{},"The ",[14,843,844],{},"'use client'"," directive at the top meant all of that code shipped to the browser, even the parts that never needed to run there.",[138,847,418],{"id":848},"the-problem-1",[19,850,852],{"className":167,"code":851,"language":169,"meta":27,"style":27},"'use client';\n\nimport { useState } from 'react';\nimport { groupBy } from 'lodash/groupBy';\nimport { format } from 'date-fns';\nimport Card from '@mui/material/Card';\nimport CardContent from '@mui/material/CardContent';\nimport Typography from '@mui/material/Typography';\nimport Select from '@mui/material/Select';\nimport MenuItem from '@mui/material/MenuItem';\n\ninterface Project {\n  id: string;\n  name: string;\n  status: 'active' | 'completed' | 'paused';\n  updatedAt: string;\n  tasks: number;\n  completedTasks: number;\n}\n\nexport default function DashboardPage() {\n  const [statusFilter, setStatusFilter] = useState('all');\n  const [projects, setProjects] = useState\u003CProject[]>([]);\n\n  // All of this runs in the browser — fetching, transforming, computing\n  useEffect(() => {\n    fetch('/api/projects').then(r => r.json()).then(setProjects);\n  }, []);\n\n  const grouped = groupBy(projects, 'status');\n  const totalTasks = projects.reduce((sum, p) => sum + p.tasks, 0);\n  const completedTasks = projects.reduce((sum, p) => sum + p.completedTasks, 0);\n  const completionRate = totalTasks > 0 ? Math.round((completedTasks / totalTasks) * 100) : 0;\n\n  const filtered = statusFilter === 'all'\n    ? projects\n    : projects.filter(p => p.status === statusFilter);\n\n  return (\n    \u003Cdiv>\n      \u003CTypography variant=\"h4\">Dashboard\u003C/Typography>\n\n      {/* Summary cards — completely static once rendered */}\n      \u003Cdiv className=\"summary-grid\">\n        \u003CCard>\u003CCardContent>\n          \u003CTypography color=\"textSecondary\">Total Projects\u003C/Typography>\n          \u003CTypography variant=\"h3\">{projects.length}\u003C/Typography>\n        \u003C/CardContent>\u003C/Card>\n        \u003CCard>\u003CCardContent>\n          \u003CTypography color=\"textSecondary\">Completion Rate\u003C/Typography>\n          \u003CTypography variant=\"h3\">{completionRate}%\u003C/Typography>\n        \u003C/CardContent>\u003C/Card>\n      \u003C/div>\n\n      {/* Only this part needs to be interactive */}\n      \u003CSelect value={statusFilter} onChange={(e) => setStatusFilter(e.target.value)}>\n        \u003CMenuItem value=\"all\">All\u003C/MenuItem>\n        \u003CMenuItem value=\"active\">Active\u003C/MenuItem>\n        \u003CMenuItem value=\"completed\">Completed\u003C/MenuItem>\n      \u003C/Select>\n\n      {filtered.map(project => (\n        \u003CCard key={project.id}>\n          \u003CCardContent>\n            \u003CTypography>{project.name}\u003C/Typography>\n            \u003CTypography color=\"textSecondary\">\n              Updated {format(new Date(project.updatedAt), 'MMM d, yyyy')}\n            \u003C/Typography>\n          \u003C/CardContent>\n        \u003C/Card>\n      ))}\n    \u003C/div>\n  );\n}\n",[14,853,854,860,864,878,891,905,917,931,945,959,973,977,987,1000,1011,1035,1047,1060,1072,1078,1083,1099,1134,1164,1169,1175,1189,1228,1234,1239,1260,1305,1342,1393,1398,1417,1426,1451,1456,1465,1477,1504,1509,1520,1535,1552,1577,1600,1616,1629,1651,1674,1689,1699,1704,1714,1739,1764,1787,1810,1820,1825,1848,1863,1872,1889,1902,1945,1955,1965,1974,1980,1990,1996],{"__ignoreMap":27},[150,855,856,858],{"class":152,"line":153},[150,857,844],{"class":160},[150,859,200],{"class":190},[150,861,862],{"class":152,"line":180},[150,863,222],{"emptyLinePlaceholder":221},[150,865,866,868,871,873,876],{"class":152,"line":203},[150,867,184],{"class":183},[150,869,870],{"class":190}," { useState } ",[150,872,194],{"class":183},[150,874,875],{"class":160}," 'react'",[150,877,200],{"class":190},[150,879,880,882,885,887,889],{"class":152,"line":218},[150,881,184],{"class":183},[150,883,884],{"class":190}," { groupBy } ",[150,886,194],{"class":183},[150,888,650],{"class":160},[150,890,200],{"class":190},[150,892,893,895,898,900,903],{"class":152,"line":225},[150,894,184],{"class":183},[150,896,897],{"class":190}," { format } ",[150,899,194],{"class":183},[150,901,902],{"class":160}," 'date-fns'",[150,904,200],{"class":190},[150,906,907,909,911,913,915],{"class":152,"line":244},[150,908,184],{"class":183},[150,910,557],{"class":190},[150,912,194],{"class":183},[150,914,562],{"class":160},[150,916,200],{"class":190},[150,918,919,921,924,926,929],{"class":152,"line":262},[150,920,184],{"class":183},[150,922,923],{"class":190}," CardContent ",[150,925,194],{"class":183},[150,927,928],{"class":160}," '@mui/material/CardContent'",[150,930,200],{"class":190},[150,932,933,935,938,940,943],{"class":152,"line":268},[150,934,184],{"class":183},[150,936,937],{"class":190}," Typography ",[150,939,194],{"class":183},[150,941,942],{"class":160}," '@mui/material/Typography'",[150,944,200],{"class":190},[150,946,947,949,952,954,957],{"class":152,"line":273},[150,948,184],{"class":183},[150,950,951],{"class":190}," Select ",[150,953,194],{"class":183},[150,955,956],{"class":160}," '@mui/material/Select'",[150,958,200],{"class":190},[150,960,961,963,966,968,971],{"class":152,"line":292},[150,962,184],{"class":183},[150,964,965],{"class":190}," MenuItem ",[150,967,194],{"class":183},[150,969,970],{"class":160}," '@mui/material/MenuItem'",[150,972,200],{"class":190},[150,974,975],{"class":152,"line":298},[150,976,222],{"emptyLinePlaceholder":221},[150,978,979,982,985],{"class":152,"line":304},[150,980,981],{"class":183},"interface",[150,983,984],{"class":156}," Project",[150,986,289],{"class":190},[150,988,989,993,995,998],{"class":152,"line":309},[150,990,992],{"class":991},"s4XuR","  id",[150,994,281],{"class":183},[150,996,997],{"class":231}," string",[150,999,200],{"class":190},[150,1001,1002,1005,1007,1009],{"class":152,"line":655},[150,1003,1004],{"class":991},"  name",[150,1006,281],{"class":183},[150,1008,997],{"class":231},[150,1010,200],{"class":190},[150,1012,1014,1017,1019,1022,1025,1028,1030,1033],{"class":152,"line":1013},15,[150,1015,1016],{"class":991},"  status",[150,1018,281],{"class":183},[150,1020,1021],{"class":160}," 'active'",[150,1023,1024],{"class":183}," |",[150,1026,1027],{"class":160}," 'completed'",[150,1029,1024],{"class":183},[150,1031,1032],{"class":160}," 'paused'",[150,1034,200],{"class":190},[150,1036,1038,1041,1043,1045],{"class":152,"line":1037},16,[150,1039,1040],{"class":991},"  updatedAt",[150,1042,281],{"class":183},[150,1044,997],{"class":231},[150,1046,200],{"class":190},[150,1048,1050,1053,1055,1058],{"class":152,"line":1049},17,[150,1051,1052],{"class":991},"  tasks",[150,1054,281],{"class":183},[150,1056,1057],{"class":231}," number",[150,1059,200],{"class":190},[150,1061,1063,1066,1068,1070],{"class":152,"line":1062},18,[150,1064,1065],{"class":991},"  completedTasks",[150,1067,281],{"class":183},[150,1069,1057],{"class":231},[150,1071,200],{"class":190},[150,1073,1075],{"class":152,"line":1074},19,[150,1076,1077],{"class":190},"}\n",[150,1079,1081],{"class":152,"line":1080},20,[150,1082,222],{"emptyLinePlaceholder":221},[150,1084,1086,1088,1090,1093,1096],{"class":152,"line":1085},21,[150,1087,312],{"class":183},[150,1089,315],{"class":183},[150,1091,1092],{"class":183}," function",[150,1094,1095],{"class":156}," DashboardPage",[150,1097,1098],{"class":190},"() {\n",[150,1100,1102,1105,1108,1111,1114,1117,1120,1122,1125,1128,1131],{"class":152,"line":1101},22,[150,1103,1104],{"class":183},"  const",[150,1106,1107],{"class":190}," [",[150,1109,1110],{"class":231},"statusFilter",[150,1112,1113],{"class":190},", ",[150,1115,1116],{"class":231},"setStatusFilter",[150,1118,1119],{"class":190},"] ",[150,1121,335],{"class":183},[150,1123,1124],{"class":156}," useState",[150,1126,1127],{"class":190},"(",[150,1129,1130],{"class":160},"'all'",[150,1132,1133],{"class":190},");\n",[150,1135,1137,1139,1141,1144,1146,1149,1151,1153,1155,1158,1161],{"class":152,"line":1136},23,[150,1138,1104],{"class":183},[150,1140,1107],{"class":190},[150,1142,1143],{"class":231},"projects",[150,1145,1113],{"class":190},[150,1147,1148],{"class":231},"setProjects",[150,1150,1119],{"class":190},[150,1152,335],{"class":183},[150,1154,1124],{"class":156},[150,1156,1157],{"class":190},"\u003C",[150,1159,1160],{"class":156},"Project",[150,1162,1163],{"class":190},"[]>([]);\n",[150,1165,1167],{"class":152,"line":1166},24,[150,1168,222],{"emptyLinePlaceholder":221},[150,1170,1172],{"class":152,"line":1171},25,[150,1173,1174],{"class":176},"  // All of this runs in the browser — fetching, transforming, computing\n",[150,1176,1178,1181,1184,1187],{"class":152,"line":1177},26,[150,1179,1180],{"class":156},"  useEffect",[150,1182,1183],{"class":190},"(() ",[150,1185,1186],{"class":183},"=>",[150,1188,289],{"class":190},[150,1190,1192,1195,1197,1200,1203,1206,1208,1211,1214,1217,1220,1223,1225],{"class":152,"line":1191},27,[150,1193,1194],{"class":156},"    fetch",[150,1196,1127],{"class":190},[150,1198,1199],{"class":160},"'/api/projects'",[150,1201,1202],{"class":190},").",[150,1204,1205],{"class":156},"then",[150,1207,1127],{"class":190},[150,1209,1210],{"class":991},"r",[150,1212,1213],{"class":183}," =>",[150,1215,1216],{"class":190}," r.",[150,1218,1219],{"class":156},"json",[150,1221,1222],{"class":190},"()).",[150,1224,1205],{"class":156},[150,1226,1227],{"class":190},"(setProjects);\n",[150,1229,1231],{"class":152,"line":1230},28,[150,1232,1233],{"class":190},"  }, []);\n",[150,1235,1237],{"class":152,"line":1236},29,[150,1238,222],{"emptyLinePlaceholder":221},[150,1240,1242,1244,1247,1249,1252,1255,1258],{"class":152,"line":1241},30,[150,1243,1104],{"class":183},[150,1245,1246],{"class":231}," grouped",[150,1248,235],{"class":183},[150,1250,1251],{"class":156}," groupBy",[150,1253,1254],{"class":190},"(projects, ",[150,1256,1257],{"class":160},"'status'",[150,1259,1133],{"class":190},[150,1261,1263,1265,1268,1270,1273,1276,1279,1282,1284,1286,1289,1291,1294,1297,1300,1303],{"class":152,"line":1262},31,[150,1264,1104],{"class":183},[150,1266,1267],{"class":231}," totalTasks",[150,1269,235],{"class":183},[150,1271,1272],{"class":190}," projects.",[150,1274,1275],{"class":156},"reduce",[150,1277,1278],{"class":190},"((",[150,1280,1281],{"class":991},"sum",[150,1283,1113],{"class":190},[150,1285,10],{"class":991},[150,1287,1288],{"class":190},") ",[150,1290,1186],{"class":183},[150,1292,1293],{"class":190}," sum ",[150,1295,1296],{"class":183},"+",[150,1298,1299],{"class":190}," p.tasks, ",[150,1301,1302],{"class":231},"0",[150,1304,1133],{"class":190},[150,1306,1308,1310,1313,1315,1317,1319,1321,1323,1325,1327,1329,1331,1333,1335,1338,1340],{"class":152,"line":1307},32,[150,1309,1104],{"class":183},[150,1311,1312],{"class":231}," completedTasks",[150,1314,235],{"class":183},[150,1316,1272],{"class":190},[150,1318,1275],{"class":156},[150,1320,1278],{"class":190},[150,1322,1281],{"class":991},[150,1324,1113],{"class":190},[150,1326,10],{"class":991},[150,1328,1288],{"class":190},[150,1330,1186],{"class":183},[150,1332,1293],{"class":190},[150,1334,1296],{"class":183},[150,1336,1337],{"class":190}," p.completedTasks, ",[150,1339,1302],{"class":231},[150,1341,1133],{"class":190},[150,1343,1345,1347,1350,1352,1355,1358,1361,1364,1367,1370,1373,1376,1379,1382,1385,1387,1389,1391],{"class":152,"line":1344},33,[150,1346,1104],{"class":183},[150,1348,1349],{"class":231}," completionRate",[150,1351,235],{"class":183},[150,1353,1354],{"class":190}," totalTasks ",[150,1356,1357],{"class":183},">",[150,1359,1360],{"class":231}," 0",[150,1362,1363],{"class":183}," ?",[150,1365,1366],{"class":190}," Math.",[150,1368,1369],{"class":156},"round",[150,1371,1372],{"class":190},"((completedTasks ",[150,1374,1375],{"class":183},"/",[150,1377,1378],{"class":190}," totalTasks) ",[150,1380,1381],{"class":183},"*",[150,1383,1384],{"class":231}," 100",[150,1386,1288],{"class":190},[150,1388,281],{"class":183},[150,1390,1360],{"class":231},[150,1392,200],{"class":190},[150,1394,1396],{"class":152,"line":1395},34,[150,1397,222],{"emptyLinePlaceholder":221},[150,1399,1401,1403,1406,1408,1411,1414],{"class":152,"line":1400},35,[150,1402,1104],{"class":183},[150,1404,1405],{"class":231}," filtered",[150,1407,235],{"class":183},[150,1409,1410],{"class":190}," statusFilter ",[150,1412,1413],{"class":183},"===",[150,1415,1416],{"class":160}," 'all'\n",[150,1418,1420,1423],{"class":152,"line":1419},36,[150,1421,1422],{"class":183},"    ?",[150,1424,1425],{"class":190}," projects\n",[150,1427,1429,1432,1434,1437,1439,1441,1443,1446,1448],{"class":152,"line":1428},37,[150,1430,1431],{"class":183},"    :",[150,1433,1272],{"class":190},[150,1435,1436],{"class":156},"filter",[150,1438,1127],{"class":190},[150,1440,10],{"class":991},[150,1442,1213],{"class":183},[150,1444,1445],{"class":190}," p.status ",[150,1447,1413],{"class":183},[150,1449,1450],{"class":190}," statusFilter);\n",[150,1452,1454],{"class":152,"line":1453},38,[150,1455,222],{"emptyLinePlaceholder":221},[150,1457,1459,1462],{"class":152,"line":1458},39,[150,1460,1461],{"class":183},"  return",[150,1463,1464],{"class":190}," (\n",[150,1466,1468,1471,1474],{"class":152,"line":1467},40,[150,1469,1470],{"class":190},"    \u003C",[150,1472,1473],{"class":156},"div",[150,1475,1476],{"class":190},">\n",[150,1478,1480,1483,1486,1488,1491,1493,1496,1499,1502],{"class":152,"line":1479},41,[150,1481,1482],{"class":183},"      \u003C",[150,1484,1485],{"class":190},"Typography variant",[150,1487,335],{"class":183},[150,1489,1490],{"class":160},"\"h4\"",[150,1492,1357],{"class":183},[150,1494,1495],{"class":190},"Dashboard",[150,1497,1498],{"class":183},"\u003C/",[150,1500,1501],{"class":190},"Typography",[150,1503,1476],{"class":183},[150,1505,1507],{"class":152,"line":1506},42,[150,1508,222],{"emptyLinePlaceholder":221},[150,1510,1512,1515,1518],{"class":152,"line":1511},43,[150,1513,1514],{"class":190},"      {",[150,1516,1517],{"class":176},"/* Summary cards — completely static once rendered */",[150,1519,1077],{"class":190},[150,1521,1523,1525,1528,1530,1533],{"class":152,"line":1522},44,[150,1524,1482],{"class":183},[150,1526,1527],{"class":190},"div className",[150,1529,335],{"class":183},[150,1531,1532],{"class":160},"\"summary-grid\"",[150,1534,1476],{"class":183},[150,1536,1538,1541,1544,1547,1550],{"class":152,"line":1537},45,[150,1539,1540],{"class":190},"        \u003C",[150,1542,1543],{"class":156},"Card",[150,1545,1546],{"class":190},">\u003C",[150,1548,1549],{"class":156},"CardContent",[150,1551,1476],{"class":190},[150,1553,1555,1558,1561,1563,1566,1568,1571,1573,1575],{"class":152,"line":1554},46,[150,1556,1557],{"class":183},"          \u003C",[150,1559,1560],{"class":190},"Typography color",[150,1562,335],{"class":183},[150,1564,1565],{"class":160},"\"textSecondary\"",[150,1567,1357],{"class":183},[150,1569,1570],{"class":190},"Total Projects",[150,1572,1498],{"class":183},[150,1574,1501],{"class":190},[150,1576,1476],{"class":183},[150,1578,1580,1582,1584,1586,1589,1591,1594,1596,1598],{"class":152,"line":1579},47,[150,1581,1557],{"class":183},[150,1583,1485],{"class":190},[150,1585,335],{"class":183},[150,1587,1588],{"class":160},"\"h3\"",[150,1590,1357],{"class":183},[150,1592,1593],{"class":190},"{projects.length}",[150,1595,1498],{"class":183},[150,1597,1501],{"class":190},[150,1599,1476],{"class":183},[150,1601,1603,1606,1608,1610,1612,1614],{"class":152,"line":1602},48,[150,1604,1605],{"class":183},"        \u003C/",[150,1607,1549],{"class":190},[150,1609,1357],{"class":183},[150,1611,1498],{"class":190},[150,1613,1543],{"class":156},[150,1615,1476],{"class":190},[150,1617,1619,1621,1623,1625,1627],{"class":152,"line":1618},49,[150,1620,1540],{"class":190},[150,1622,1543],{"class":156},[150,1624,1546],{"class":190},[150,1626,1549],{"class":156},[150,1628,1476],{"class":190},[150,1630,1632,1634,1636,1638,1640,1642,1645,1647,1649],{"class":152,"line":1631},50,[150,1633,1557],{"class":183},[150,1635,1560],{"class":190},[150,1637,335],{"class":183},[150,1639,1565],{"class":160},[150,1641,1357],{"class":183},[150,1643,1644],{"class":190},"Completion Rate",[150,1646,1498],{"class":183},[150,1648,1501],{"class":190},[150,1650,1476],{"class":183},[150,1652,1654,1656,1658,1660,1662,1664,1667,1670,1672],{"class":152,"line":1653},51,[150,1655,1557],{"class":183},[150,1657,1485],{"class":190},[150,1659,335],{"class":183},[150,1661,1588],{"class":160},[150,1663,1357],{"class":183},[150,1665,1666],{"class":190},"{completionRate}",[150,1668,1669],{"class":183},"%\u003C/",[150,1671,1501],{"class":190},[150,1673,1476],{"class":183},[150,1675,1677,1679,1681,1683,1685,1687],{"class":152,"line":1676},52,[150,1678,1605],{"class":183},[150,1680,1549],{"class":190},[150,1682,1357],{"class":183},[150,1684,1498],{"class":190},[150,1686,1543],{"class":156},[150,1688,1476],{"class":190},[150,1690,1692,1695,1697],{"class":152,"line":1691},53,[150,1693,1694],{"class":183},"      \u003C/",[150,1696,1473],{"class":190},[150,1698,1476],{"class":183},[150,1700,1702],{"class":152,"line":1701},54,[150,1703,222],{"emptyLinePlaceholder":221},[150,1705,1707,1709,1712],{"class":152,"line":1706},55,[150,1708,1514],{"class":190},[150,1710,1711],{"class":176},"/* Only this part needs to be interactive */",[150,1713,1077],{"class":190},[150,1715,1717,1719,1722,1724,1727,1729,1732,1734,1737],{"class":152,"line":1716},56,[150,1718,1482],{"class":183},[150,1720,1721],{"class":190},"Select value",[150,1723,335],{"class":183},[150,1725,1726],{"class":190},"{statusFilter} onChange",[150,1728,335],{"class":183},[150,1730,1731],{"class":190},"{(e) => ",[150,1733,1116],{"class":156},[150,1735,1736],{"class":190},"(e.target.value)}",[150,1738,1476],{"class":183},[150,1740,1742,1744,1747,1749,1752,1754,1757,1759,1762],{"class":152,"line":1741},57,[150,1743,1540],{"class":183},[150,1745,1746],{"class":190},"MenuItem value",[150,1748,335],{"class":183},[150,1750,1751],{"class":160},"\"all\"",[150,1753,1357],{"class":183},[150,1755,1756],{"class":190},"All",[150,1758,1498],{"class":183},[150,1760,1761],{"class":190},"MenuItem",[150,1763,1476],{"class":183},[150,1765,1767,1769,1771,1773,1776,1778,1781,1783,1785],{"class":152,"line":1766},58,[150,1768,1540],{"class":183},[150,1770,1746],{"class":190},[150,1772,335],{"class":183},[150,1774,1775],{"class":160},"\"active\"",[150,1777,1357],{"class":183},[150,1779,1780],{"class":190},"Active",[150,1782,1498],{"class":183},[150,1784,1761],{"class":190},[150,1786,1476],{"class":183},[150,1788,1790,1792,1794,1796,1799,1801,1804,1806,1808],{"class":152,"line":1789},59,[150,1791,1540],{"class":183},[150,1793,1746],{"class":190},[150,1795,335],{"class":183},[150,1797,1798],{"class":160},"\"completed\"",[150,1800,1357],{"class":183},[150,1802,1803],{"class":190},"Completed",[150,1805,1498],{"class":183},[150,1807,1761],{"class":190},[150,1809,1476],{"class":183},[150,1811,1813,1815,1818],{"class":152,"line":1812},60,[150,1814,1694],{"class":183},[150,1816,1817],{"class":190},"Select",[150,1819,1476],{"class":183},[150,1821,1823],{"class":152,"line":1822},61,[150,1824,222],{"emptyLinePlaceholder":221},[150,1826,1828,1830,1833,1836,1839,1841,1844,1846],{"class":152,"line":1827},62,[150,1829,1514],{"class":190},[150,1831,1832],{"class":991},"filtered",[150,1834,1835],{"class":190},".",[150,1837,1838],{"class":991},"map",[150,1840,1127],{"class":190},[150,1842,1843],{"class":991},"project",[150,1845,1213],{"class":183},[150,1847,1464],{"class":190},[150,1849,1851,1853,1856,1858,1861],{"class":152,"line":1850},63,[150,1852,1540],{"class":183},[150,1854,1855],{"class":190},"Card key",[150,1857,335],{"class":183},[150,1859,1860],{"class":190},"{project.id}",[150,1862,1476],{"class":183},[150,1864,1866,1868,1870],{"class":152,"line":1865},64,[150,1867,1557],{"class":190},[150,1869,1549],{"class":156},[150,1871,1476],{"class":190},[150,1873,1875,1878,1880,1883,1885,1887],{"class":152,"line":1874},65,[150,1876,1877],{"class":190},"            \u003C",[150,1879,1501],{"class":156},[150,1881,1882],{"class":190},">{project.name}",[150,1884,1498],{"class":183},[150,1886,1501],{"class":190},[150,1888,1476],{"class":183},[150,1890,1892,1894,1896,1898,1900],{"class":152,"line":1891},66,[150,1893,1877],{"class":183},[150,1895,1560],{"class":190},[150,1897,335],{"class":183},[150,1899,1565],{"class":160},[150,1901,1476],{"class":183},[150,1903,1905,1908,1911,1913,1916,1919,1921,1923,1925,1928,1931,1934,1937,1939,1942],{"class":152,"line":1904},67,[150,1906,1907],{"class":190},"              Updated {",[150,1909,1910],{"class":991},"format",[150,1912,1127],{"class":190},[150,1914,1915],{"class":991},"new",[150,1917,1918],{"class":991}," Date",[150,1920,1127],{"class":190},[150,1922,1843],{"class":991},[150,1924,1835],{"class":190},[150,1926,1927],{"class":991},"updatedAt",[150,1929,1930],{"class":190},"), '",[150,1932,1933],{"class":991},"MMM",[150,1935,1936],{"class":991}," d",[150,1938,1113],{"class":190},[150,1940,1941],{"class":991},"yyyy",[150,1943,1944],{"class":190},"')}\n",[150,1946,1948,1951,1953],{"class":152,"line":1947},68,[150,1949,1950],{"class":183},"            \u003C/",[150,1952,1501],{"class":190},[150,1954,1476],{"class":183},[150,1956,1958,1961,1963],{"class":152,"line":1957},69,[150,1959,1960],{"class":183},"          \u003C/",[150,1962,1549],{"class":190},[150,1964,1476],{"class":183},[150,1966,1968,1970,1972],{"class":152,"line":1967},70,[150,1969,1605],{"class":183},[150,1971,1543],{"class":190},[150,1973,1476],{"class":183},[150,1975,1977],{"class":152,"line":1976},71,[150,1978,1979],{"class":190},"      ))}\n",[150,1981,1983,1986,1988],{"class":152,"line":1982},72,[150,1984,1985],{"class":183},"    \u003C/",[150,1987,1473],{"class":190},[150,1989,1476],{"class":183},[150,1991,1993],{"class":152,"line":1992},73,[150,1994,1995],{"class":190},"  );\n",[150,1997,1999],{"class":152,"line":1998},74,[150,2000,1077],{"class":190},[138,2002,512],{"id":2003},"the-fix-1",[10,2005,2006],{},"Split into a Server Component that handles data and a small Client Component for the interactive filter:",[19,2008,2010],{"className":167,"code":2009,"language":169,"meta":27,"style":27},"// app/dashboard/page.tsx (Server Component — no 'use client')\nimport { format } from 'date-fns';\nimport Card from '@mui/material/Card';\nimport CardContent from '@mui/material/CardContent';\nimport Typography from '@mui/material/Typography';\nimport { ProjectFilter } from './project-filter';\n\ninterface Project {\n  id: string;\n  name: string;\n  status: 'active' | 'completed' | 'paused';\n  updatedAt: string;\n  tasks: number;\n  completedTasks: number;\n}\n\nasync function getProjects(): Promise\u003CProject[]> {\n  const res = await fetch('https://api.taskflow.dev/projects', {\n    next: { revalidate: 60 },\n  });\n  return res.json();\n}\n\nexport default async function DashboardPage() {\n  const projects = await getProjects();\n\n  const totalTasks = projects.reduce((sum, p) => sum + p.tasks, 0);\n  const completedTasks = projects.reduce((sum, p) => sum + p.completedTasks, 0);\n  const completionRate = totalTasks > 0\n    ? Math.round((completedTasks / totalTasks) * 100) : 0;\n\n  return (\n    \u003Cdiv>\n      \u003CTypography variant=\"h4\">Dashboard\u003C/Typography>\n\n      \u003Cdiv className=\"summary-grid\">\n        \u003CCard>\u003CCardContent>\n          \u003CTypography color=\"textSecondary\">Total Projects\u003C/Typography>\n          \u003CTypography variant=\"h3\">{projects.length}\u003C/Typography>\n        \u003C/CardContent>\u003C/Card>\n        \u003CCard>\u003CCardContent>\n          \u003CTypography color=\"textSecondary\">Completion Rate\u003C/Typography>\n          \u003CTypography variant=\"h3\">{completionRate}%\u003C/Typography>\n        \u003C/CardContent>\u003C/Card>\n      \u003C/div>\n\n      {/* Only the filter is a Client Component */}\n      \u003CProjectFilter projects={projects} />\n    \u003C/div>\n  );\n}\n",[14,2011,2012,2017,2029,2041,2053,2065,2079,2083,2091,2101,2111,2129,2139,2149,2159,2163,2167,2192,2215,2226,2231,2243,2247,2251,2266,2281,2285,2319,2353,2368,2394,2398,2404,2412,2432,2436,2448,2460,2480,2500,2514,2526,2546,2566,2580,2588,2592,2601,2616,2624,2628],{"__ignoreMap":27},[150,2013,2014],{"class":152,"line":153},[150,2015,2016],{"class":176},"// app/dashboard/page.tsx (Server Component — no 'use client')\n",[150,2018,2019,2021,2023,2025,2027],{"class":152,"line":180},[150,2020,184],{"class":183},[150,2022,897],{"class":190},[150,2024,194],{"class":183},[150,2026,902],{"class":160},[150,2028,200],{"class":190},[150,2030,2031,2033,2035,2037,2039],{"class":152,"line":203},[150,2032,184],{"class":183},[150,2034,557],{"class":190},[150,2036,194],{"class":183},[150,2038,562],{"class":160},[150,2040,200],{"class":190},[150,2042,2043,2045,2047,2049,2051],{"class":152,"line":218},[150,2044,184],{"class":183},[150,2046,923],{"class":190},[150,2048,194],{"class":183},[150,2050,928],{"class":160},[150,2052,200],{"class":190},[150,2054,2055,2057,2059,2061,2063],{"class":152,"line":225},[150,2056,184],{"class":183},[150,2058,937],{"class":190},[150,2060,194],{"class":183},[150,2062,942],{"class":160},[150,2064,200],{"class":190},[150,2066,2067,2069,2072,2074,2077],{"class":152,"line":244},[150,2068,184],{"class":183},[150,2070,2071],{"class":190}," { ProjectFilter } ",[150,2073,194],{"class":183},[150,2075,2076],{"class":160}," './project-filter'",[150,2078,200],{"class":190},[150,2080,2081],{"class":152,"line":262},[150,2082,222],{"emptyLinePlaceholder":221},[150,2084,2085,2087,2089],{"class":152,"line":268},[150,2086,981],{"class":183},[150,2088,984],{"class":156},[150,2090,289],{"class":190},[150,2092,2093,2095,2097,2099],{"class":152,"line":273},[150,2094,992],{"class":991},[150,2096,281],{"class":183},[150,2098,997],{"class":231},[150,2100,200],{"class":190},[150,2102,2103,2105,2107,2109],{"class":152,"line":292},[150,2104,1004],{"class":991},[150,2106,281],{"class":183},[150,2108,997],{"class":231},[150,2110,200],{"class":190},[150,2112,2113,2115,2117,2119,2121,2123,2125,2127],{"class":152,"line":298},[150,2114,1016],{"class":991},[150,2116,281],{"class":183},[150,2118,1021],{"class":160},[150,2120,1024],{"class":183},[150,2122,1027],{"class":160},[150,2124,1024],{"class":183},[150,2126,1032],{"class":160},[150,2128,200],{"class":190},[150,2130,2131,2133,2135,2137],{"class":152,"line":304},[150,2132,1040],{"class":991},[150,2134,281],{"class":183},[150,2136,997],{"class":231},[150,2138,200],{"class":190},[150,2140,2141,2143,2145,2147],{"class":152,"line":309},[150,2142,1052],{"class":991},[150,2144,281],{"class":183},[150,2146,1057],{"class":231},[150,2148,200],{"class":190},[150,2150,2151,2153,2155,2157],{"class":152,"line":655},[150,2152,1065],{"class":991},[150,2154,281],{"class":183},[150,2156,1057],{"class":231},[150,2158,200],{"class":190},[150,2160,2161],{"class":152,"line":1013},[150,2162,1077],{"class":190},[150,2164,2165],{"class":152,"line":1037},[150,2166,222],{"emptyLinePlaceholder":221},[150,2168,2169,2172,2174,2177,2180,2182,2185,2187,2189],{"class":152,"line":1049},[150,2170,2171],{"class":183},"async",[150,2173,1092],{"class":183},[150,2175,2176],{"class":156}," getProjects",[150,2178,2179],{"class":190},"()",[150,2181,281],{"class":183},[150,2183,2184],{"class":156}," Promise",[150,2186,1157],{"class":190},[150,2188,1160],{"class":156},[150,2190,2191],{"class":190},"[]> {\n",[150,2193,2194,2196,2199,2201,2204,2207,2209,2212],{"class":152,"line":1062},[150,2195,1104],{"class":183},[150,2197,2198],{"class":231}," res",[150,2200,235],{"class":183},[150,2202,2203],{"class":183}," await",[150,2205,2206],{"class":156}," fetch",[150,2208,1127],{"class":190},[150,2210,2211],{"class":160},"'https://api.taskflow.dev/projects'",[150,2213,2214],{"class":190},", {\n",[150,2216,2217,2220,2223],{"class":152,"line":1074},[150,2218,2219],{"class":190},"    next: { revalidate: ",[150,2221,2222],{"class":231},"60",[150,2224,2225],{"class":190}," },\n",[150,2227,2228],{"class":152,"line":1080},[150,2229,2230],{"class":190},"  });\n",[150,2232,2233,2235,2238,2240],{"class":152,"line":1085},[150,2234,1461],{"class":183},[150,2236,2237],{"class":190}," res.",[150,2239,1219],{"class":156},[150,2241,2242],{"class":190},"();\n",[150,2244,2245],{"class":152,"line":1101},[150,2246,1077],{"class":190},[150,2248,2249],{"class":152,"line":1136},[150,2250,222],{"emptyLinePlaceholder":221},[150,2252,2253,2255,2257,2260,2262,2264],{"class":152,"line":1166},[150,2254,312],{"class":183},[150,2256,315],{"class":183},[150,2258,2259],{"class":183}," async",[150,2261,1092],{"class":183},[150,2263,1095],{"class":156},[150,2265,1098],{"class":190},[150,2267,2268,2270,2273,2275,2277,2279],{"class":152,"line":1171},[150,2269,1104],{"class":183},[150,2271,2272],{"class":231}," projects",[150,2274,235],{"class":183},[150,2276,2203],{"class":183},[150,2278,2176],{"class":156},[150,2280,2242],{"class":190},[150,2282,2283],{"class":152,"line":1177},[150,2284,222],{"emptyLinePlaceholder":221},[150,2286,2287,2289,2291,2293,2295,2297,2299,2301,2303,2305,2307,2309,2311,2313,2315,2317],{"class":152,"line":1191},[150,2288,1104],{"class":183},[150,2290,1267],{"class":231},[150,2292,235],{"class":183},[150,2294,1272],{"class":190},[150,2296,1275],{"class":156},[150,2298,1278],{"class":190},[150,2300,1281],{"class":991},[150,2302,1113],{"class":190},[150,2304,10],{"class":991},[150,2306,1288],{"class":190},[150,2308,1186],{"class":183},[150,2310,1293],{"class":190},[150,2312,1296],{"class":183},[150,2314,1299],{"class":190},[150,2316,1302],{"class":231},[150,2318,1133],{"class":190},[150,2320,2321,2323,2325,2327,2329,2331,2333,2335,2337,2339,2341,2343,2345,2347,2349,2351],{"class":152,"line":1230},[150,2322,1104],{"class":183},[150,2324,1312],{"class":231},[150,2326,235],{"class":183},[150,2328,1272],{"class":190},[150,2330,1275],{"class":156},[150,2332,1278],{"class":190},[150,2334,1281],{"class":991},[150,2336,1113],{"class":190},[150,2338,10],{"class":991},[150,2340,1288],{"class":190},[150,2342,1186],{"class":183},[150,2344,1293],{"class":190},[150,2346,1296],{"class":183},[150,2348,1337],{"class":190},[150,2350,1302],{"class":231},[150,2352,1133],{"class":190},[150,2354,2355,2357,2359,2361,2363,2365],{"class":152,"line":1236},[150,2356,1104],{"class":183},[150,2358,1349],{"class":231},[150,2360,235],{"class":183},[150,2362,1354],{"class":190},[150,2364,1357],{"class":183},[150,2366,2367],{"class":231}," 0\n",[150,2369,2370,2372,2374,2376,2378,2380,2382,2384,2386,2388,2390,2392],{"class":152,"line":1241},[150,2371,1422],{"class":183},[150,2373,1366],{"class":190},[150,2375,1369],{"class":156},[150,2377,1372],{"class":190},[150,2379,1375],{"class":183},[150,2381,1378],{"class":190},[150,2383,1381],{"class":183},[150,2385,1384],{"class":231},[150,2387,1288],{"class":190},[150,2389,281],{"class":183},[150,2391,1360],{"class":231},[150,2393,200],{"class":190},[150,2395,2396],{"class":152,"line":1262},[150,2397,222],{"emptyLinePlaceholder":221},[150,2399,2400,2402],{"class":152,"line":1307},[150,2401,1461],{"class":183},[150,2403,1464],{"class":190},[150,2405,2406,2408,2410],{"class":152,"line":1344},[150,2407,1470],{"class":190},[150,2409,1473],{"class":156},[150,2411,1476],{"class":190},[150,2413,2414,2416,2418,2420,2422,2424,2426,2428,2430],{"class":152,"line":1395},[150,2415,1482],{"class":183},[150,2417,1485],{"class":190},[150,2419,335],{"class":183},[150,2421,1490],{"class":160},[150,2423,1357],{"class":183},[150,2425,1495],{"class":190},[150,2427,1498],{"class":183},[150,2429,1501],{"class":190},[150,2431,1476],{"class":183},[150,2433,2434],{"class":152,"line":1400},[150,2435,222],{"emptyLinePlaceholder":221},[150,2437,2438,2440,2442,2444,2446],{"class":152,"line":1419},[150,2439,1482],{"class":183},[150,2441,1527],{"class":190},[150,2443,335],{"class":183},[150,2445,1532],{"class":160},[150,2447,1476],{"class":183},[150,2449,2450,2452,2454,2456,2458],{"class":152,"line":1428},[150,2451,1540],{"class":190},[150,2453,1543],{"class":156},[150,2455,1546],{"class":190},[150,2457,1549],{"class":156},[150,2459,1476],{"class":190},[150,2461,2462,2464,2466,2468,2470,2472,2474,2476,2478],{"class":152,"line":1453},[150,2463,1557],{"class":183},[150,2465,1560],{"class":190},[150,2467,335],{"class":183},[150,2469,1565],{"class":160},[150,2471,1357],{"class":183},[150,2473,1570],{"class":190},[150,2475,1498],{"class":183},[150,2477,1501],{"class":190},[150,2479,1476],{"class":183},[150,2481,2482,2484,2486,2488,2490,2492,2494,2496,2498],{"class":152,"line":1458},[150,2483,1557],{"class":183},[150,2485,1485],{"class":190},[150,2487,335],{"class":183},[150,2489,1588],{"class":160},[150,2491,1357],{"class":183},[150,2493,1593],{"class":190},[150,2495,1498],{"class":183},[150,2497,1501],{"class":190},[150,2499,1476],{"class":183},[150,2501,2502,2504,2506,2508,2510,2512],{"class":152,"line":1467},[150,2503,1605],{"class":183},[150,2505,1549],{"class":190},[150,2507,1357],{"class":183},[150,2509,1498],{"class":190},[150,2511,1543],{"class":156},[150,2513,1476],{"class":190},[150,2515,2516,2518,2520,2522,2524],{"class":152,"line":1479},[150,2517,1540],{"class":190},[150,2519,1543],{"class":156},[150,2521,1546],{"class":190},[150,2523,1549],{"class":156},[150,2525,1476],{"class":190},[150,2527,2528,2530,2532,2534,2536,2538,2540,2542,2544],{"class":152,"line":1506},[150,2529,1557],{"class":183},[150,2531,1560],{"class":190},[150,2533,335],{"class":183},[150,2535,1565],{"class":160},[150,2537,1357],{"class":183},[150,2539,1644],{"class":190},[150,2541,1498],{"class":183},[150,2543,1501],{"class":190},[150,2545,1476],{"class":183},[150,2547,2548,2550,2552,2554,2556,2558,2560,2562,2564],{"class":152,"line":1511},[150,2549,1557],{"class":183},[150,2551,1485],{"class":190},[150,2553,335],{"class":183},[150,2555,1588],{"class":160},[150,2557,1357],{"class":183},[150,2559,1666],{"class":190},[150,2561,1669],{"class":183},[150,2563,1501],{"class":190},[150,2565,1476],{"class":183},[150,2567,2568,2570,2572,2574,2576,2578],{"class":152,"line":1522},[150,2569,1605],{"class":183},[150,2571,1549],{"class":190},[150,2573,1357],{"class":183},[150,2575,1498],{"class":190},[150,2577,1543],{"class":156},[150,2579,1476],{"class":190},[150,2581,2582,2584,2586],{"class":152,"line":1537},[150,2583,1694],{"class":183},[150,2585,1473],{"class":190},[150,2587,1476],{"class":183},[150,2589,2590],{"class":152,"line":1554},[150,2591,222],{"emptyLinePlaceholder":221},[150,2593,2594,2596,2599],{"class":152,"line":1579},[150,2595,1514],{"class":190},[150,2597,2598],{"class":176},"/* Only the filter is a Client Component */",[150,2600,1077],{"class":190},[150,2602,2603,2605,2608,2610,2613],{"class":152,"line":1602},[150,2604,1482],{"class":183},[150,2606,2607],{"class":190},"ProjectFilter projects",[150,2609,335],{"class":183},[150,2611,2612],{"class":190},"{projects} ",[150,2614,2615],{"class":183},"/>\n",[150,2617,2618,2620,2622],{"class":152,"line":1618},[150,2619,1985],{"class":183},[150,2621,1473],{"class":190},[150,2623,1476],{"class":183},[150,2625,2626],{"class":152,"line":1631},[150,2627,1995],{"class":190},[150,2629,2630],{"class":152,"line":1653},[150,2631,1077],{"class":190},[19,2633,2635],{"className":167,"code":2634,"language":169,"meta":27,"style":27},"// app/dashboard/project-filter.tsx\n'use client';\n\nimport { useState } from 'react';\nimport { format } from 'date-fns';\nimport Select from '@mui/material/Select';\nimport MenuItem from '@mui/material/MenuItem';\nimport Card from '@mui/material/Card';\nimport CardContent from '@mui/material/CardContent';\nimport Typography from '@mui/material/Typography';\n\ninterface Project {\n  id: string;\n  name: string;\n  status: 'active' | 'completed' | 'paused';\n  updatedAt: string;\n}\n\nexport function ProjectFilter({ projects }: { projects: Project[] }) {\n  const [statusFilter, setStatusFilter] = useState('all');\n\n  const filtered = statusFilter === 'all'\n    ? projects\n    : projects.filter(p => p.status === statusFilter);\n\n  return (\n    \u003C>\n      \u003CSelect value={statusFilter} onChange={(e) => setStatusFilter(e.target.value)}>\n        \u003CMenuItem value=\"all\">All\u003C/MenuItem>\n        \u003CMenuItem value=\"active\">Active\u003C/MenuItem>\n        \u003CMenuItem value=\"completed\">Completed\u003C/MenuItem>\n      \u003C/Select>\n\n      {filtered.map(project => (\n        \u003CCard key={project.id}>\n          \u003CCardContent>\n            \u003CTypography>{project.name}\u003C/Typography>\n            \u003CTypography color=\"textSecondary\">\n              Updated {format(new Date(project.updatedAt), 'MMM d, yyyy')}\n            \u003C/Typography>\n          \u003C/CardContent>\n        \u003C/Card>\n      ))}\n    \u003C/>\n  );\n}\n",[14,2636,2637,2642,2648,2652,2664,2676,2688,2700,2712,2724,2736,2740,2748,2758,2768,2786,2796,2800,2804,2835,2859,2863,2877,2883,2903,2907,2913,2918,2938,2958,2978,2998,3006,3010,3028,3040,3048,3062,3074,3106,3114,3122,3130,3134,3139,3143],{"__ignoreMap":27},[150,2638,2639],{"class":152,"line":153},[150,2640,2641],{"class":176},"// app/dashboard/project-filter.tsx\n",[150,2643,2644,2646],{"class":152,"line":180},[150,2645,844],{"class":160},[150,2647,200],{"class":190},[150,2649,2650],{"class":152,"line":203},[150,2651,222],{"emptyLinePlaceholder":221},[150,2653,2654,2656,2658,2660,2662],{"class":152,"line":218},[150,2655,184],{"class":183},[150,2657,870],{"class":190},[150,2659,194],{"class":183},[150,2661,875],{"class":160},[150,2663,200],{"class":190},[150,2665,2666,2668,2670,2672,2674],{"class":152,"line":225},[150,2667,184],{"class":183},[150,2669,897],{"class":190},[150,2671,194],{"class":183},[150,2673,902],{"class":160},[150,2675,200],{"class":190},[150,2677,2678,2680,2682,2684,2686],{"class":152,"line":244},[150,2679,184],{"class":183},[150,2681,951],{"class":190},[150,2683,194],{"class":183},[150,2685,956],{"class":160},[150,2687,200],{"class":190},[150,2689,2690,2692,2694,2696,2698],{"class":152,"line":262},[150,2691,184],{"class":183},[150,2693,965],{"class":190},[150,2695,194],{"class":183},[150,2697,970],{"class":160},[150,2699,200],{"class":190},[150,2701,2702,2704,2706,2708,2710],{"class":152,"line":268},[150,2703,184],{"class":183},[150,2705,557],{"class":190},[150,2707,194],{"class":183},[150,2709,562],{"class":160},[150,2711,200],{"class":190},[150,2713,2714,2716,2718,2720,2722],{"class":152,"line":273},[150,2715,184],{"class":183},[150,2717,923],{"class":190},[150,2719,194],{"class":183},[150,2721,928],{"class":160},[150,2723,200],{"class":190},[150,2725,2726,2728,2730,2732,2734],{"class":152,"line":292},[150,2727,184],{"class":183},[150,2729,937],{"class":190},[150,2731,194],{"class":183},[150,2733,942],{"class":160},[150,2735,200],{"class":190},[150,2737,2738],{"class":152,"line":298},[150,2739,222],{"emptyLinePlaceholder":221},[150,2741,2742,2744,2746],{"class":152,"line":304},[150,2743,981],{"class":183},[150,2745,984],{"class":156},[150,2747,289],{"class":190},[150,2749,2750,2752,2754,2756],{"class":152,"line":309},[150,2751,992],{"class":991},[150,2753,281],{"class":183},[150,2755,997],{"class":231},[150,2757,200],{"class":190},[150,2759,2760,2762,2764,2766],{"class":152,"line":655},[150,2761,1004],{"class":991},[150,2763,281],{"class":183},[150,2765,997],{"class":231},[150,2767,200],{"class":190},[150,2769,2770,2772,2774,2776,2778,2780,2782,2784],{"class":152,"line":1013},[150,2771,1016],{"class":991},[150,2773,281],{"class":183},[150,2775,1021],{"class":160},[150,2777,1024],{"class":183},[150,2779,1027],{"class":160},[150,2781,1024],{"class":183},[150,2783,1032],{"class":160},[150,2785,200],{"class":190},[150,2787,2788,2790,2792,2794],{"class":152,"line":1037},[150,2789,1040],{"class":991},[150,2791,281],{"class":183},[150,2793,997],{"class":231},[150,2795,200],{"class":190},[150,2797,2798],{"class":152,"line":1049},[150,2799,1077],{"class":190},[150,2801,2802],{"class":152,"line":1062},[150,2803,222],{"emptyLinePlaceholder":221},[150,2805,2806,2808,2810,2813,2816,2818,2821,2823,2826,2828,2830,2832],{"class":152,"line":1074},[150,2807,312],{"class":183},[150,2809,1092],{"class":183},[150,2811,2812],{"class":156}," ProjectFilter",[150,2814,2815],{"class":190},"({ ",[150,2817,1143],{"class":991},[150,2819,2820],{"class":190}," }",[150,2822,281],{"class":183},[150,2824,2825],{"class":190}," { ",[150,2827,1143],{"class":991},[150,2829,281],{"class":183},[150,2831,984],{"class":156},[150,2833,2834],{"class":190},"[] }) {\n",[150,2836,2837,2839,2841,2843,2845,2847,2849,2851,2853,2855,2857],{"class":152,"line":1080},[150,2838,1104],{"class":183},[150,2840,1107],{"class":190},[150,2842,1110],{"class":231},[150,2844,1113],{"class":190},[150,2846,1116],{"class":231},[150,2848,1119],{"class":190},[150,2850,335],{"class":183},[150,2852,1124],{"class":156},[150,2854,1127],{"class":190},[150,2856,1130],{"class":160},[150,2858,1133],{"class":190},[150,2860,2861],{"class":152,"line":1085},[150,2862,222],{"emptyLinePlaceholder":221},[150,2864,2865,2867,2869,2871,2873,2875],{"class":152,"line":1101},[150,2866,1104],{"class":183},[150,2868,1405],{"class":231},[150,2870,235],{"class":183},[150,2872,1410],{"class":190},[150,2874,1413],{"class":183},[150,2876,1416],{"class":160},[150,2878,2879,2881],{"class":152,"line":1136},[150,2880,1422],{"class":183},[150,2882,1425],{"class":190},[150,2884,2885,2887,2889,2891,2893,2895,2897,2899,2901],{"class":152,"line":1166},[150,2886,1431],{"class":183},[150,2888,1272],{"class":190},[150,2890,1436],{"class":156},[150,2892,1127],{"class":190},[150,2894,10],{"class":991},[150,2896,1213],{"class":183},[150,2898,1445],{"class":190},[150,2900,1413],{"class":183},[150,2902,1450],{"class":190},[150,2904,2905],{"class":152,"line":1171},[150,2906,222],{"emptyLinePlaceholder":221},[150,2908,2909,2911],{"class":152,"line":1177},[150,2910,1461],{"class":183},[150,2912,1464],{"class":190},[150,2914,2915],{"class":152,"line":1191},[150,2916,2917],{"class":183},"    \u003C>\n",[150,2919,2920,2922,2924,2926,2928,2930,2932,2934,2936],{"class":152,"line":1230},[150,2921,1482],{"class":183},[150,2923,1721],{"class":190},[150,2925,335],{"class":183},[150,2927,1726],{"class":190},[150,2929,335],{"class":183},[150,2931,1731],{"class":190},[150,2933,1116],{"class":156},[150,2935,1736],{"class":190},[150,2937,1476],{"class":183},[150,2939,2940,2942,2944,2946,2948,2950,2952,2954,2956],{"class":152,"line":1236},[150,2941,1540],{"class":183},[150,2943,1746],{"class":190},[150,2945,335],{"class":183},[150,2947,1751],{"class":160},[150,2949,1357],{"class":183},[150,2951,1756],{"class":190},[150,2953,1498],{"class":183},[150,2955,1761],{"class":190},[150,2957,1476],{"class":183},[150,2959,2960,2962,2964,2966,2968,2970,2972,2974,2976],{"class":152,"line":1241},[150,2961,1540],{"class":183},[150,2963,1746],{"class":190},[150,2965,335],{"class":183},[150,2967,1775],{"class":160},[150,2969,1357],{"class":183},[150,2971,1780],{"class":190},[150,2973,1498],{"class":183},[150,2975,1761],{"class":190},[150,2977,1476],{"class":183},[150,2979,2980,2982,2984,2986,2988,2990,2992,2994,2996],{"class":152,"line":1262},[150,2981,1540],{"class":183},[150,2983,1746],{"class":190},[150,2985,335],{"class":183},[150,2987,1798],{"class":160},[150,2989,1357],{"class":183},[150,2991,1803],{"class":190},[150,2993,1498],{"class":183},[150,2995,1761],{"class":190},[150,2997,1476],{"class":183},[150,2999,3000,3002,3004],{"class":152,"line":1307},[150,3001,1694],{"class":183},[150,3003,1817],{"class":190},[150,3005,1476],{"class":183},[150,3007,3008],{"class":152,"line":1344},[150,3009,222],{"emptyLinePlaceholder":221},[150,3011,3012,3014,3016,3018,3020,3022,3024,3026],{"class":152,"line":1395},[150,3013,1514],{"class":190},[150,3015,1832],{"class":991},[150,3017,1835],{"class":190},[150,3019,1838],{"class":991},[150,3021,1127],{"class":190},[150,3023,1843],{"class":991},[150,3025,1213],{"class":183},[150,3027,1464],{"class":190},[150,3029,3030,3032,3034,3036,3038],{"class":152,"line":1400},[150,3031,1540],{"class":183},[150,3033,1855],{"class":190},[150,3035,335],{"class":183},[150,3037,1860],{"class":190},[150,3039,1476],{"class":183},[150,3041,3042,3044,3046],{"class":152,"line":1419},[150,3043,1557],{"class":190},[150,3045,1549],{"class":156},[150,3047,1476],{"class":190},[150,3049,3050,3052,3054,3056,3058,3060],{"class":152,"line":1428},[150,3051,1877],{"class":190},[150,3053,1501],{"class":156},[150,3055,1882],{"class":190},[150,3057,1498],{"class":183},[150,3059,1501],{"class":190},[150,3061,1476],{"class":183},[150,3063,3064,3066,3068,3070,3072],{"class":152,"line":1453},[150,3065,1877],{"class":183},[150,3067,1560],{"class":190},[150,3069,335],{"class":183},[150,3071,1565],{"class":160},[150,3073,1476],{"class":183},[150,3075,3076,3078,3080,3082,3084,3086,3088,3090,3092,3094,3096,3098,3100,3102,3104],{"class":152,"line":1458},[150,3077,1907],{"class":190},[150,3079,1910],{"class":991},[150,3081,1127],{"class":190},[150,3083,1915],{"class":991},[150,3085,1918],{"class":991},[150,3087,1127],{"class":190},[150,3089,1843],{"class":991},[150,3091,1835],{"class":190},[150,3093,1927],{"class":991},[150,3095,1930],{"class":190},[150,3097,1933],{"class":991},[150,3099,1936],{"class":991},[150,3101,1113],{"class":190},[150,3103,1941],{"class":991},[150,3105,1944],{"class":190},[150,3107,3108,3110,3112],{"class":152,"line":1467},[150,3109,1950],{"class":183},[150,3111,1501],{"class":190},[150,3113,1476],{"class":183},[150,3115,3116,3118,3120],{"class":152,"line":1479},[150,3117,1960],{"class":183},[150,3119,1549],{"class":190},[150,3121,1476],{"class":183},[150,3123,3124,3126,3128],{"class":152,"line":1506},[150,3125,1605],{"class":183},[150,3127,1543],{"class":190},[150,3129,1476],{"class":183},[150,3131,3132],{"class":152,"line":1511},[150,3133,1979],{"class":190},[150,3135,3136],{"class":152,"line":1522},[150,3137,3138],{"class":183},"    \u003C/>\n",[150,3140,3141],{"class":152,"line":1537},[150,3142,1995],{"class":190},[150,3144,3145],{"class":152,"line":1554},[150,3146,1077],{"class":190},[10,3148,3149,3150,3154],{},"The key insight: the summary cards, the page title, and the data fetching/transformation logic are now ",[86,3151,3153],{"href":3152},"/blog/react-server-components-core-web-vitals/","Server Components",". They run on the server and ship zero JavaScript to the browser. The only client code is the filter dropdown and list rendering — maybe 2kb of actual interactive logic.",[138,3156,760],{"id":3157},"results-1",[38,3159,3160,3170],{},[41,3161,3162],{},[44,3163,3164,3167],{},[47,3165,3166],{},"What moved to server",[47,3168,3169],{},"Size removed from client",[54,3171,3172,3180,3188,3196,3204,3211],{},[44,3173,3174,3177],{},[59,3175,3176],{},"Data fetching + useEffect",[59,3178,3179],{},"~12kb",[44,3181,3182,3185],{},[59,3183,3184],{},"Data transformation (groupBy, reduce)",[59,3186,3187],{},"~16kb",[44,3189,3190,3193],{},[59,3191,3192],{},"Summary card rendering",[59,3194,3195],{},"~8kb",[44,3197,3198,3201],{},[59,3199,3200],{},"Date formatting utilities",[59,3202,3203],{},"~10kb",[44,3205,3206,3209],{},[59,3207,3208],{},"Type definitions + interfaces",[59,3210,3179],{},[44,3212,3213,3218],{},[59,3214,3215],{},[362,3216,3217],{},"Total",[59,3219,3220],{},[362,3221,3222],{},"58kb",[19,3224,3227],{"className":3225,"code":3226,"language":24},[22],"First Load JS: 348kb → 290kb\n",[14,3228,3226],{"__ignoreMap":27},[10,3230,3231],{},"This is the optimization most developers skip because it requires rethinking component boundaries. But it's the second biggest win after imports, and it makes your app faster in ways bundle size alone doesn't capture — the data fetching now happens on the server, closer to the database, with no client-side waterfall.",[126,3233,3235],{"id":3234},"fix-3-dynamic-imports-for-heavy-components-saved-52kb","Fix 3: Dynamic imports for heavy components (saved 52kb)",[10,3237,3238],{},"TaskFlow's analytics page uses Recharts for data visualization. The charting library is 45kb gzipped — reasonable for a charting library, but unreasonable to load on every page when charts only appear on one tab.",[10,3240,3241],{},"There's also a settings modal with a rich text editor that most users never open.",[138,3243,418],{"id":3244},"the-problem-2",[19,3246,3248],{"className":167,"code":3247,"language":169,"meta":27,"style":27},"// app/dashboard/layout.tsx\n'use client';\n\n// These load on EVERY page, even pages without charts\nimport { BarChart, Bar, XAxis, YAxis, Tooltip, ResponsiveContainer } from 'recharts';\nimport { RichTextEditor } from './rich-text-editor';\n",[14,3249,3250,3255,3261,3265,3270,3284],{"__ignoreMap":27},[150,3251,3252],{"class":152,"line":153},[150,3253,3254],{"class":176},"// app/dashboard/layout.tsx\n",[150,3256,3257,3259],{"class":152,"line":180},[150,3258,844],{"class":160},[150,3260,200],{"class":190},[150,3262,3263],{"class":152,"line":203},[150,3264,222],{"emptyLinePlaceholder":221},[150,3266,3267],{"class":152,"line":218},[150,3268,3269],{"class":176},"// These load on EVERY page, even pages without charts\n",[150,3271,3272,3274,3277,3279,3282],{"class":152,"line":225},[150,3273,184],{"class":183},[150,3275,3276],{"class":190}," { BarChart, Bar, XAxis, YAxis, Tooltip, ResponsiveContainer } ",[150,3278,194],{"class":183},[150,3280,3281],{"class":160}," 'recharts'",[150,3283,200],{"class":190},[150,3285,3286,3288,3291,3293,3296],{"class":152,"line":244},[150,3287,184],{"class":183},[150,3289,3290],{"class":190}," { RichTextEditor } ",[150,3292,194],{"class":183},[150,3294,3295],{"class":160}," './rich-text-editor'",[150,3297,200],{"class":190},[10,3299,3300],{},"Static imports mean these packages are in the shared bundle. Every route pays the cost, even routes that never render a chart.",[138,3302,512],{"id":3303},"the-fix-2",[10,3305,3306,3307,3310],{},"Use ",[14,3308,3309],{},"next/dynamic"," to load heavy components only when they're needed:",[19,3312,3314],{"className":167,"code":3313,"language":169,"meta":27,"style":27},"// app/dashboard/analytics-chart.tsx\n'use client';\n\nimport dynamic from 'next/dynamic';\nimport Skeleton from '@mui/material/Skeleton';\n\n// Only loads when this component renders\nconst BarChart = dynamic(\n  () => import('recharts').then(mod => ({ default: mod.BarChart })),\n  {\n    ssr: false,\n    loading: () => (\n      \u003CSkeleton\n        variant=\"rectangular\"\n        width=\"100%\"\n        height={300}\n        sx={{ borderRadius: 2 }}\n      />\n    ),\n  }\n);\n\nconst Bar = dynamic(() => import('recharts').then(mod => ({ default: mod.Bar })));\nconst XAxis = dynamic(() => import('recharts').then(mod => ({ default: mod.XAxis })));\nconst YAxis = dynamic(() => import('recharts').then(mod => ({ default: mod.YAxis })));\nconst Tooltip = dynamic(() => import('recharts').then(mod => ({ default: mod.Tooltip })));\nconst ResponsiveContainer = dynamic(\n  () => import('recharts').then(mod => ({ default: mod.ResponsiveContainer }))\n);\n\ninterface ChartData {\n  name: string;\n  tasks: number;\n  completed: number;\n}\n\nexport function AnalyticsChart({ data }: { data: ChartData[] }) {\n  return (\n    \u003CResponsiveContainer width=\"100%\" height={300}>\n      \u003CBarChart data={data}>\n        \u003CXAxis dataKey=\"name\" />\n        \u003CYAxis />\n        \u003CTooltip />\n        \u003CBar dataKey=\"completed\" fill=\"#4ade80\" />\n        \u003CBar dataKey=\"tasks\" fill=\"#94a3b8\" />\n      \u003C/BarChart>\n    \u003C/ResponsiveContainer>\n  );\n}\n",[14,3315,3316,3321,3327,3331,3345,3359,3363,3368,3383,3412,3417,3427,3439,3446,3456,3466,3481,3503,3508,3513,3518,3522,3526,3560,3594,3628,3662,3675,3700,3704,3708,3717,3727,3737,3748,3752,3756,3784,3790,3816,3830,3845,3854,3863,3884,3904,3913,3922,3926],{"__ignoreMap":27},[150,3317,3318],{"class":152,"line":153},[150,3319,3320],{"class":176},"// app/dashboard/analytics-chart.tsx\n",[150,3322,3323,3325],{"class":152,"line":180},[150,3324,844],{"class":160},[150,3326,200],{"class":190},[150,3328,3329],{"class":152,"line":203},[150,3330,222],{"emptyLinePlaceholder":221},[150,3332,3333,3335,3338,3340,3343],{"class":152,"line":218},[150,3334,184],{"class":183},[150,3336,3337],{"class":190}," dynamic ",[150,3339,194],{"class":183},[150,3341,3342],{"class":160}," 'next/dynamic'",[150,3344,200],{"class":190},[150,3346,3347,3349,3352,3354,3357],{"class":152,"line":225},[150,3348,184],{"class":183},[150,3350,3351],{"class":190}," Skeleton ",[150,3353,194],{"class":183},[150,3355,3356],{"class":160}," '@mui/material/Skeleton'",[150,3358,200],{"class":190},[150,3360,3361],{"class":152,"line":244},[150,3362,222],{"emptyLinePlaceholder":221},[150,3364,3365],{"class":152,"line":262},[150,3366,3367],{"class":176},"// Only loads when this component renders\n",[150,3369,3370,3372,3375,3377,3380],{"class":152,"line":268},[150,3371,228],{"class":183},[150,3373,3374],{"class":231}," BarChart",[150,3376,235],{"class":183},[150,3378,3379],{"class":156}," dynamic",[150,3381,3382],{"class":190},"(\n",[150,3384,3385,3388,3390,3393,3395,3398,3400,3402,3404,3407,3409],{"class":152,"line":273},[150,3386,3387],{"class":190},"  () ",[150,3389,1186],{"class":183},[150,3391,3392],{"class":183}," import",[150,3394,1127],{"class":190},[150,3396,3397],{"class":160},"'recharts'",[150,3399,1202],{"class":190},[150,3401,1205],{"class":156},[150,3403,1127],{"class":190},[150,3405,3406],{"class":991},"mod",[150,3408,1213],{"class":183},[150,3410,3411],{"class":190}," ({ default: mod.BarChart })),\n",[150,3413,3414],{"class":152,"line":292},[150,3415,3416],{"class":190},"  {\n",[150,3418,3419,3422,3425],{"class":152,"line":298},[150,3420,3421],{"class":190},"    ssr: ",[150,3423,3424],{"class":231},"false",[150,3426,259],{"class":190},[150,3428,3429,3432,3435,3437],{"class":152,"line":304},[150,3430,3431],{"class":156},"    loading",[150,3433,3434],{"class":190},": () ",[150,3436,1186],{"class":183},[150,3438,1464],{"class":190},[150,3440,3441,3443],{"class":152,"line":309},[150,3442,1482],{"class":183},[150,3444,3445],{"class":991},"Skeleton\n",[150,3447,3448,3451,3453],{"class":152,"line":655},[150,3449,3450],{"class":190},"        variant",[150,3452,335],{"class":183},[150,3454,3455],{"class":160},"\"rectangular\"\n",[150,3457,3458,3461,3463],{"class":152,"line":1013},[150,3459,3460],{"class":190},"        width",[150,3462,335],{"class":183},[150,3464,3465],{"class":160},"\"100%\"\n",[150,3467,3468,3471,3473,3476,3479],{"class":152,"line":1037},[150,3469,3470],{"class":190},"        height",[150,3472,335],{"class":183},[150,3474,3475],{"class":190},"{",[150,3477,3478],{"class":231},"300",[150,3480,1077],{"class":190},[150,3482,3483,3486,3488,3491,3494,3497,3500],{"class":152,"line":1049},[150,3484,3485],{"class":190},"        sx",[150,3487,335],{"class":183},[150,3489,3490],{"class":190},"{{ ",[150,3492,3493],{"class":156},"borderRadius",[150,3495,3496],{"class":190},": ",[150,3498,3499],{"class":231},"2",[150,3501,3502],{"class":190}," }}\n",[150,3504,3505],{"class":152,"line":1062},[150,3506,3507],{"class":183},"      />\n",[150,3509,3510],{"class":152,"line":1074},[150,3511,3512],{"class":190},"    ),\n",[150,3514,3515],{"class":152,"line":1080},[150,3516,3517],{"class":190},"  }\n",[150,3519,3520],{"class":152,"line":1085},[150,3521,1133],{"class":190},[150,3523,3524],{"class":152,"line":1101},[150,3525,222],{"emptyLinePlaceholder":221},[150,3527,3528,3530,3533,3535,3537,3539,3541,3543,3545,3547,3549,3551,3553,3555,3557],{"class":152,"line":1136},[150,3529,228],{"class":183},[150,3531,3532],{"class":231}," Bar",[150,3534,235],{"class":183},[150,3536,3379],{"class":156},[150,3538,1183],{"class":190},[150,3540,1186],{"class":183},[150,3542,3392],{"class":183},[150,3544,1127],{"class":190},[150,3546,3397],{"class":160},[150,3548,1202],{"class":190},[150,3550,1205],{"class":156},[150,3552,1127],{"class":190},[150,3554,3406],{"class":991},[150,3556,1213],{"class":183},[150,3558,3559],{"class":190}," ({ default: mod.Bar })));\n",[150,3561,3562,3564,3567,3569,3571,3573,3575,3577,3579,3581,3583,3585,3587,3589,3591],{"class":152,"line":1166},[150,3563,228],{"class":183},[150,3565,3566],{"class":231}," XAxis",[150,3568,235],{"class":183},[150,3570,3379],{"class":156},[150,3572,1183],{"class":190},[150,3574,1186],{"class":183},[150,3576,3392],{"class":183},[150,3578,1127],{"class":190},[150,3580,3397],{"class":160},[150,3582,1202],{"class":190},[150,3584,1205],{"class":156},[150,3586,1127],{"class":190},[150,3588,3406],{"class":991},[150,3590,1213],{"class":183},[150,3592,3593],{"class":190}," ({ default: mod.XAxis })));\n",[150,3595,3596,3598,3601,3603,3605,3607,3609,3611,3613,3615,3617,3619,3621,3623,3625],{"class":152,"line":1171},[150,3597,228],{"class":183},[150,3599,3600],{"class":231}," YAxis",[150,3602,235],{"class":183},[150,3604,3379],{"class":156},[150,3606,1183],{"class":190},[150,3608,1186],{"class":183},[150,3610,3392],{"class":183},[150,3612,1127],{"class":190},[150,3614,3397],{"class":160},[150,3616,1202],{"class":190},[150,3618,1205],{"class":156},[150,3620,1127],{"class":190},[150,3622,3406],{"class":991},[150,3624,1213],{"class":183},[150,3626,3627],{"class":190}," ({ default: mod.YAxis })));\n",[150,3629,3630,3632,3635,3637,3639,3641,3643,3645,3647,3649,3651,3653,3655,3657,3659],{"class":152,"line":1177},[150,3631,228],{"class":183},[150,3633,3634],{"class":231}," Tooltip",[150,3636,235],{"class":183},[150,3638,3379],{"class":156},[150,3640,1183],{"class":190},[150,3642,1186],{"class":183},[150,3644,3392],{"class":183},[150,3646,1127],{"class":190},[150,3648,3397],{"class":160},[150,3650,1202],{"class":190},[150,3652,1205],{"class":156},[150,3654,1127],{"class":190},[150,3656,3406],{"class":991},[150,3658,1213],{"class":183},[150,3660,3661],{"class":190}," ({ default: mod.Tooltip })));\n",[150,3663,3664,3666,3669,3671,3673],{"class":152,"line":1191},[150,3665,228],{"class":183},[150,3667,3668],{"class":231}," ResponsiveContainer",[150,3670,235],{"class":183},[150,3672,3379],{"class":156},[150,3674,3382],{"class":190},[150,3676,3677,3679,3681,3683,3685,3687,3689,3691,3693,3695,3697],{"class":152,"line":1230},[150,3678,3387],{"class":190},[150,3680,1186],{"class":183},[150,3682,3392],{"class":183},[150,3684,1127],{"class":190},[150,3686,3397],{"class":160},[150,3688,1202],{"class":190},[150,3690,1205],{"class":156},[150,3692,1127],{"class":190},[150,3694,3406],{"class":991},[150,3696,1213],{"class":183},[150,3698,3699],{"class":190}," ({ default: mod.ResponsiveContainer }))\n",[150,3701,3702],{"class":152,"line":1236},[150,3703,1133],{"class":190},[150,3705,3706],{"class":152,"line":1241},[150,3707,222],{"emptyLinePlaceholder":221},[150,3709,3710,3712,3715],{"class":152,"line":1262},[150,3711,981],{"class":183},[150,3713,3714],{"class":156}," ChartData",[150,3716,289],{"class":190},[150,3718,3719,3721,3723,3725],{"class":152,"line":1307},[150,3720,1004],{"class":991},[150,3722,281],{"class":183},[150,3724,997],{"class":231},[150,3726,200],{"class":190},[150,3728,3729,3731,3733,3735],{"class":152,"line":1344},[150,3730,1052],{"class":991},[150,3732,281],{"class":183},[150,3734,1057],{"class":231},[150,3736,200],{"class":190},[150,3738,3739,3742,3744,3746],{"class":152,"line":1395},[150,3740,3741],{"class":991},"  completed",[150,3743,281],{"class":183},[150,3745,1057],{"class":231},[150,3747,200],{"class":190},[150,3749,3750],{"class":152,"line":1400},[150,3751,1077],{"class":190},[150,3753,3754],{"class":152,"line":1419},[150,3755,222],{"emptyLinePlaceholder":221},[150,3757,3758,3760,3762,3765,3767,3770,3772,3774,3776,3778,3780,3782],{"class":152,"line":1428},[150,3759,312],{"class":183},[150,3761,1092],{"class":183},[150,3763,3764],{"class":156}," AnalyticsChart",[150,3766,2815],{"class":190},[150,3768,3769],{"class":991},"data",[150,3771,2820],{"class":190},[150,3773,281],{"class":183},[150,3775,2825],{"class":190},[150,3777,3769],{"class":991},[150,3779,281],{"class":183},[150,3781,3714],{"class":156},[150,3783,2834],{"class":190},[150,3785,3786,3788],{"class":152,"line":1453},[150,3787,1461],{"class":183},[150,3789,1464],{"class":190},[150,3791,3792,3794,3797,3799,3802,3805,3807,3809,3811,3814],{"class":152,"line":1458},[150,3793,1470],{"class":183},[150,3795,3796],{"class":190},"ResponsiveContainer width",[150,3798,335],{"class":183},[150,3800,3801],{"class":160},"\"100%\"",[150,3803,3804],{"class":190}," height",[150,3806,335],{"class":183},[150,3808,3475],{"class":190},[150,3810,3478],{"class":231},[150,3812,3813],{"class":190},"}",[150,3815,1476],{"class":183},[150,3817,3818,3820,3823,3825,3828],{"class":152,"line":1467},[150,3819,1482],{"class":183},[150,3821,3822],{"class":190},"BarChart data",[150,3824,335],{"class":183},[150,3826,3827],{"class":190},"{data}",[150,3829,1476],{"class":183},[150,3831,3832,3834,3837,3839,3842],{"class":152,"line":1479},[150,3833,1540],{"class":183},[150,3835,3836],{"class":190},"XAxis dataKey",[150,3838,335],{"class":183},[150,3840,3841],{"class":160},"\"name\"",[150,3843,3844],{"class":183}," />\n",[150,3846,3847,3849,3852],{"class":152,"line":1506},[150,3848,1540],{"class":183},[150,3850,3851],{"class":190},"YAxis ",[150,3853,2615],{"class":183},[150,3855,3856,3858,3861],{"class":152,"line":1511},[150,3857,1540],{"class":183},[150,3859,3860],{"class":190},"Tooltip ",[150,3862,2615],{"class":183},[150,3864,3865,3867,3870,3872,3874,3877,3879,3882],{"class":152,"line":1522},[150,3866,1540],{"class":183},[150,3868,3869],{"class":190},"Bar dataKey",[150,3871,335],{"class":183},[150,3873,1798],{"class":160},[150,3875,3876],{"class":190}," fill",[150,3878,335],{"class":183},[150,3880,3881],{"class":160},"\"#4ade80\"",[150,3883,3844],{"class":183},[150,3885,3886,3888,3890,3892,3895,3897,3899,3902],{"class":152,"line":1537},[150,3887,1540],{"class":183},[150,3889,3869],{"class":190},[150,3891,335],{"class":183},[150,3893,3894],{"class":160},"\"tasks\"",[150,3896,3876],{"class":190},[150,3898,335],{"class":183},[150,3900,3901],{"class":160},"\"#94a3b8\"",[150,3903,3844],{"class":183},[150,3905,3906,3908,3911],{"class":152,"line":1554},[150,3907,1694],{"class":183},[150,3909,3910],{"class":190},"BarChart",[150,3912,1476],{"class":183},[150,3914,3915,3917,3920],{"class":152,"line":1579},[150,3916,1985],{"class":183},[150,3918,3919],{"class":190},"ResponsiveContainer",[150,3921,1476],{"class":183},[150,3923,3924],{"class":152,"line":1602},[150,3925,1995],{"class":190},[150,3927,3928],{"class":152,"line":1618},[150,3929,1077],{"class":190},[10,3931,3932],{},"For the settings modal, load it on user interaction:",[19,3934,3936],{"className":167,"code":3935,"language":169,"meta":27,"style":27},"// app/dashboard/settings-button.tsx\n'use client';\n\nimport { useState } from 'react';\nimport dynamic from 'next/dynamic';\nimport Button from '@mui/material/Button';\nimport Settings from '@mui/icons-material/Settings';\n\n// Only downloads when the user clicks \"Settings\"\nconst SettingsModal = dynamic(() => import('./settings-modal'), {\n  loading: () => null,\n});\n\nexport function SettingsButton() {\n  const [open, setOpen] = useState(false);\n\n  return (\n    \u003C>\n      \u003CButton startIcon={\u003CSettings />} onClick={() => setOpen(true)}>\n        Settings\n      \u003C/Button>\n      {open && \u003CSettingsModal open={open} onClose={() => setOpen(false)} />}\n    \u003C/>\n  );\n}\n",[14,3937,3938,3943,3949,3953,3965,3977,3989,4001,4005,4010,4035,4049,4053,4057,4068,4094,4098,4104,4108,4136,4141,4150,4188,4192,4196],{"__ignoreMap":27},[150,3939,3940],{"class":152,"line":153},[150,3941,3942],{"class":176},"// app/dashboard/settings-button.tsx\n",[150,3944,3945,3947],{"class":152,"line":180},[150,3946,844],{"class":160},[150,3948,200],{"class":190},[150,3950,3951],{"class":152,"line":203},[150,3952,222],{"emptyLinePlaceholder":221},[150,3954,3955,3957,3959,3961,3963],{"class":152,"line":218},[150,3956,184],{"class":183},[150,3958,870],{"class":190},[150,3960,194],{"class":183},[150,3962,875],{"class":160},[150,3964,200],{"class":190},[150,3966,3967,3969,3971,3973,3975],{"class":152,"line":225},[150,3968,184],{"class":183},[150,3970,3337],{"class":190},[150,3972,194],{"class":183},[150,3974,3342],{"class":160},[150,3976,200],{"class":190},[150,3978,3979,3981,3983,3985,3987],{"class":152,"line":244},[150,3980,184],{"class":183},[150,3982,529],{"class":190},[150,3984,194],{"class":183},[150,3986,534],{"class":160},[150,3988,200],{"class":190},[150,3990,3991,3993,3995,3997,3999],{"class":152,"line":262},[150,3992,184],{"class":183},[150,3994,608],{"class":190},[150,3996,194],{"class":183},[150,3998,613],{"class":160},[150,4000,200],{"class":190},[150,4002,4003],{"class":152,"line":268},[150,4004,222],{"emptyLinePlaceholder":221},[150,4006,4007],{"class":152,"line":273},[150,4008,4009],{"class":176},"// Only downloads when the user clicks \"Settings\"\n",[150,4011,4012,4014,4017,4019,4021,4023,4025,4027,4029,4032],{"class":152,"line":292},[150,4013,228],{"class":183},[150,4015,4016],{"class":231}," SettingsModal",[150,4018,235],{"class":183},[150,4020,3379],{"class":156},[150,4022,1183],{"class":190},[150,4024,1186],{"class":183},[150,4026,3392],{"class":183},[150,4028,1127],{"class":190},[150,4030,4031],{"class":160},"'./settings-modal'",[150,4033,4034],{"class":190},"), {\n",[150,4036,4037,4040,4042,4044,4047],{"class":152,"line":298},[150,4038,4039],{"class":156},"  loading",[150,4041,3434],{"class":190},[150,4043,1186],{"class":183},[150,4045,4046],{"class":231}," null",[150,4048,259],{"class":190},[150,4050,4051],{"class":152,"line":304},[150,4052,265],{"class":190},[150,4054,4055],{"class":152,"line":309},[150,4056,222],{"emptyLinePlaceholder":221},[150,4058,4059,4061,4063,4066],{"class":152,"line":655},[150,4060,312],{"class":183},[150,4062,1092],{"class":183},[150,4064,4065],{"class":156}," SettingsButton",[150,4067,1098],{"class":190},[150,4069,4070,4072,4074,4077,4079,4082,4084,4086,4088,4090,4092],{"class":152,"line":1013},[150,4071,1104],{"class":183},[150,4073,1107],{"class":190},[150,4075,4076],{"class":231},"open",[150,4078,1113],{"class":190},[150,4080,4081],{"class":231},"setOpen",[150,4083,1119],{"class":190},[150,4085,335],{"class":183},[150,4087,1124],{"class":156},[150,4089,1127],{"class":190},[150,4091,3424],{"class":231},[150,4093,1133],{"class":190},[150,4095,4096],{"class":152,"line":1037},[150,4097,222],{"emptyLinePlaceholder":221},[150,4099,4100,4102],{"class":152,"line":1049},[150,4101,1461],{"class":183},[150,4103,1464],{"class":190},[150,4105,4106],{"class":152,"line":1062},[150,4107,2917],{"class":183},[150,4109,4110,4112,4115,4117,4120,4122,4125,4127,4129,4131,4134],{"class":152,"line":1074},[150,4111,1482],{"class":183},[150,4113,4114],{"class":190},"Button startIcon",[150,4116,335],{"class":183},[150,4118,4119],{"class":190},"{\u003CSettings />} onClick",[150,4121,335],{"class":183},[150,4123,4124],{"class":190},"{() => ",[150,4126,4081],{"class":156},[150,4128,1127],{"class":190},[150,4130,338],{"class":991},[150,4132,4133],{"class":190},")}",[150,4135,1476],{"class":183},[150,4137,4138],{"class":152,"line":1080},[150,4139,4140],{"class":991},"        Settings\n",[150,4142,4143,4145,4148],{"class":152,"line":1085},[150,4144,1694],{"class":183},[150,4146,4147],{"class":190},"Button",[150,4149,1476],{"class":183},[150,4151,4152,4154,4156,4159,4162,4165,4167,4170,4172,4174,4176,4178,4180,4183,4186],{"class":152,"line":1101},[150,4153,1514],{"class":190},[150,4155,4076],{"class":991},[150,4157,4158],{"class":190}," && \u003C",[150,4160,4161],{"class":991},"SettingsModal",[150,4163,4164],{"class":991}," open",[150,4166,335],{"class":183},[150,4168,4169],{"class":190},"{open} onClose",[150,4171,335],{"class":183},[150,4173,4124],{"class":190},[150,4175,4081],{"class":156},[150,4177,1127],{"class":190},[150,4179,3424],{"class":991},[150,4181,4182],{"class":190},")} ",[150,4184,4185],{"class":183},"/>",[150,4187,1077],{"class":190},[150,4189,4190],{"class":152,"line":1136},[150,4191,3138],{"class":183},[150,4193,4194],{"class":152,"line":1166},[150,4195,1995],{"class":190},[150,4197,4198],{"class":152,"line":1171},[150,4199,1077],{"class":190},[10,4201,841,4202,4205],{},[14,4203,4204],{},"{open && \u003CSettingsModal />}"," pattern means the dynamic import doesn't even start until the user clicks the button. Zero cost until it's needed.",[138,4207,760],{"id":4208},"results-2",[38,4210,4211,4221],{},[41,4212,4213],{},[44,4214,4215,4218],{},[47,4216,4217],{},"Component",[47,4219,4220],{},"Size removed from initial bundle",[54,4222,4223,4231,4239],{},[44,4224,4225,4228],{},[59,4226,4227],{},"Recharts (all components)",[59,4229,4230],{},"45kb",[44,4232,4233,4236],{},[59,4234,4235],{},"Settings modal + rich text editor",[59,4237,4238],{},"7kb",[44,4240,4241,4245],{},[59,4242,4243],{},[362,4244,3217],{},[59,4246,4247],{},[362,4248,4249],{},"52kb",[19,4251,4254],{"className":4252,"code":4253,"language":24},[22],"First Load JS: 290kb → 238kb\n",[14,4255,4253],{"__ignoreMap":27},[10,4257,4258],{},"The charts still load fast — dynamic chunks are typically fetched in under 200ms on a decent connection. The skeleton loader prevents any layout shift while the chunk loads.",[126,4260,4262],{"id":4261},"fix-4-replace-heavy-dependencies-saved-33kb","Fix 4: Replace heavy dependencies (saved 33kb)",[10,4264,4265],{},"Some libraries earn their bundle cost. Others don't. TaskFlow had three that were easy wins.",[138,4267,4269],{"id":4268},"momentjs-date-fns","moment.js → date-fns",[10,4271,4272,4273,4276],{},"moment.js is 67kb minified. It can't tree-shake because of its architecture — you always ship the entire library, even if you use one function. TaskFlow used ",[14,4274,4275],{},"moment()"," in exactly three places: formatting dates for display.",[19,4278,4280],{"className":167,"code":4279,"language":169,"meta":27,"style":27},"// Before — 67kb for this\nimport moment from 'moment';\n\nconst formatted = moment(project.updatedAt).format('MMM D, YYYY');\nconst relative = moment(project.updatedAt).fromNow();\nconst isRecent = moment(project.updatedAt).isAfter(moment().subtract(7, 'days'));\n",[14,4281,4282,4287,4301,4305,4329,4347],{"__ignoreMap":27},[150,4283,4284],{"class":152,"line":153},[150,4285,4286],{"class":176},"// Before — 67kb for this\n",[150,4288,4289,4291,4294,4296,4299],{"class":152,"line":180},[150,4290,184],{"class":183},[150,4292,4293],{"class":190}," moment ",[150,4295,194],{"class":183},[150,4297,4298],{"class":160}," 'moment'",[150,4300,200],{"class":190},[150,4302,4303],{"class":152,"line":203},[150,4304,222],{"emptyLinePlaceholder":221},[150,4306,4307,4309,4312,4314,4317,4320,4322,4324,4327],{"class":152,"line":218},[150,4308,228],{"class":183},[150,4310,4311],{"class":231}," formatted",[150,4313,235],{"class":183},[150,4315,4316],{"class":156}," moment",[150,4318,4319],{"class":190},"(project.updatedAt).",[150,4321,1910],{"class":156},[150,4323,1127],{"class":190},[150,4325,4326],{"class":160},"'MMM D, YYYY'",[150,4328,1133],{"class":190},[150,4330,4331,4333,4336,4338,4340,4342,4345],{"class":152,"line":225},[150,4332,228],{"class":183},[150,4334,4335],{"class":231}," relative",[150,4337,235],{"class":183},[150,4339,4316],{"class":156},[150,4341,4319],{"class":190},[150,4343,4344],{"class":156},"fromNow",[150,4346,2242],{"class":190},[150,4348,4349,4351,4354,4356,4358,4360,4363,4365,4368,4371,4374,4376,4379,4381,4384],{"class":152,"line":244},[150,4350,228],{"class":183},[150,4352,4353],{"class":231}," isRecent",[150,4355,235],{"class":183},[150,4357,4316],{"class":156},[150,4359,4319],{"class":190},[150,4361,4362],{"class":156},"isAfter",[150,4364,1127],{"class":190},[150,4366,4367],{"class":156},"moment",[150,4369,4370],{"class":190},"().",[150,4372,4373],{"class":156},"subtract",[150,4375,1127],{"class":190},[150,4377,4378],{"class":231},"7",[150,4380,1113],{"class":190},[150,4382,4383],{"class":160},"'days'",[150,4385,4386],{"class":190},"));\n",[19,4388,4390],{"className":167,"code":4389,"language":169,"meta":27,"style":27},"// After — ~3kb for these three imports (tree-shakeable)\nimport { format, formatDistanceToNow, isAfter, subDays } from 'date-fns';\n\nconst formatted = format(new Date(project.updatedAt), 'MMM d, yyyy');\nconst relative = formatDistanceToNow(new Date(project.updatedAt), { addSuffix: true });\nconst isRecent = isAfter(new Date(project.updatedAt), subDays(new Date(), 7));\n",[14,4391,4392,4397,4410,4414,4439,4464],{"__ignoreMap":27},[150,4393,4394],{"class":152,"line":153},[150,4395,4396],{"class":176},"// After — ~3kb for these three imports (tree-shakeable)\n",[150,4398,4399,4401,4404,4406,4408],{"class":152,"line":180},[150,4400,184],{"class":183},[150,4402,4403],{"class":190}," { format, formatDistanceToNow, isAfter, subDays } ",[150,4405,194],{"class":183},[150,4407,902],{"class":160},[150,4409,200],{"class":190},[150,4411,4412],{"class":152,"line":203},[150,4413,222],{"emptyLinePlaceholder":221},[150,4415,4416,4418,4420,4422,4425,4427,4429,4431,4434,4437],{"class":152,"line":218},[150,4417,228],{"class":183},[150,4419,4311],{"class":231},[150,4421,235],{"class":183},[150,4423,4424],{"class":156}," format",[150,4426,1127],{"class":190},[150,4428,1915],{"class":183},[150,4430,1918],{"class":156},[150,4432,4433],{"class":190},"(project.updatedAt), ",[150,4435,4436],{"class":160},"'MMM d, yyyy'",[150,4438,1133],{"class":190},[150,4440,4441,4443,4445,4447,4450,4452,4454,4456,4459,4461],{"class":152,"line":225},[150,4442,228],{"class":183},[150,4444,4335],{"class":231},[150,4446,235],{"class":183},[150,4448,4449],{"class":156}," formatDistanceToNow",[150,4451,1127],{"class":190},[150,4453,1915],{"class":183},[150,4455,1918],{"class":156},[150,4457,4458],{"class":190},"(project.updatedAt), { addSuffix: ",[150,4460,338],{"class":231},[150,4462,4463],{"class":190}," });\n",[150,4465,4466,4468,4470,4472,4475,4477,4479,4481,4483,4486,4488,4490,4492,4495,4497],{"class":152,"line":244},[150,4467,228],{"class":183},[150,4469,4353],{"class":231},[150,4471,235],{"class":183},[150,4473,4474],{"class":156}," isAfter",[150,4476,1127],{"class":190},[150,4478,1915],{"class":183},[150,4480,1918],{"class":156},[150,4482,4433],{"class":190},[150,4484,4485],{"class":156},"subDays",[150,4487,1127],{"class":190},[150,4489,1915],{"class":183},[150,4491,1918],{"class":156},[150,4493,4494],{"class":190},"(), ",[150,4496,4378],{"class":231},[150,4498,4386],{"class":190},[10,4500,4501,4502,4505],{},"The API is slightly different, but the migration is straightforward. For an app-wide find-and-replace, ",[14,4503,4504],{},"npx moment-to-date-fns"," can automate most of it.",[138,4507,4509],{"id":4508},"audit-with-knip","Audit with knip",[10,4511,4512,4513,281],{},"How do you find unused imports you don't know about? Run ",[14,4514,4515],{},"knip",[19,4517,4519],{"className":144,"code":4518,"language":146,"meta":27,"style":27},"npx knip\n",[14,4520,4521],{"__ignoreMap":27},[150,4522,4523,4526],{"class":152,"line":153},[150,4524,4525],{"class":156},"npx",[150,4527,4528],{"class":160}," knip\n",[19,4530,4533],{"className":4531,"code":4532,"language":24},[22],"Unused dependencies (2)\n framer-motion    package.json\n react-hot-toast  package.json\n\nUnused exports (4)\n ProjectCard      app/components/project-card.tsx:12\n useAnalytics     app/hooks/use-analytics.ts:5\n formatCurrency   app/utils/format.ts:23\n CHART_COLORS     app/constants.ts:8\n",[14,4534,4532],{"__ignoreMap":27},[10,4536,4537,4538,4540,4541,4544],{},"TaskFlow had ",[14,4539,400],{}," (32kb) still in ",[14,4542,4543],{},"package.json"," from an old animation experiment. The import was in a component file but the component was never rendered. The bundler included it anyway because it couldn't prove the import was dead.",[138,4546,760],{"id":4547},"results-3",[38,4549,4550,4559],{},[41,4551,4552],{},[44,4553,4554,4557],{},[47,4555,4556],{},"Change",[47,4558,778],{},[54,4560,4561,4568,4576,4584],{},[44,4562,4563,4565],{},[59,4564,4269],{},[59,4566,4567],{},"20kb",[44,4569,4570,4573],{},[59,4571,4572],{},"Remove unused framer-motion",[59,4574,4575],{},"5kb",[44,4577,4578,4581],{},[59,4579,4580],{},"Remove unused MUI component imports",[59,4582,4583],{},"8kb",[44,4585,4586,4590],{},[59,4587,4588],{},[362,4589,3217],{},[59,4591,4592],{},[362,4593,4594],{},"33kb",[19,4596,4599],{"className":4597,"code":4598,"language":24},[22],"First Load JS: 238kb → 205kb\n",[14,4600,4598],{"__ignoreMap":27},[10,4602,4603,4604,4606,4607,4610],{},"The lesson: your bundler can only tree-shake what's provably unused at the module level. If you import a library but never call it, some bundlers will still include it. Regular audits with ",[14,4605,4515],{}," or ",[14,4608,4609],{},"depcheck"," catch what tree-shaking misses.",[126,4612,4614],{"id":4613},"fix-5-build-configuration-saved-20kb","Fix 5: Build configuration (saved 20kb)",[10,4616,4617],{},"The last 20kb came from build-level optimizations. These aren't dramatic individually, but they compound.",[138,4619,4621],{"id":4620},"final-nextconfigts","Final next.config.ts",[10,4623,4624],{},"Here's TaskFlow's optimized config with everything from this article combined:",[19,4626,4628],{"className":167,"code":4627,"language":169,"meta":27,"style":27},"// next.config.ts\nimport type { NextConfig } from 'next';\nimport bundleAnalyzer from '@next/bundle-analyzer';\n\nconst withBundleAnalyzer = bundleAnalyzer({\n  enabled: process.env.ANALYZE === 'true',\n});\n\nconst nextConfig: NextConfig = {\n  reactStrictMode: true,\n  experimental: {\n    optimizePackageImports: [\n      '@mui/material',\n      '@mui/icons-material',\n      'lodash',\n      'date-fns',\n    ],\n    optimizeCss: true,\n  },\n};\n\nexport default withBundleAnalyzer(nextConfig);\n",[14,4629,4630,4634,4648,4660,4664,4676,4688,4692,4696,4710,4719,4723,4727,4733,4739,4745,4752,4756,4765,4769,4773,4777],{"__ignoreMap":27},[150,4631,4632],{"class":152,"line":153},[150,4633,177],{"class":176},[150,4635,4636,4638,4640,4642,4644,4646],{"class":152,"line":180},[150,4637,184],{"class":183},[150,4639,187],{"class":183},[150,4641,191],{"class":190},[150,4643,194],{"class":183},[150,4645,197],{"class":160},[150,4647,200],{"class":190},[150,4649,4650,4652,4654,4656,4658],{"class":152,"line":203},[150,4651,184],{"class":183},[150,4653,208],{"class":190},[150,4655,194],{"class":183},[150,4657,213],{"class":160},[150,4659,200],{"class":190},[150,4661,4662],{"class":152,"line":218},[150,4663,222],{"emptyLinePlaceholder":221},[150,4665,4666,4668,4670,4672,4674],{"class":152,"line":225},[150,4667,228],{"class":183},[150,4669,232],{"class":231},[150,4671,235],{"class":183},[150,4673,238],{"class":156},[150,4675,241],{"class":190},[150,4677,4678,4680,4682,4684,4686],{"class":152,"line":244},[150,4679,247],{"class":190},[150,4681,250],{"class":231},[150,4683,253],{"class":183},[150,4685,256],{"class":160},[150,4687,259],{"class":190},[150,4689,4690],{"class":152,"line":262},[150,4691,265],{"class":190},[150,4693,4694],{"class":152,"line":268},[150,4695,222],{"emptyLinePlaceholder":221},[150,4697,4698,4700,4702,4704,4706,4708],{"class":152,"line":273},[150,4699,228],{"class":183},[150,4701,278],{"class":231},[150,4703,281],{"class":183},[150,4705,284],{"class":156},[150,4707,235],{"class":183},[150,4709,289],{"class":190},[150,4711,4712,4715,4717],{"class":152,"line":292},[150,4713,4714],{"class":190},"  reactStrictMode: ",[150,4716,338],{"class":231},[150,4718,259],{"class":190},[150,4720,4721],{"class":152,"line":298},[150,4722,709],{"class":190},[150,4724,4725],{"class":152,"line":304},[150,4726,714],{"class":190},[150,4728,4729,4731],{"class":152,"line":309},[150,4730,719],{"class":160},[150,4732,259],{"class":190},[150,4734,4735,4737],{"class":152,"line":655},[150,4736,726],{"class":160},[150,4738,259],{"class":190},[150,4740,4741,4743],{"class":152,"line":1013},[150,4742,733],{"class":160},[150,4744,259],{"class":190},[150,4746,4747,4750],{"class":152,"line":1037},[150,4748,4749],{"class":160},"      'date-fns'",[150,4751,259],{"class":190},[150,4753,4754],{"class":152,"line":1049},[150,4755,740],{"class":190},[150,4757,4758,4761,4763],{"class":152,"line":1062},[150,4759,4760],{"class":190},"    optimizeCss: ",[150,4762,338],{"class":231},[150,4764,259],{"class":190},[150,4766,4767],{"class":152,"line":1074},[150,4768,745],{"class":190},[150,4770,4771],{"class":152,"line":1080},[150,4772,301],{"class":190},[150,4774,4775],{"class":152,"line":1085},[150,4776,222],{"emptyLinePlaceholder":221},[150,4778,4779,4781,4783,4785],{"class":152,"line":1101},[150,4780,312],{"class":183},[150,4782,315],{"class":183},[150,4784,232],{"class":156},[150,4786,320],{"class":190},[138,4788,4790],{"id":4789},"verify-compression","Verify compression",[10,4792,4793],{},"Bundle size on disk doesn't equal bytes over the wire. Make sure your hosting platform serves Brotli-compressed assets — it compresses 15-20% better than Gzip for JavaScript.",[10,4795,4796],{},"Check your response headers:",[19,4798,4800],{"className":144,"code":4799,"language":146,"meta":27,"style":27},"curl -sI -H \"Accept-Encoding: br\" https://your-app.com/_next/static/chunks/main-app.js | grep content-encoding\n# Should show: content-encoding: br\n",[14,4801,4802,4827],{"__ignoreMap":27},[150,4803,4804,4807,4810,4813,4816,4819,4821,4824],{"class":152,"line":153},[150,4805,4806],{"class":156},"curl",[150,4808,4809],{"class":231}," -sI",[150,4811,4812],{"class":231}," -H",[150,4814,4815],{"class":160}," \"Accept-Encoding: br\"",[150,4817,4818],{"class":160}," https://your-app.com/_next/static/chunks/main-app.js",[150,4820,1024],{"class":183},[150,4822,4823],{"class":156}," grep",[150,4825,4826],{"class":160}," content-encoding\n",[150,4828,4829],{"class":152,"line":180},[150,4830,4831],{"class":176},"# Should show: content-encoding: br\n",[10,4833,4834,4835,4838,4839,4842],{},"If you see ",[14,4836,4837],{},"gzip"," instead of ",[14,4840,4841],{},"br",", check your platform's compression settings. Vercel and Cloudflare enable Brotli by default. Self-hosted setups may need to configure it manually.",[138,4844,760],{"id":4845},"results-4",[38,4847,4848,4857],{},[41,4849,4850],{},[44,4851,4852,4855],{},[47,4853,4854],{},"Optimization",[47,4856,778],{},[54,4858,4859,4866,4874],{},[44,4860,4861,4864],{},[59,4862,4863],{},"optimizeCss (removes unused CSS)",[59,4865,4583],{},[44,4867,4868,4871],{},[59,4869,4870],{},"Brotli compression gains (vs Gzip)",[59,4872,4873],{},"12kb",[44,4875,4876,4880],{},[59,4877,4878],{},[362,4879,3217],{},[59,4881,4882],{},[362,4883,4567],{},[19,4885,4888],{"className":4886,"code":4887,"language":24},[22],"First Load JS: 205kb → 185kb ✓\n",[14,4889,4887],{"__ignoreMap":27},[126,4891,4893],{"id":4892},"the-results","The results",[10,4895,4896],{},"Five fixes. One day of focused work. Here's the full picture:",[38,4898,4899,4911],{},[41,4900,4901],{},[44,4902,4903,4905,4907,4909],{},[47,4904,49],{},[47,4906,772],{},[47,4908,775],{},[47,4910,4556],{},[54,4912,4913,4927,4941,4955,4971,4987],{},[44,4914,4915,4917,4919,4922],{},[59,4916,61],{},[59,4918,64],{},[59,4920,4921],{},"185kb",[59,4923,4924],{},[362,4925,4926],{},"-55%",[44,4928,4929,4931,4933,4936],{},[59,4930,69],{},[59,4932,72],{},[59,4934,4935],{},"780kb",[59,4937,4938],{},[362,4939,4940],{},"-57%",[44,4942,4943,4945,4947,4950],{},[59,4944,77],{},[59,4946,80],{},[59,4948,4949],{},"82",[59,4951,4952],{},[362,4953,4954],{},"+44 points",[44,4956,4957,4961,4963,4966],{},[59,4958,4959],{},[86,4960,89],{"href":88},[59,4962,92],{},[59,4964,4965],{},"2.1s",[59,4967,4968],{},[362,4969,4970],{},"-56%",[44,4972,4973,4977,4979,4982],{},[59,4974,4975],{},[86,4976,100],{"href":99},[59,4978,103],{},[59,4980,4981],{},"160ms",[59,4983,4984],{},[362,4985,4986],{},"-53%",[44,4988,4989,4993,4995,4997],{},[59,4990,4991],{},[86,4992,111],{"href":110},[59,4994,114],{},[59,4996,114],{},[59,4998,4999],{},"No change",[10,5001,5002],{},"And the breakdown by fix:",[38,5004,5005,5020],{},[41,5006,5007],{},[44,5008,5009,5012,5015,5017],{},[47,5010,5011],{},"Fix",[47,5013,5014],{},"Technique",[47,5016,778],{},[47,5018,5019],{},"Running Total",[54,5021,5022,5035,5046,5059,5072],{},[44,5023,5024,5027,5030,5032],{},[59,5025,5026],{},"1",[59,5028,5029],{},"Barrel imports + tree-shaking",[59,5031,793],{},[59,5033,5034],{},"348kb",[44,5036,5037,5039,5041,5043],{},[59,5038,3499],{},[59,5040,3153],{},[59,5042,3222],{},[59,5044,5045],{},"290kb",[44,5047,5048,5051,5054,5056],{},[59,5049,5050],{},"3",[59,5052,5053],{},"Dynamic imports",[59,5055,4249],{},[59,5057,5058],{},"238kb",[44,5060,5061,5064,5067,5069],{},[59,5062,5063],{},"4",[59,5065,5066],{},"Replace heavy deps",[59,5068,4594],{},[59,5070,5071],{},"205kb",[44,5073,5074,5077,5080,5082],{},[59,5075,5076],{},"5",[59,5078,5079],{},"Build config + compression",[59,5081,4567],{},[59,5083,4921],{},[138,5085,5087],{"id":5086},"what-made-the-biggest-difference","What made the biggest difference",[10,5089,5090],{},"The top two fixes — import optimization and Server Components — accounted for 122kb, more than half the total reduction. Neither required any new tooling or architectural overhaul. Import fixes are a config change. Server Components are about drawing the right line between what needs to be interactive and what doesn't.",[138,5092,5094],{"id":5093},"the-core-web-vitals-connection","The Core Web Vitals connection",[10,5096,5097],{},"Bundle size doesn't exist in a vacuum. Every kilobyte of JavaScript has a cascading effect:",[356,5099,5100,5107,5114],{},[359,5101,5102,5106],{},[362,5103,5104],{},[86,5105,89],{"href":88}," dropped from 4.8s to 2.1s because the browser could start rendering sooner. Less JavaScript to download and parse means the largest content element appears faster.",[359,5108,5109,5113],{},[362,5110,5111],{},[86,5112,100],{"href":99}," improved from 340ms to 160ms because there's less JavaScript competing for the main thread. User interactions get processed faster when the browser isn't busy parsing a 400kb bundle.",[359,5115,5116,5120],{},[362,5117,5118],{},[86,5119,111],{"href":110}," stayed the same at 0.02 — bundle size doesn't directly affect layout stability, but the skeleton loaders we added for dynamic imports help maintain it.",[10,5122,5123,5124,1113,5127,5130,5131,1835],{},"For a deeper dive on each metric, see our guides on ",[86,5125,5126],{"href":88},"improving LCP",[86,5128,5129],{"href":110},"fixing CLS",", and ",[86,5132,5133],{"href":99},"improving INP",[138,5135,5137],{"id":5136},"a-note-on-ai-discoverability","A note on AI discoverability",[10,5139,5140],{},"JavaScript-heavy apps face an emerging challenge: AI crawlers like GPTBot and ClaudeBot don't execute JavaScript. If your content is trapped behind client-side rendering, it's invisible to AI-powered search engines. Reducing client-side JavaScript and using Server Components for content-heavy pages helps ensure your app is discoverable by both traditional and AI-powered search.",[126,5142,5144],{"id":5143},"your-action-plan","Your action plan",[5146,5147,5148,5157,5163,5169,5172,5179],"ol",{},[359,5149,5150,5151,5153,5154],{},"Install ",[14,5152,135],{}," and run ",[14,5155,5156],{},"ANALYZE=true next build",[359,5158,5159,5160,5162],{},"Fix barrel imports or add ",[14,5161,680],{}," to your config",[359,5164,5165,5166,5168],{},"Look for ",[14,5167,844],{}," files that do data fetching or transformation — split them",[359,5170,5171],{},"Dynamic import any component larger than 20kb that isn't visible on initial load",[359,5173,5174,5175,5178],{},"Run ",[14,5176,5177],{},"npx knip"," to find unused dependencies",[359,5180,5181],{},"Verify Brotli compression is enabled on your hosting platform",[10,5183,5184,5185,5188,5189,5193],{},"If you're using MUI with Next.js, our ",[86,5186,5187],{"href":506},"Material UI performance guide"," covers MUI-specific optimizations in depth. For a broader look at ",[86,5190,5192],{"href":5191},"/blog/nextjs-core-web-vitals/","Next.js and Core Web Vitals",", we have a dedicated guide for that too.",[126,5195,5197],{"id":5196},"frequently-asked-questions","Frequently Asked Questions",[138,5199,5201],{"id":5200},"what-is-a-good-first-load-js-size-for-nextjs","What is a good First Load JS size for Next.js?",[10,5203,5204,5205,5208],{},"Next.js color-codes bundle sizes in its build output — green means acceptable, red means too large. The threshold is around ",[362,5206,5207],{},"130kb",". Well-optimized production apps with App Router typically achieve 80-130kb First Load JS. According to a study of 300,000 Next.js domains, the lightest 10% have median total bundles around 350kb, while the median site crosses 1MB. If your First Load JS is above 200kb, there's likely room to optimize.",[138,5210,5212],{"id":5211},"why-is-my-nextjs-bundle-so-big","Why is my Next.js bundle so big?",[10,5214,5215,5216,5218],{},"The most common culprits are: barrel imports pulling in entire libraries (especially MUI icons and lodash), heavy client-side dependencies that should be Server Components, charting libraries loaded on initial page load instead of dynamically, and date libraries like moment.js that don't tree-shake well. Use ",[14,5217,135],{}," to visualize exactly which packages are inflating your bundle.",[138,5220,5222],{"id":5221},"does-bundle-size-affect-core-web-vitals","Does bundle size affect Core Web Vitals?",[10,5224,5225,5226,5228,5229,5231,5232,5235],{},"Yes, directly. Large JavaScript bundles block rendering (hurting ",[86,5227,89],{"href":88},"), increase parse and execution time (hurting ",[86,5230,100],{"href":99},"), and delay interactivity. Every 100kb of JavaScript adds roughly ",[362,5233,5234],{},"350ms of processing time"," on a mid-tier mobile device. Reducing bundle size is one of the most impactful ways to improve all three Core Web Vitals.",[138,5237,5239],{"id":5238},"app-router-vs-pages-router-which-has-smaller-bundles","App Router vs Pages Router — which has smaller bundles?",[10,5241,5242,5243,5245,5246,5248,5249,5253],{},"App Router can produce smaller client bundles when used correctly, because ",[86,5244,3153],{"href":3152}," run entirely on the server and ship zero JavaScript to the browser. However, if you mark too many components with ",[14,5247,844],{}," or use client-heavy libraries without dynamic imports, App Router bundles can actually be ",[5250,5251,5252],"em",{},"larger",". A MUI GitHub issue documented a 42% bundle increase when migrating to App Router without optimizing component boundaries.",[138,5255,5257],{"id":5256},"do-react-server-components-reduce-bundle-size","Do React Server Components reduce bundle size?",[10,5259,5260,5261,1113,5264,1113,5267,5270,5271,5274],{},"Yes — Server Components are the single biggest bundle size win in modern Next.js. Any component that doesn't need browser APIs (",[14,5262,5263],{},"useState",[14,5265,5266],{},"useEffect",[14,5268,5269],{},"onClick",") or interactivity can be a Server Component, meaning its code never reaches the browser. Data fetching, markdown rendering, syntax highlighting, and data transformations are ideal candidates. Real-world reports show ",[362,5272,5273],{},"25-30% client bundle reductions"," from proper Server Component usage.",[5276,5277,5278],"style",{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}",{"title":27,"searchDepth":180,"depth":180,"links":5280},[5281,5285,5291,5296,5301,5306,5311,5316,5317],{"id":128,"depth":180,"text":129,"children":5282},[5283,5284],{"id":140,"depth":203,"text":141},{"id":350,"depth":203,"text":351},{"id":410,"depth":180,"text":411,"children":5286},[5287,5288,5289,5290],{"id":417,"depth":203,"text":418},{"id":511,"depth":203,"text":512},{"id":673,"depth":203,"text":674},{"id":759,"depth":203,"text":760},{"id":834,"depth":180,"text":835,"children":5292},[5293,5294,5295],{"id":848,"depth":203,"text":418},{"id":2003,"depth":203,"text":512},{"id":3157,"depth":203,"text":760},{"id":3234,"depth":180,"text":3235,"children":5297},[5298,5299,5300],{"id":3244,"depth":203,"text":418},{"id":3303,"depth":203,"text":512},{"id":4208,"depth":203,"text":760},{"id":4261,"depth":180,"text":4262,"children":5302},[5303,5304,5305],{"id":4268,"depth":203,"text":4269},{"id":4508,"depth":203,"text":4509},{"id":4547,"depth":203,"text":760},{"id":4613,"depth":180,"text":4614,"children":5307},[5308,5309,5310],{"id":4620,"depth":203,"text":4621},{"id":4789,"depth":203,"text":4790},{"id":4845,"depth":203,"text":760},{"id":4892,"depth":180,"text":4893,"children":5312},[5313,5314,5315],{"id":5086,"depth":203,"text":5087},{"id":5093,"depth":203,"text":5094},{"id":5136,"depth":203,"text":5137},{"id":5143,"depth":180,"text":5144},{"id":5196,"depth":180,"text":5197,"children":5318},[5319,5320,5321,5322,5323],{"id":5200,"depth":203,"text":5201},{"id":5211,"depth":203,"text":5212},{"id":5221,"depth":203,"text":5222},{"id":5238,"depth":203,"text":5239},{"id":5256,"depth":203,"text":5257},"A step-by-step case study reducing a Next.js app's First Load JS from 412kb to 185kb. Learn to diagnose bundle bloat with real code fixes and measurable results.","md",[5327,5329,5331,5333,5335],{"question":5201,"answer":5328},"Next.js color-codes bundle sizes in its build output — green means acceptable, red means too large. The threshold is around 130kb. Well-optimized production apps with App Router typically achieve 80-130kb First Load JS. According to a study of 300,000 Next.js domains, the lightest 10% have median total bundles around 350kb, while the median site crosses 1MB. If your First Load JS is above 200kb, there's likely room to optimize.",{"question":5212,"answer":5330},"The most common culprits are: barrel imports pulling in entire libraries (especially MUI icons and lodash), heavy client-side dependencies that should be Server Components, charting libraries loaded on initial page load instead of dynamically, and date libraries like moment.js that don't tree-shake well. Use @next/bundle-analyzer to visualize exactly which packages are inflating your bundle.",{"question":5222,"answer":5332},"Yes, directly. Large JavaScript bundles block rendering (hurting LCP), increase parse and execution time (hurting INP), and delay interactivity. Every 100kb of JavaScript adds roughly 350ms of processing time on a mid-tier mobile device. Reducing bundle size is one of the most impactful ways to improve all three Core Web Vitals.",{"question":5239,"answer":5334},"App Router can produce smaller client bundles when used correctly, because Server Components run entirely on the server and ship zero JavaScript to the browser. However, if you mark too many components with 'use client' or use client-heavy libraries without dynamic imports, App Router bundles can actually be larger. A MUI GitHub issue documented a 42% bundle increase when migrating to App Router without optimizing component boundaries.",{"question":5257,"answer":5336},"Yes — Server Components are the single biggest bundle size win in modern Next.js. Any component that doesn't need browser APIs (useState, useEffect, onClick) or interactivity can be a Server Component, meaning its code never reaches the browser. Data fetching, markdown rendering, syntax highlighting, and data transformations are ideal candidates. Real-world reports show 25-30% client bundle reductions from proper Server Component usage.","/og/nextjs-bundle-size.png","Next.js guide: Next.js Bundle Size: Find and Fix What's Bloating Your App",null,{},"/blog/nextjs-bundle-size","2026-04-15",[5344,5347,5349],{"title":5345,"url":5346},"Next.js Package Bundling Guide","https://nextjs.org/docs/app/guides/package-bundling",{"title":5348,"url":120},"Next.js Bundle Sizes: Insights from 300,000 Domains",{"title":135,"url":5350},"https://www.npmjs.com/package/@next/bundle-analyzer",{"title":5,"description":5324},"blog/nextjs-bundle-size",[5354,5355,5356,5357,5358],"next.js","bundle-size","performance","react","core-web-vitals","KVObAtqlFo3tMZ-UoThEL_2FapSOOHJ7Da44jMqaVqQ",1776939172536]