Summary
The pre-built UI bundle that ships inside @teambit/ui (artifacts/ui-bundle/) is ~90 MB. It is
built twice — once per UI root (workspace and scope) — and the scope copy additionally carries a
45 MB SSR build. There is a lot of room to shrink this.
This matters more than it used to: the esbuild CLI-bundling work relies on shipping this artifact so
bit start can serve a pre-built UI instead of running rspack at startup. At that point the artifact
is a large fraction of the whole distribution (~90 MB of a ~320 MB bundled bit), and the single
biggest remaining size lever.
Measurements
Measured on bit-bundle3, artifact produced by the BundleUI build task:
| part |
size |
files |
ui-bundle/workspace (browser) |
22.6 MB |
39 |
ui-bundle/scope (browser) |
22.6 MB |
54 |
ui-bundle/scope/public/bit/ssr |
45.2 MB |
45 |
| total |
90.4 MB |
138 |
For comparison, the released 2.0.72 artifact is 58 MB (50 MB scope + 8.2 MB workspace), so this
has also grown — worth understanding separately.
Largest single file: scope/public/bit/ssr/index.js at 38 MB.
Two observations
1. workspace and scope are two full builds of the same app
Both are 22.6 MB browser bundles of the same Bit UI, differing only in the root aspect
(teambit.workspace/workspace vs teambit.scope/scope). BundleUiTask simply loops over both roots
and runs UiMain.build for each:
Object.values(UIROOT_ASPECT_IDS).map(async (uiRootAspectId) => {
const outputPath = join(capsule.path, BundleUiTask.getArtifactDirectory(uiRootAspectId));
await this.ui.build(uiRootAspectId, outputPath);
await this.generateHash(outputPath);
})
Only 25 files / 1.9 MB are byte-identical between them, so naive file-level dedup buys little —
the two builds chunk and content-hash independently even where the underlying modules are the same.
The duplication is logical rather than byte-level: React, the component-compare UI, monaco, the
graph/tree components etc. are compiled into both.
Possible directions:
- one rspack build with two entry points, sharing chunks via
splitChunks, so the common vendor and
UI chunks are emitted once;
- or a shared runtime/vendor chunk emitted to a common dir that both roots reference.
2. The 45 MB SSR build is only ever used by the scope root
setupServerSideRendering returns immediately unless buildOptions.ssr:
private async setupServerSideRendering({ root, port, app }) {
if (!this.buildOptions?.ssr) return;
...
}
ssr: true is set only on scope.ui-root.ts; workspace.ui-root.ts has ssr: false. So half the
artifact serves one root, and is never touched by bit start in a workspace — which is the common
case, and the only case a bundled CLI needs by default.
Worth asking:
- is SSR still required for the scope UI? If yes, can it be a separate optional artifact rather than
part of the package every install pulls down?
- 38 MB for a single
ssr/index.js looks high on its own and may be worth a metafile inspection
independently of the packaging question.
Reproduce
bit build "teambit.ui-foundation/ui" --tasks "BundleUI" --reuse-capsules --unmodified
# then, in the capsule printed as "build output can be found in path:"
du -sh teambit.ui-foundation_ui@*/artifacts/ui-bundle/{workspace,scope}
du -sh teambit.ui-foundation_ui@*/artifacts/ui-bundle/scope/public/bit/ssr
Related
Context on why the artifact is now shipped inside the CLI bundle, and the numbers above, are recorded
in bundle-plan.md §17 on the bit-bundle3 branch.
Summary
The pre-built UI bundle that ships inside
@teambit/ui(artifacts/ui-bundle/) is ~90 MB. It isbuilt twice — once per UI root (
workspaceandscope) — and thescopecopy additionally carries a45 MB SSR build. There is a lot of room to shrink this.
This matters more than it used to: the esbuild CLI-bundling work relies on shipping this artifact so
bit startcan serve a pre-built UI instead of running rspack at startup. At that point the artifactis a large fraction of the whole distribution (~90 MB of a ~320 MB bundled bit), and the single
biggest remaining size lever.
Measurements
Measured on
bit-bundle3, artifact produced by theBundleUIbuild task:ui-bundle/workspace(browser)ui-bundle/scope(browser)ui-bundle/scope/public/bit/ssrFor comparison, the released
2.0.72artifact is 58 MB (50 MBscope+ 8.2 MBworkspace), so thishas also grown — worth understanding separately.
Largest single file:
scope/public/bit/ssr/index.jsat 38 MB.Two observations
1.
workspaceandscopeare two full builds of the same appBoth are 22.6 MB browser bundles of the same Bit UI, differing only in the root aspect
(
teambit.workspace/workspacevsteambit.scope/scope).BundleUiTasksimply loops over both rootsand runs
UiMain.buildfor each:Only 25 files / 1.9 MB are byte-identical between them, so naive file-level dedup buys little —
the two builds chunk and content-hash independently even where the underlying modules are the same.
The duplication is logical rather than byte-level: React, the component-compare UI, monaco, the
graph/tree components etc. are compiled into both.
Possible directions:
splitChunks, so the common vendor andUI chunks are emitted once;
2. The 45 MB SSR build is only ever used by the scope root
setupServerSideRenderingreturns immediately unlessbuildOptions.ssr:ssr: trueis set only onscope.ui-root.ts;workspace.ui-root.tshasssr: false. So half theartifact serves one root, and is never touched by
bit startin a workspace — which is the commoncase, and the only case a bundled CLI needs by default.
Worth asking:
part of the package every install pulls down?
ssr/index.jslooks high on its own and may be worth ametafileinspectionindependently of the packaging question.
Reproduce
Related
Context on why the artifact is now shipped inside the CLI bundle, and the numbers above, are recorded
in
bundle-plan.md§17 on thebit-bundle3branch.