Key takeaways

  • Count UTF-8 bytes before compression, not JavaScript string characters.
  • Gzip size estimates transfer size for one exact artifact and configuration.
  • Measure built route chunks and loading behavior, not only source snippets.
  • Compression size is not a complete performance score.

A gzip measurement answers a precise question: how many bytes does this exact UTF-8 input occupy after gzip compression by this implementation? That is useful, but it is not automatically the size a user downloads and it is not a prediction of page speed. Delivery headers, chunking, caches, browser support, and the route’s loading sequence determine the real experience.

The JavaScript Gzip Size Calculator uses exact encoded bytes and browser compression support for a focused comparison. For release work, measure the production artifacts generated by the bundler and confirm the Content-Encoding of actual deployed responses.

Characters are not bytes

JavaScript string length counts UTF-16 code units. Network text is commonly UTF-8. ASCII source often has matching character and byte counts, but comments, string literals, identifiers, and embedded data can contain characters that require multiple UTF-8 bytes. Emoji may occupy two UTF-16 code units and four UTF-8 bytes.

TextEncoder.encode returns a Uint8Array containing the UTF-8 representation. Its byteLength is the meaningful uncompressed byte count for a browser measurement. Counting source.length is only safe when you have proved the input is restricted to one-byte ASCII, and even then an explicit byte measurement communicates intent better.

The gzip result includes format headers and checksums, so extremely tiny samples can appear to compress poorly or even grow. Compression becomes representative on realistic artifacts with repeated syntax, names, and strings.

A measurement example

const source = "export const greeting = 'Hello, 世界 👋';";
const bytes = new TextEncoder().encode(source);
const stream = new Blob([bytes]).stream().pipeThrough(new CompressionStream("gzip"));
const gzipBytes = await new Response(stream).arrayBuffer();

console.log({ utf8: bytes.byteLength, gzip: gzipBytes.byteLength });

This gives a deterministic result for the browser’s compression implementation and exact input. A build system or server compressor may choose different levels, headers, or dictionaries and produce a slightly different gzip file while remaining standards-compatible.

A practical size-audit workflow

  1. Build for production. Development bundles contain diagnostics, source structure, and runtime helpers not representative of release output.
  2. List route artifacts. Identify entry chunks, shared chunks, lazy chunks, styles, and data requested for the target page.
  3. Measure raw UTF-8 bytes. Preserve the exact emitted bytes rather than reconstructing text through a different newline or encoding path.
  4. Measure gzip and Brotli separately. Label the algorithms and settings. Do not present the smallest of several encodings without saying which one it is.
  5. Map bytes to loading phases. Separate initial navigation, post-interaction lazy loading, prefetching, and background work.
  6. Inspect duplication. Two routes may each look acceptable while shipping duplicate library versions or repeated helpers.
  7. Check live headers. Verify Content-Encoding, Vary: Accept-Encoding, cache-control, content type, and immutable filenames on deployed responses.
  8. Pair size with user metrics. Evaluate loading, parsing, execution, and interaction on representative devices and network conditions.

Why gzip likes JavaScript

JavaScript repeats keywords, punctuation, import paths, property names, and common expression shapes. Gzip refers back to repeated byte sequences instead of storing each occurrence in full. Minification removes whitespace and shortens names, reducing raw bytes, while sometimes removing repetition that gzip would have compressed efficiently. This is why raw and compressed reductions do not move in identical percentages.

Compression works across the contents of one response, not across every JavaScript file a page loads. Splitting code into many tiny chunks can reduce the repetition available within each response and add headers and scheduling overhead. Bundlers balance caching and lazy loading against those costs.

Brotli often produces smaller static text assets at high quality settings, while gzip remains widely supported and fast. Servers normally choose an encoding through Accept-Encoding negotiation. Precompress fingerprinted assets during deployment when build time is acceptable, rather than spending CPU recompressing unchanged files per request.

Transfer size is not execution cost

A compact library can perform expensive initialization. A larger data table may parse quickly and replace repeated network calls. A lazy chunk may have zero first-load cost but introduce a delay when a user opens a feature. Size budgets should align with product moments rather than becoming one global number.

Browser developer tools distinguish resource size, transferred size, and cached responses. A disk-cache hit may transfer almost nothing but still require parsing or execution in a new page context. Service workers and prefetching can change timing further. Record the test conditions whenever sharing a measurement.

Source maps usually should not be fetched by normal users, though they may be uploaded to monitoring or available to developer tools. Do not include map bytes in the user’s initial JavaScript total unless the browser actually requests them. Do include runtime helpers and polyfills that are genuinely shipped for the target browser set.

Common mistakes

  • Using string length as UTF-8 byte size.
  • Compressing a development source file and calling it production transfer size.
  • Adding every lazy chunk to the initial route total.
  • Comparing gzip from one tool with Brotli from another without labeling them.
  • Ignoring response headers and CDN configuration.
  • Claiming a percentage size reduction produces the same performance improvement.
  • Measuring each module separately even though the bundler combines them.
  • Forgetting cache state and test device when comparing page loads.

Size review checklist

  • The artifact comes from a production build.
  • Raw size uses exact bytes, not source character count.
  • Gzip and Brotli values are labeled separately.
  • Initial, lazy, shared, and prefetched chunks are identified.
  • Duplicate packages and unexpectedly broad imports are investigated.
  • Live responses use the intended encoding and cache policy.
  • Source maps follow the organization’s observability and exposure policy.
  • Performance is tested on a representative slower device and network.
  • The report records build commit, tool version, and measurement conditions.

Frequently asked questions

Why is gzip larger than my tiny snippet?

The gzip container adds fixed overhead, and very short input has little repetition to exploit. Measure realistic files before judging compression effectiveness.

Should I report gzip or Brotli size?

Report both when your server serves both, and state the settings or tool. Brotli may better represent modern delivery for static assets, while gzip remains an important compatibility path.

Is bundle size the same as page weight?

No. Page weight includes styles, images, fonts, data, and other responses. JavaScript bundle reports also need to distinguish which chunks load for the route and when.

How accurate is browser CompressionStream?

It accurately compresses the supplied bytes to a standards-compatible gzip stream. Exact size may differ from a CDN or command-line compressor using different implementation choices, so verify production responses for deployment claims.

Further reading

Gzip size is valuable when its boundaries are explicit: exact bytes, exact artifact, exact encoding, and exact loading phase. Keep those labels attached and it becomes a reliable engineering signal instead of a vague performance promise.

Try the related tool

Apply the guide to a focused example, then verify the result in your own project.

Open the related tool →