Skip to content

[docs-infra] Support turbopack for docs#48569

Open
brijeshb42 wants to merge 12 commits into
mui:masterfrom
brijeshb42:turbopack
Open

[docs-infra] Support turbopack for docs#48569
brijeshb42 wants to merge 12 commits into
mui:masterfrom
brijeshb42:turbopack

Conversation

@brijeshb42
Copy link
Copy Markdown
Contributor

@brijeshb42 brijeshb42 commented May 25, 2026

A followup to #48370

TLDR: Even though we gain on prod build times with turbopack, it affects the end user experience with increased bundle size and slightly impacted page perf.
So, we should use it on dev for better DX and use webpack on prod to not impact the end-user experience.

Impact of moving prod build to turbopack

Perf compare — webpack vs turbopack (deploy previews)

Both targets on Netlify Edge:

Methodology: Playwright Chromium headless, fresh context per run, CDP Network.setCacheDisabled, first-party only, domcontentloadedload → 300 ms, transferred bytes via Network.loadingFinished.encodedDataLength. 5 runs/page, median reported.

Summary

  • JS: webpack ships less by 3,034 kB (~33% smaller) across 10 pages. Per-page Δ ranges +18% to +74%; biggest deltas on simple pages (install, dark-mode, how-to-customize) where turbopack ships ~400 kB extra — points at worse common-chunk dedupe / finer per-route splitting.
  • CSS: webpack ships less by 6.7 kB (~7%). Constant ~700-byte delta on every page → fixed overhead, not content diff.
  • RUM: within bundler-noise floor. FCP mean Δ = −21 ms; LCP mean Δ = +121 ms but skewed by react-table (+1032 ms outlier, webpack LCP==FCP on that page suggests LCP candidate flickers between runs). Excluding table, LCP mean Δ ≈ +20 ms = noise. User-visible perf effectively no visible winner despite turbopack shipping more bytes — HTTP/2 + parallel fetch absorbs it on this network.
See Raw Data

JS bytes (median, first-party)

page webpack (kB) turbopack (kB) Δ%
/ 656.0 1058.2 +61.3%
/material-ui/getting-started/installation/ 545.7 948.6 +73.8%
/material-ui/getting-started/usage/ 544.3 685.1 +25.9%
/material-ui/react-button/ 565.2 672.4 +19.0%
/material-ui/react-autocomplete/ 817.1 1183.4 +44.8%
/material-ui/react-text-field/ 591.6 949.9 +60.6%
/material-ui/react-table/ 830.5 1186.9 +42.9%
/material-ui/customization/how-to-customize/ 560.1 969.9 +73.2%
/material-ui/customization/dark-mode/ 550.7 939.5 +70.6%
/material-ui/all-components/ 558.9 660.4 +18.2%
TOTAL 6220.1 9254.2 +48.8%

CSS bytes (median, first-party)

page webpack (kB) turbopack (kB) Δ%
/ 9.2 9.9 +7.5%
/material-ui/getting-started/installation/ 9.2 9.9 +7.4%
/material-ui/getting-started/usage/ 9.2 9.9 +7.4%
/material-ui/react-button/ 9.2 9.9 +7.3%
/material-ui/react-autocomplete/ 9.2 9.9 +7.4%
/material-ui/react-text-field/ 9.2 9.9 +7.2%
/material-ui/react-table/ 9.2 9.9 +7.4%
/material-ui/customization/how-to-customize/ 9.2 9.9 +6.9%
/material-ui/customization/dark-mode/ 9.2 9.9 +7.2%
/material-ui/all-components/ 9.2 9.9 +7.2%
TOTAL 92.3 99.0 +7.3%

Total bytes (median, first-party)

page webpack (kB) turbopack (kB) Δ%
/ 1377.3 1621.7 +17.7%
/material-ui/getting-started/installation/ 1086.9 1443.3 +32.8%
/material-ui/getting-started/usage/ 1087.0 1177.8 +8.4%
/material-ui/react-button/ 1320.7 1384.9 +4.9%
/material-ui/react-autocomplete/ 1392.8 1722.8 +23.7%
/material-ui/react-text-field/ 1177.5 1495.8 +27.0%
/material-ui/react-table/ 1382.0 1697.6 +22.8%
/material-ui/customization/how-to-customize/ 1138.0 1502.0 +32.0%
/material-ui/customization/dark-mode/ 1859.4 2211.0 +18.9%
/material-ui/all-components/ 2523.6 2577.2 +2.1%
TOTAL 14345.1 16834.2 +17.4%

FCP / LCP (median, ms)

page FCP wp FCP tp FCP Δms LCP wp LCP tp LCP Δms
/ 832 784 -48 832 784 -48
/material-ui/getting-started/installation/ 780 788 +8 1328 1348 +20
/material-ui/getting-started/usage/ 792 732 -60 1328 1340 +12
/material-ui/react-button/ 812 864 +52 1476 1536 +60
/material-ui/react-autocomplete/ 856 804 -52 1568 1568 0
/material-ui/react-text-field/ 804 836 +32 1484 1532 +48
/material-ui/react-table/ 852 816 -36 852 1884 +1032
/material-ui/customization/how-to-customize/ 776 796 +20 1404 1424 +20
/material-ui/customization/dark-mode/ 828 788 -40 828 788 -40
/material-ui/all-components/ 796 708 -88 1360 1468 +108

Gains on moving prod build to turbopack

Overall prod build on Netlfiy -

Previous - 5m45sec
New - 1m45sec

Bundler benchmark: webpack vs turbopack

  • Host: Brijeshs-MacBook-Pro.local (darwin arm64, 14 cores, 38.7 GB RAM)
  • Node: v24.14.0
  • Next.js: 16.2.6
  • Date: 2026-05-25
  • Runs per metric: 10 (HMR: + 2 warmup discarded)
  • Component page: /material-ui/react-button/
  • HMR edit target: docs/data/material/components/buttons/BasicButtons.js. First >WORD< JSX text child rewritten to a fresh marker each run (cascade, no reset between runs).
  • HMR timing: writeFile → first [Fast Refresh] done in Xms console event seen in a headless Chrome tab (Playwright).

Summary

Metric webpack (mean) turbopack (mean) turbopack vs webpack
Cold prod build 98.43 s 36.03 s 2.73× faster
Dev startup (process boot → Ready in) 408 ms 368 ms 1.11× faster
Home / cold first-load 5.68 s 4.77 s 1.19× faster
Component cold first-load 2.06 s 1.32 s 1.56× faster
HMR — driver wall clock (edit → event) 416 ms 282 ms 1.47× faster
HMR — bundler-reported (Fast Refresh done in Xms) 402 ms 111 ms 3.64× faster
See Raw Data

Cold prod build (next build)

bundler n mean stddev min median p95 max
webpack 10 98.43 s 4.06 s 93.40 s 97.25 s 108.51 s 108.51 s
turbopack 10 36.03 s 2.51 s 32.16 s 36.34 s 39.20 s 39.20 s

turbopack mean 2.73× faster than webpack.

raw samples (ms)
  • webpack: 97644, 96750, 93396, 101528, 108506, 97228, 96482, 97265, 97160, 98348
  • turbopack: 32157, 32827, 33746, 35170, 35528, 37154, 37905, 38132, 38482, 39204

Dev startup (process boot → Ready in)

bundler n mean stddev min median p95 max
webpack 10 408 ms 90 ms 350 ms 372 ms 642 ms 642 ms
turbopack 10 368 ms 15 ms 354 ms 367 ms 403 ms 403 ms

turbopack mean 1.11× faster than webpack.

raw samples (ms)
  • webpack: 642, 382, 464, 434, 356, 383, 359, 350, 362, 352
  • turbopack: 362, 403, 371, 358, 356, 382, 356, 371, 354, 371

Home / cold first-load

bundler n mean stddev min median p95 max
webpack 10 5.68 s 1.69 s 4.74 s 5.11 s 10.41 s 10.41 s
turbopack 10 4.77 s 151 ms 4.54 s 4.74 s 5.05 s 5.05 s

turbopack mean 1.19× faster than webpack.

raw samples (ms)
  • webpack: 10412, 5940, 5449, 4992, 5071, 4966, 5139, 4927, 4737, 5198
  • turbopack: 4728, 4873, 5054, 4847, 4695, 4542, 4882, 4753, 4594, 4704

Component /material-ui/react-button/ cold first-load

bundler n mean stddev min median p95 max
webpack 10 2.06 s 742 ms 1.68 s 1.74 s 3.85 s 3.85 s
turbopack 10 1.32 s 41 ms 1.27 s 1.32 s 1.39 s 1.39 s

turbopack mean 1.56× faster than webpack.

raw samples (ms)
  • webpack: 3849, 2973, 1775, 1749, 1680, 1685, 1726, 1710, 1690, 1764
  • turbopack: 1277, 1385, 1315, 1326, 1289, 1266, 1350, 1349, 1375, 1295

HMR — edit → [Fast Refresh] done (driver wall clock)

Real HMR via Playwright: edit demo file, observe the next [Fast Refresh] done console event in the open browser tab.

bundler n mean stddev min median p95 max
webpack 10 416 ms 48 ms 341 ms 421 ms 482 ms 482 ms
turbopack 10 282 ms 57 ms 212 ms 290 ms 358 ms 358 ms

turbopack mean 1.47× faster than webpack.

raw samples (ms)
  • webpack: 453, 396, 448, 382, 446, 366, 482, 381, 463, 341
  • turbopack: 358, 212, 319, 215, 328, 230, 336, 236, 329, 260

HMR — bundler-reported ([Fast Refresh] done in Xms)

Parses the Xms value printed by Next's Fast Refresh runtime — bundler-side recompile only, excludes WS round-trip and Playwright console plumbing.

bundler n mean stddev min median p95 max
webpack 10 402 ms 48 ms 327 ms 408 ms 469 ms 469 ms
turbopack 10 111 ms 75 ms 71 ms 87 ms 320 ms 320 ms

turbopack mean 3.64× faster than webpack.

raw samples (ms)
  • webpack: 440, 382, 435, 369, 433, 353, 469, 366, 449, 327
  • turbopack: 99, 74, 71, 77, 82, 91, 320, 99, 71, 121

@brijeshb42 brijeshb42 added the scope: docs-infra Involves the docs-infra product (https://www.notion.so/mui-org/b9f676062eb94747b6768209f7751305). label May 25, 2026
@code-infra-dashboard
Copy link
Copy Markdown

code-infra-dashboard Bot commented May 25, 2026

Deploy preview

https://deploy-preview-48569--material-ui.netlify.app/

Bundle size

Bundle Parsed size Gzip size
@mui/material 0B(0.00%) 0B(0.00%)
@mui/lab 0B(0.00%) 0B(0.00%)
@mui/private-theming 0B(0.00%) 0B(0.00%)
@mui/system 0B(0.00%) 0B(0.00%)
@mui/utils 0B(0.00%) 0B(0.00%)

Details of bundle changes


Check out the code infra dashboard for more information about this PR.

@brijeshb42 brijeshb42 force-pushed the turbopack branch 5 times, most recently from 7c7ac52 to 834b7c7 Compare May 26, 2026 11:32
@zannager zannager requested a review from Janpot May 26, 2026 14:53
Copy link
Copy Markdown
Member

@Janpot Janpot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are there any changes to the bundles?

@github-actions github-actions Bot added the PR: out-of-date The pull request has merge conflicts and can't be merged. label May 27, 2026
@github-actions github-actions Bot removed the PR: out-of-date The pull request has merge conflicts and can't be merged. label May 27, 2026
brijeshb42 and others added 12 commits May 28, 2026 17:35
…ranslation

Collapse the helper to operate on a single raw translation object. The
caller no longer wraps in a require.context shim or a per-language map;
it just passes the imported English JSON. The function mutates and
returns the translation with `componentDescription` rendered to HTML
and `componentDescriptionToc` populated.

Breaking change for downstream consumers (mui-x). Coordinate the
@mui/internal-core-docs publish with PR 3+4 once mui-x has migrated.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Update the ComponentApiBuilder template to statically import the
English translation JSON and call mapApiPageTranslation directly. The
{ en: ... } wrapper is constructed at the call site since ApiPage still
indexes descriptions by language. Webpack's require.context is gone and
the runtime markdown renderer stays out of the API page client bundle
because Next strips the import along with getStaticProps.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Host: Brijeshs-MacBook-Pro.local (darwin arm64, 14 cores, 38.7 GB RAM)
- Node: v24.14.0
- Next.js: 16.2.6
- Date: 2026-05-25
- Runs per metric: 10 (HMR: + 2 warmup discarded)
- Component page: `/material-ui/react-button/`
- HMR edit target: `docs/data/material/components/buttons/BasicButtons.js`. First `>WORD<` JSX text child rewritten to a fresh marker each run (cascade, no reset between runs).
- HMR timing: `writeFile` → first `[Fast Refresh] done in Xms` console event seen in a headless Chrome tab (Playwright).

| Metric | webpack (mean) | turbopack (mean) | turbopack vs webpack |
|---|---:|---:|---:|
| Cold prod build | 98.43 s | 36.03 s | **2.73× faster** |
| Dev startup (process boot → `Ready in`) | 408 ms | 368 ms | 1.11× faster |
| Home `/` cold first-load | 5.68 s | 4.77 s | 1.19× faster |
| Component cold first-load | 2.06 s | 1.32 s | **1.56× faster** |
| HMR — driver wall clock (edit → event) | 416 ms | 282 ms | **1.47× faster** |
| HMR — bundler-reported (`Fast Refresh done in Xms`) | 402 ms | 111 ms | **3.64× faster** |

| bundler | n | mean | stddev | min | median | p95 | max |
|---|---:|---:|---:|---:|---:|---:|---:|
| webpack | 10 | 98.43 s | 4.06 s | 93.40 s | 97.25 s | 108.51 s | 108.51 s |
| turbopack | 10 | 36.03 s | 2.51 s | 32.16 s | 36.34 s | 39.20 s | 39.20 s |

_turbopack mean **2.73× faster** than webpack._

<details><summary>raw samples (ms)</summary>

- **webpack**: `97644, 96750, 93396, 101528, 108506, 97228, 96482, 97265, 97160, 98348`
- **turbopack**: `32157, 32827, 33746, 35170, 35528, 37154, 37905, 38132, 38482, 39204`

</details>

| bundler | n | mean | stddev | min | median | p95 | max |
|---|---:|---:|---:|---:|---:|---:|---:|
| webpack | 10 | 408 ms | 90 ms | 350 ms | 372 ms | 642 ms | 642 ms |
| turbopack | 10 | 368 ms | 15 ms | 354 ms | 367 ms | 403 ms | 403 ms |

_turbopack mean **1.11× faster** than webpack._

<details><summary>raw samples (ms)</summary>

- **webpack**: `642, 382, 464, 434, 356, 383, 359, 350, 362, 352`
- **turbopack**: `362, 403, 371, 358, 356, 382, 356, 371, 354, 371`

</details>

| bundler | n | mean | stddev | min | median | p95 | max |
|---|---:|---:|---:|---:|---:|---:|---:|
| webpack | 10 | 5.68 s | 1.69 s | 4.74 s | 5.11 s | 10.41 s | 10.41 s |
| turbopack | 10 | 4.77 s | 151 ms | 4.54 s | 4.74 s | 5.05 s | 5.05 s |

_turbopack mean **1.19× faster** than webpack._

<details><summary>raw samples (ms)</summary>

- **webpack**: `10412, 5940, 5449, 4992, 5071, 4966, 5139, 4927, 4737, 5198`
- **turbopack**: `4728, 4873, 5054, 4847, 4695, 4542, 4882, 4753, 4594, 4704`

</details>

| bundler | n | mean | stddev | min | median | p95 | max |
|---|---:|---:|---:|---:|---:|---:|---:|
| webpack | 10 | 2.06 s | 742 ms | 1.68 s | 1.74 s | 3.85 s | 3.85 s |
| turbopack | 10 | 1.32 s | 41 ms | 1.27 s | 1.32 s | 1.39 s | 1.39 s |

_turbopack mean **1.56× faster** than webpack._

<details><summary>raw samples (ms)</summary>

- **webpack**: `3849, 2973, 1775, 1749, 1680, 1685, 1726, 1710, 1690, 1764`
- **turbopack**: `1277, 1385, 1315, 1326, 1289, 1266, 1350, 1349, 1375, 1295`

</details>

Real HMR via Playwright: edit demo file, observe the next `[Fast Refresh] done` console event in the open browser tab.

| bundler | n | mean | stddev | min | median | p95 | max |
|---|---:|---:|---:|---:|---:|---:|---:|
| webpack | 10 | 416 ms | 48 ms | 341 ms | 421 ms | 482 ms | 482 ms |
| turbopack | 10 | 282 ms | 57 ms | 212 ms | 290 ms | 358 ms | 358 ms |

_turbopack mean **1.47× faster** than webpack._

<details><summary>raw samples (ms)</summary>

- **webpack**: `453, 396, 448, 382, 446, 366, 482, 381, 463, 341`
- **turbopack**: `358, 212, 319, 215, 328, 230, 336, 236, 329, 260`

</details>

Parses the `Xms` value printed by Next's Fast Refresh runtime — bundler-side recompile only, excludes WS round-trip and Playwright console plumbing.

| bundler | n | mean | stddev | min | median | p95 | max |
|---|---:|---:|---:|---:|---:|---:|---:|
| webpack | 10 | 402 ms | 48 ms | 327 ms | 408 ms | 469 ms | 469 ms |
| turbopack | 10 | 111 ms | 75 ms | 71 ms | 87 ms | 320 ms | 320 ms |

_turbopack mean **3.64× faster** than webpack._

<details><summary>raw samples (ms)</summary>

- **webpack**: `440, 382, 435, 369, 433, 353, 469, 366, 449, 327`
- **turbopack**: `99, 74, 71, 77, 82, 91, 320, 99, 71, 121`

</details>
Since the built docs' page loads more js chunks for turbopack, it'll be
better for us to use turbopack for dev and webpack for prod builds
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

scope: docs-infra Involves the docs-infra product (https://www.notion.so/mui-org/b9f676062eb94747b6768209f7751305).

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants