DISCLAIMER: This analysis is for educational purposes only. All trademarks mentioned are property of their respective owners. Catchmetrics is not affiliated with Polymarket or any other companies mentioned. Views expressed are solely those of the author, CatchMetrics Ltd.
JavaScript bundle size is particularly critical - large bundles slow down load times, degrade user experience, and harm SEO rankings. Our recent analysis of Polymarket.com, a popular Next.js application, revealed how their JavaScript bundle ballooned to an astonishing 9 MB (minified, uncompressed). According to our previous blog post where we looked into data from 300,000 sites using Next.js that lands them in the 98th percentile, indicating significant optimization opportunities. Here's our detailed breakdown and recommendations to prevent similar issues.
Bundle Size Explained in Next.js
In Next.js, "bundle size" refers to the total size of all JavaScript files sent to the browser during the initial page load. Large bundle sizes can significantly delay load times, negatively impacting user experience and search engine optimization. Typically, these excessive sizes arise due to several factors including inefficient module imports, inclusion of unused or redundant code, insufficient utilization of tree-shaking techniques (which help eliminate unused code during build time), and inadequate implementation of code-splitting strategies (which divide JavaScript into smaller chunks that load only when required). Effectively managing these areas ensures optimal application performance and enhanced user interactions.
Core Web Vitals: The Real‑World Cost of Bloated Bundles
Bundle size isn’t an abstract build‑time metric - it directly surfaces in Core Web Vitals(CWV) data. The screenshot below comes from CrUX Vis for Polymarket’s origin on mobile devices.
What the graph shows
Metric | 75th‑percentile value | Status | Trend |
---|---|---|---|
Largest Contentful Paint (LCP) | ~4.7 s | Poor (red) | Brief improvement Apr - May 2025 → regressing again by June 2025 |
Interaction to Next Paint (INP) | ~497 ms | Needs Improvement (amber) | Uptick to poor in late 2024, improved in Apr – May 2025, now holding steady |
Cumulative Layout Shift (CLS) | 0.07 | Good (green) | Sharp improvement in Jan 2025, got worse in March - June, fixed in July 2025 |
Why CLS is fine: layout stability is dominated by visual assets and CSS, both of which Polymarket handles reasonably well. The site’s pain points are squarely tied to JavaScript weight and main‑thread blocking.
Business impact of poor CWV
- Search ranking hit: Google’s page‑experience signals can demote pages with consistently poor LCP/INP, reducing organic acquisition.
- Paid traffic waste: Users who bounce before the first interaction still cost the same in ad spend.
- User‑retention risk: Portent’s Site Speed & Conversion Rates study shows pages that load in 1 second convert 3x better than those that take 5 seconds, and conversion rate falls by 4.4 % for every additional second between 0 - 5 s. On a real‑time prediction market like Polymarket, slower loads mean fewer traders entering markets and thinner liquidity.
Our Analytical Approach
METHODOLOGY DISCLAIMER: This analysis was conducted using only publicly accessible web assets that are served to all users visiting the website. No proprietary information, private source code, or internal systems were accessed. All findings are based on standard web development analysis techniques applied to public resources.
To investigate Polymarket’s bundle size issues, we:
- Downloaded all JavaScript bundles and source map files. Only assets publicly available, no private code or credentials accessed.
- Converted these source maps back to readable TypeScript using the source-map library.
- Then we tried to reconstruct their package.json file as well from the reconstructed node_modules directory and from other hints from sourcemaps.
This process also restored many code comments that were included in the public source maps. We did not have access to any private source or build process documentation as part of this experiment. As such, there may be errors or omissions, but we thought it was a very interesting approach to demonstrating common Next.js bundle optimization opportunities.
Identified Key Issues
Our analysis uncovered several critical practices significantly inflating their bundle size:
Importing Entire Lodash
Instead of selectively importing only what's needed, the entire Lodash library was imported unnecessarily. This typically happens when developers use default or wildcard imports like import _ from 'lodash', which pulls in the entire library - even if only a single utility function like capitalize or isEqual is needed. Lodash includes a large number of utility methods that can significantly bloat a bundle when fully included. By switching to named imports for only the used methods, or by leveraging modular alternatives such as lodash-es or per-method packages, this issue can be avoided and bundle size can be dramatically reduced. We’ve also explored this topic more broadly in our own guide on optimizing bundle size in Next.js. Check out the "Lodash Optimisation and Tree‑Shaking" section in our blog post: Optimizing Next.js performance: bundles, lazy loading, and images.
Bad example:
import _, { capitalize } from "lodash";
Good example:
import { isEqual, capitalize } from "lodash";
Selective imports allow effective tree-shaking, substantially reducing bundle size.
Excessive Use of "Import *" Statements (123 instances across 111 files)
Using import * as syntax disables tree-shaking in modern JavaScript build tools, such as Webpack or SWC, because the bundler cannot determine which parts of the library are actually used. As a result, the entire module - including all functions, utilities, and side-effect code - is included in the final bundle, regardless of what your application needs. This dramatically inflates the bundle size and slows down performance, especially on initial load. The issue becomes more pronounced when working with large UI libraries, analytics SDKs, or polyfill-heavy modules. We’ve also explored this topic more broadly in our own guide on optimizing bundle size in Next.js. Check out the "Avoiding Wildcard (import *) Imports" section in our blog post: Optimizing Next.js performance: bundles, lazy loading, and images. Major affected libraries in Polymarket's case include:
- @sentry/nextjs: 797 kB
- @radix-ui component libraries: 714 kB
- @amplitude analytics libraries: 569 kB
- react-phone-number-input: 142 kB
- yup: 65 kB
Typical examples:
import * as Sentry from "@sentry/nextjs";
import * as LabelPrimitive from "@radix-ui/react-label";
import * as HoverCardPrimitive from "@radix-ui/react-hover-card";
import * as amplitudeClient from "@amplitude/analytics-browser";
import * as amplitudeServer from "@amplitude/analytics-node";
import * as RPNInput from "react-phone-number-input";
import * as yup from "yup";
Use of Barrel Files
Polymarket utilized 16 barrel files, which hinder tree-shaking and unnecessarily enlarge bundles. Barrel files are modules that re-export several components or functions from different parts of the codebase using a single export * from syntax. While they can simplify import paths and improve code organization, they come at the cost of tree-shaking efficiency. Since bundlers can't determine which specific exports are used when everything is exported, the entire contents of those modules usually get included in the final bundle - even if only one utility is actually needed. This results in larger JavaScript payloads and slower page loads, especially for apps with extensive shared utilities or component libraries. We’ve also explored this topic more broadly in our own guide on barrel file optimizations: https://www.catchmetrics.io/blog/nextjs-bundle-size-improvements-optimize-your-performance. This is a sample barrel file from Polymarket.com:
export * from "./store";
export * from "./helpers";
export * from "./provider";
export * from "./selectors";
export * from "./types";
export * from "./adapters";
export * from "./init";
Noteworthy Libraries to Optimize
Polymarket.com uses a total of an estimated 277 third-party libraries, which is unfortunately not unusual for a modern web application, especially one with real-time features, complex UI components, and analytics integration. However, 43 of these libraries remain above 100 kB even after tree-shaking - highlighting potential opportunities for size reduction and performance gains. Large third-party dependencies are often the most substantial contributors to bundle bloat, particularly when they're used inconsistently or imported inefficiently. We’ve also explored this topic more broadly in our own guide on optimizing bundle size in Next.js. Check out the "Replacing Large Libraries with Smaller Alternatives" section in our blog post: Stop Shipping Bloat: Practical Bundle Diet for Next.js Developers. Below we highlight some of the most significant contributors, along with strategies for minimizing their impact on the bundle:
Viem (~350 kB minified) / @ethersproject (~900 kB minified):
- Viem and ethersproject libraries have a lot of overlap and these are the biggest libraries in your bundle. We would seriously consider trying to get rid of one or the other. Also, for some reason, the actual size of viem is significantly larger than it should be, as the bundle includes both the CommonJS (CJS) and ECMAScript Module (ESM) versions of the library. This type of duplication leads to unnecessary bloat. Without access to the source, it's hard to say definitively why this happens, but ensuring that only one module format is bundled would help reduce the payload considerably.
Sentry (~400 kB minified):
- Sentry's SDK for JavaScript is quite large, especially when used with Next.js, and it can quickly add hundreds of kilobytes to your bundle. Fortunately, the Sentry team provides a dedicated guide for enabling tree-shaking and minimizing its footprint in Next.js applications. By configuring Sentry according to their official recommendations - such as using @sentry/nextjs selectively, avoiding wildcard imports, and correctly setting the flags - you can significantly reduce its size in production builds. For detailed setup instructions, refer to the Sentry Docs
- We’ve also explored this topic more broadly in our own guide on optimizing bundle size in Next.js. Check out the "Configure third-party libraries for tree shaking" section in our blog post: Optimizing Next.js performance: bundles, lazy loading, and images.
Lodash (~70 kB minified):
- Only import per package which you’re doing in many places.
- Or use babel-plugin-import
- Per-method Lodash packages
- Or migrate to es-toolkit
- We’ve also explored this topic more broadly in our own guide on optimizing bundle size in Next.js. Check out the "Lodash Optimisation and Tree‑Shaking" section in our blog post: Optimizing Next.js performance: bundles, lazy loading, and images.
@radix-ui packages (~700 kB minified):
- Replace "import *" statements with selective component imports.
Recharts (~570 kB minified):
- Consider using a smaller alternative, especially since you’re already using lightweight-charts (~177 kB minified, ~70% smaller). So we would suggest fully migrating to lightweight-charts if possible.
- Alternative charting libraries that are even smaller:
- Chartist.js (~38 kB minified)
- uPlot (~51 kB minified)
@amplitude packages (~400 kB minified):
- Replace "import *" statements with selective component imports.
Animation Libraries (framer-motion ~185 kB / motion ~92 kB minified):
- Using multiple animation libraries simultaneously, such as framer-motion and motion, leads to unnecessary duplication and inflated bundle sizes. These libraries often offer overlapping functionality, so consolidating to a single one is both practical and impactful. If motion is preferred, it offers dedicated guidance for reducing bundle size through dynamic imports and selective usage: motion.dev.
- Or you could use a more lightweight alternative. For instance, react-spring offers similar animation capabilities with a significantly smaller size (~50 kB minified)
html2canvas (~197 kB minified):
- Dynamically load this library only when needed.
- We’ve also explored this topic more broadly in our own guide on optimizing bundle size in Next.js. Check out the "Lazy-Load and Code Split Heavy Modules" section in our blog post: Optimizing Next.js performance: bundles, lazy loading, and images.
date-fns (~77 kB minified):
- Consider using smaller alternatives like dayjs (~7 kB).
country-flag-icons (~350 kB minified):
- Consider using self-hosted SVGs instead to shrink the bundle size. The country-flag-icons package, while convenient, inflates your bundle size.
Next.js Page Data Size
Another often overlooked source of payload bloat in Next.js apps is the __NEXT_DATA__ script - an inline JSON blob that powers server-rendered or statically generated pages. In the case of Polymarket.com, this page-data weighs in at an astonishing 2.4 MB, which is nearly the entire HTML payload (2.5 MB total). This kind of data-heavy output can drastically delay time-to-interactive, especially on slower networks or mobile devices.
In the case of Polymarket.com, getStaticProps is used to prefetch a large number of queries via prefetchQuery and prefetchInfiniteQuery methods. While the implementation does include pruning of returned data using lists of allowed keys for objects like markets, events, and series, the total amount of structured content returned across queries remains extremely large. Even with pruning logic in place, the combination of many concurrent prefetches, deeply nested object graphs, and repetitive query patterns causes the resulting HTML to balloon in size. Because this JSON blob is injected into the page during SSR and parsed on the client, it directly impacts initial load and interactivity.
To address this:
- Audit all prefetched queries and reduce their number where possible.
- Move non-essential data fetching to the client after initial load.
- Break up or paginate large result sets.
- Ensure hydration strategies only include data immediately needed for above-the-fold content.
We break this down further in our blog post: Next.js Page Data Deep Dive and Performance Insights.
Additional Optimization Opportunities
While third-party libraries account for a significant portion of bundle size, there are a few other practical strategies Polymarket - and similar apps - can adopt to further enhance performance:
CDN Caching and Cache-Control Strategy
Another optimization opportunity lies in caching strategy. Polymarket.com currently returns the following cache-control header for its HTML documents:
cache-control: public, max-age=0, must-revalidate
- This effectively disables browser caching and revalidates the response on every request. While this ensures users always get the freshest content, it also means the full HTML response must be regenerated and re-downloaded frequently - even when the content hasn’t changed.
- Setting some cache duration or leveraging stale-while-revalidate strategies or introducing cache purging can significantly improve performance and reduce server load and can save a lot of infrastructure costs. Combined with modern CDN behavior, this can reduce latency and improve perceived responsiveness, particularly in high-traffic scenarios.
- We explore the impact of cache strategy on performance and infrastructure cost in more detail in our blog post: The Economics of Caching – The Million Dollar Question.
Automate Bundle Budgets in CI/CD
- Keeping bundle sizes under control is much easier when integrated directly into your development workflow. By setting up automated bundle budget checks in your CI/CD pipeline, you can ensure new code doesn’t introduce unexpected size regressions.
- We discuss this in more depth in the "Automate Bundle Budgets in CI/CD" section of our blog post: Optimizing Next.js performance: bundles, lazy loading, and images
Optimize Images with next/image
- We noticed that the site still uses regular_<img>_ tags in some places - even explicitly disabling ESLint rules such as @next/next/no-img-element to do so. This bypasses the powerful optimization features built into Next.js, including lazy loading, resizing, and responsive image delivery via modern formats like WebP.
- Migrating to the next/image component can reduce image payloads dramatically and improve LCP (Largest Contentful Paint) scores. Learn more in the "Optimize Images with next/image" section of our performance optimization guide.
Control Your Bundle Size with Catchmetrics
If you're concerned about your site's bundle sizes and want to ensure the best possible performance, reach out to us at Catch Metrics! We can help and give you a free health check of your NextJS site.
LEGAL NOTICE: This analysis constitutes fair use for educational and commentary purposes. All code snippets shown are for illustrative purposes only. Performance metrics and optimization recommendations are based on publicly available information and general best practices. Actual results may vary. No warranty or guarantee is provided regarding the accuracy or completeness of this analysis. Readers should conduct their own assessments before making technical decisions.