# Bundle size

What Comvi i18n weighs, measured through the published exports map: the gated fixture behind every figure and what each capability adds.

Canonical: https://comvi.io/docs/i18n/bundle-size

What each piece of Comvi i18n weighs, and which import adds it.

Figures are min+gz, produced by `pnpm size` and measured through each package's **published exports map** rather than a `dist/` path, so what is measured is what an installed app resolves. A framework's own runtime is left out, so a framework row is the Comvi code only.

CI re-measures every row against its recorded baseline with 5% of headroom and fails when a row exceeds that budget, so a regression inside the margin passes.

## What it weighs

| app shape | measured as | min+gz |
| --- | --- | --- |
| `@comvi/core` alone, nothing composed | `core-base` | 5270 B |
| `@comvi/core` + ICU | `core-base-icu` | 6502 B |
| every core capability composed | `core-full-composite` | 9389 B |
| `@comvi/react`, bare instance + bindings | `fw-react-default` | 7369 B |
| `@comvi/react`, every capability composed | `fw-react-full-composite` | 12262 B |
| `@comvi/react` + ICU + the CDN loader | `platform-path-react` | 11353 B |
| core + ICU + the CDN loader | `plugin-fetch-loader` | 9692 B |

Every figure names its fixture, and each fixture is a gated row in `scripts/size-budgets.json` in the comvi-js repository. Rows one and two give the price of ICU, 1232 B, which is why it is the `compiler` constructor option rather than a default. Rows one and four give the price of the React bindings over the core floor, 2099 B. Rows four and five bracket a React app: the bindings alone against everything composed.

`core-full-composite` recomposes `/tags`, `/icu`, `/loader`, `/plugins` and `/devtools`.

## The two loader entries

The root entry of `@comvi/plugin-fetch-loader` is the CDN request path on its own. `fetchLoader` from `@comvi/plugin-fetch-loader/api` chooses between the CDN and the API at runtime, so it carries both request paths whether or not you reach them.

| graph | measured as | min+gz |
| --- | --- | --- |
| bare instance, ICU, and the `/api` entry | `plugin-fetch-loader-api` | 11711 B |
| bare instance, ICU, and the root entry | `plugin-fetch-loader` | 9692 B |

An app that only reads a public bundle swaps `fetchLoader` for `cdnLoader` and drops 2019 B with the API path. Keep `fetchLoader` wherever the i18n instance has an `apiKey`. See [CDN mode vs API mode](/docs/i18n/plugins/fetch-loader#cdn-mode-vs-api-mode).

The import-map adapter behind `.use(loader(map))` is in your graph only when you hand over a map: `attachLoader` does not name it, so an instance that registers a plain loader function never carries it.

> **Two things the figures are not**
>
> They are not comparable with a third-party size service: a different bundler, a different entry and a different set of externals produce a different number for the same package. And they are not a claim about anyone else's library: the figures measure Comvi only.

## Where the weight comes from

Every capability is an import you add, so the floor is the bare instance and each capability is a line in your own composition.

- Nothing arrives switched off. An absent capability's module never entered the bundle, which is why a missing one is a TypeScript error rather than a runtime no-op.
- No `eval` and no `new Function` in any runtime package, so everything runs under a strict CSP without `unsafe-eval`.
- `@comvi/core` has zero runtime dependencies.
- The `unpkg` / `jsdelivr` global build is the one exception and still carries everything: a `<script src>` consumer has no import graph to extend.

## Read next

- [`.use(installer)`](/docs/i18n/api/core#useinstaller): the i18n instance, and what each installer adds to it.
- [Fetch loader](/docs/i18n/plugins/fetch-loader): why the root entry carries two request paths, and when to use which.
- [Pluralize with ICU](/docs/i18n/pluralization): what the 1232 B buys: plural, select and selectordinal per framework.
