product icon

Paraglide JS

App

Benchmark

This benchmark compares the transfer size of Paraglide JS (a compiler-based i18n library) against i18next (a runtime-based library).

Key Takeaways

Paraglide ships 3-10x smaller bundles than runtime libraries

In typical scenarios, Paraglide ships 47-144 KB vs i18next's 205-422 KB.

ScenarioParaglidei18nextDifference
5 locales, 100 msgs used, 200 total47 KB205 KB4.4x smaller
10 locales, 100 msgs used, 200 total76 KB259 KB3.4x smaller
10 locales, 200 msgs used, 500 total144 KB422 KB2.9x smaller

Tree-shaking makes Paraglide immune to total message count

Runtime libraries ship all messages in a namespace, even unused ones. Paraglide only ships messages you actually use on a page.

Total messagesMessages usedParaglidei18next
20010047 KB205 KB
50010047 KB283 KB
100010047 KB414 KB

Paraglide's bundle size stays constant regardless of how many messages exist in your project—unused messages are tree-shaken away at build time.

Runtime libraries like i18next rely on manual namespacing to reduce bundle size, but namespaces still ship all their messages whether used or not. This means developers must carefully split messages across namespaces, and even then, unused messages within a namespace are still shipped.

Why Paraglide ships smaller bundles

AspectParaglide (Compiler)i18next (Runtime)
Unused messagesTree-shaken awayShipped in bundle
Runtime overhead~300 bytes~8-10 KB
Message formatCompiled to functionsJSON dictionary

For a feature comparison, see the comparison table.

Interactive Benchmark

Explore the full benchmark data with different configurations:

Methodology

What is Being Tested

The benchmark creates a static website for each configuration (library variant, number of locales, messages per page, and namespace size). Each website is loaded in a headless browser, and the total transfer size is measured.

  • Number of Locales: How does an i18n library scale with the number of locales?
  • Number of used Messages: How does an i18n library scale with the number of messages that are used on a given page?
  • Library Implementation Variants: Testing different implementation approaches:
    • Paraglide:
      • default: Standard implementation
      • experimental-: Experimental implementation with per-locale splitting
    • i18next:
      • http-backend: Using HTTP backend for loading translations
  • Namespace Size: Testing how the total available messages in a namespace affects bundle size

Library modes

Each library is tested in different modes:

  • Paraglide:
    • default: Out of the box Paraglide JS with no additional compiler options.
    • : Mode with a compiler option that is being tested.
  • i18next:
    • default: The default i18next implementation source.
    • http-backend: i18next implementation using HTTP backend for loading translations on demand source.

Limitations

Choosing the number of messages and namespace varies between projects

Some teams use per component namespacing while other teams have one namespace for their entire project. In cal.com's case, every component that uses i18n loads at least 3000 messages per locale.

To the point of the problem: Avoiding manual chunking of messages into namespaces is the benefit of Paraglide JS. The bundler tree-shakes all unused messages, making namespaces redundant.

Contributing

Contributions to improve the benchmark are welcome.

  1. adjust the build matrix in build.config.ts
  2. run pnpm run bench to build the benchmark
  3. run pnpm run preview to preview the results