npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

h3-compression

v1.1.0

Published

Adds compression to h3 request (brotli, gzip, deflate, zstd)

Readme

H3-compression

npm version npm downloads bundle JSDocs License

Handles compression for H3

Features

✔️  Zlib Compression: You can use zlib compression (brotli, gzip, deflate and opt-in zstd)

✔️  Stream Compression: You can use stream compressions (gzip, deflate and opt-in brotli / zstd)

✔️  Compression Detection: It uses the best compression which is accepted

✔️  h3 v1 & v2: Works with both h3 v1 and v2

✔️  Nuxt module: Drop h3-compression/nuxt into nuxt.config and configure it there

Install

# Using npm
npm install h3-compression

# Using yarn
yarn add h3-compression

# Using pnpm
pnpm add h3-compression

Usage (h3 v1)

import { createServer } from 'node:http'
import { createApp, eventHandler, toNodeListener } from 'h3'
import { useCompressionStream } from 'h3-compression'

const app = createApp({ onBeforeResponse: useCompressionStream }) // or { onBeforeResponse: useCompression }
app.use(
  '/',
  eventHandler(() => 'Hello world!'),
)

createServer(toNodeListener(app)).listen(process.env.PORT || 3000)

Example using listhen for an elegant listener:

import { createApp, eventHandler, toNodeListener } from 'h3'
import { listen } from 'listhen'
import { useCompressionStream } from 'h3-compression'

const app = createApp({ onBeforeResponse: useCompressionStream }) // or { onBeforeResponse: useCompression }
app.use(
  '/',
  eventHandler(() => 'Hello world!'),
)

listen(toNodeListener(app))

Usage (h3 v2)

In h3 v2 the response is an immutable web Response and the onBeforeResponse hook was removed. Use the compression / compressionStream middleware instead — they read the response returned by the next handler and replace it with a compressed one.

import { createServer } from 'node:http'
import { H3, toNodeHandler } from 'h3'
import { compression } from 'h3-compression'

const app = new H3()

app.use(compression()) // or app.use(compressionStream())
app.get('/', () => 'Hello world!')

createServer(toNodeHandler(app)).listen(process.env.PORT || 3000)

You can also force a specific method (e.g. compression('gzip')) instead of detecting it from the Accept-Encoding header.

Brotli and stream compression

The native CompressionStream implements the WHATWG CompressionFormat enum, which only defines gzip, deflate and deflate-raw — there is no brotli format. This package therefore streams brotli through node:zlib instead, so it is available wherever node:zlib is (Node, and runtimes with node compatibility), but not on pure edge runtimes.

Because brotli is noticeably more CPU-expensive per request than gzip, it is never picked automatically. Turn it on with the brotli flag, or force it as the method:

app.use(compressionStream()) // gzip / deflate — unchanged default
app.use(compressionStream({ brotli: true })) // brotli, then gzip, then deflate
app.use(compressionStream('br')) // always brotli

The same flag works for the composable:

await useCompressionStream(event, response, { brotli: true })
// or explicitly
await useBrotliCompressionStream(event, response)

[!NOTE] The brotli stream is flushed per chunk (BROTLI_OPERATION_FLUSH) so that streamed responses stay streamed. With zlib's defaults brotli buffers the whole body until the source closes.

Zstd

Zstd is supported on both paths, and is opt-in for a different reason than brotli: node:zlib only gained zstd in Node 22.15.0 (and 23.8.0). Enabling it by default would make the negotiated Content-Encoding depend on the Node version the app happens to run on, which is a poor thing to discover in production. The package itself only requires Node >= 20.11.1, the same floor as h3.

app.use(compression({ zstd: true })) // zstd, then brotli, then gzip, then deflate
app.use(compressionStream({ zstd: true, brotli: true })) // same order, streamed

await useCompression(event, response, { zstd: true })

Behaviour on a runtime without zstd:

  • with the zstd: true flag, zstd is skipped during negotiation and the next accepted encoding is used — no error, no special-casing needed in your code
  • when forced (compression('zstd'), useZstdCompression), a TypeError naming the required Node version is thrown, because silently sending something else would be worse

Branch on it yourself with the exported predicate:

import { isZstdSupported } from 'h3-compression'

app.use(compression({ zstd: isZstdSupported() }))

Nuxt 3 & 4

Add the module and you're done — it wires the right Nitro hooks, skips Nuxt's internal routes and filters by content type for you:

// nuxt.config.ts
export default defineNuxtConfig({
  modules: ['h3-compression/nuxt'],
})

Everything is configurable under the compression key:

export default defineNuxtConfig({
  modules: ['h3-compression/nuxt'],
  compression: {
    enabled: true,
    encoding: 'zlib', // or 'stream'
    brotli: false, // stream path only — zlib always prefers brotli
    zstd: false, // needs Node >= 22.15
    method: undefined, // force one method instead of negotiating
    contentTypes: ['text/', 'application/json', 'application/javascript', 'application/xml', 'image/svg+xml'],
    exclude: ['/_nuxt', '/__nuxt'],
    routeRules: true, // also compress cached (swr/isr) routes and /server/api
    threshold: 0, // skip bodies smaller than this many bytes
  },
})

| Option | Default | What it does | | --- | --- | --- | | enabled | true | Turn compression off without removing the module | | encoding | 'zlib' | 'zlib' buffers the body; 'stream' pipes it through a compression transform | | brotli | false | Consider brotli when negotiating. Only meaningful for 'stream' — the zlib path already prefers brotli | | zstd | false | Consider zstd when negotiating. Ignored on Node < 22.15, see Zstd | | method | – | Force one method instead of negotiating from Accept-Encoding | | contentTypes | text, JSON, JS, XML, SVG | Prefix match against Content-Type. Set to [] to compress everything | | exclude | ['/_nuxt', '/__nuxt'] | Path prefixes to skip. Compressing these breaks Nuxt's error page | | routeRules | true | Also attach to beforeResponse, which is what cached (swr/isr) routes and /server/api handlers go through | | threshold | 0 | Skip bodies below this size — under roughly a kilobyte compression makes payloads larger. Ignored for 'stream', where the size is not known up front |

[!NOTE] contentTypes and exclude replace the defaults rather than extending them. Spread the defaults in if you want to add to them.

Doing it manually

The module is a convenience wrapper — the hooks are still yours to wire if you want different behaviour per route:

server/plugins/compression.ts

import { useCompression } from 'h3-compression'

export default defineNitroPlugin((nitro) => {
  // Freshly rendered SSR pages.
  nitro.hooks.hook('render:response', async (response, { event }) => {
    // Skip internal nuxt routes (e.g. error page)
    if (['/_nuxt', '/__nuxt'].some(prefix => getRequestURL(event).pathname.startsWith(prefix)))
      return

    if (!response.headers?.['content-type']?.startsWith('text/html'))
      return

    await useCompression(event, response)
  })

  // The `render:response` hook only runs for freshly rendered SSR pages.
  // Responses served from the Nitro route cache (`routeRules` with `swr` / `isr`)
  // and `/server/api` handlers go through `beforeResponse` instead.
  nitro.hooks.hook('beforeResponse', async (event, response) => {
    if (['/_nuxt', '/__nuxt'].some(prefix => event.path.startsWith(prefix)))
      return

    await useCompression(event, response)
  })
})

useCompression compresses string, Buffer/Uint8Array and JSON (object) bodies and skips everything else (e.g. streams), so binary assets are left untouched. If you only want to compress specific content types, guard on response.headers?.['content-type'] before calling it.

Utilities

H3-compression has a concept of composable utilities that accept event (from eventHandler((event) => {})) as their first argument and response as their second.

Zlib Compression

  • useGZipCompression(event, response)
  • useDeflateCompression(event, response)
  • useBrotliCompression(event, response)
  • useZstdCompression(event, response)  – requires Node >= 22.15
  • useCompression(event, response, options?)  – pass { zstd: true } to include zstd

Stream Compression

  • useGZipCompressionStream(event, response)
  • useDeflateCompressionStream(event, response)
  • useBrotliCompressionStream(event, response)
  • useZstdCompressionStream(event, response)  – requires Node >= 22.15
  • useCompressionStream(event, response, options?)  – pass { brotli: true } / { zstd: true }

Middleware (h3 v2)

  • compression(method | options?)  – middleware using zlib (brotli, gzip, deflate, opt-in zstd)
  • compressionStream(method | options?)  – stream middleware (gzip, deflate, opt-in brotli / zstd)
  • compressResponse(event, value, method?, options?)  – low-level helper returning a compressed Response
  • compressResponseStream(event, value, method?, options?)  – low-level stream helper returning a compressed Response
  • isZstdSupported()  – whether the runtime can compress with zstd

Nuxt

  • h3-compression/nuxt  – the Nuxt module, configured under the compression key

Sponsors

Releated Projects

License

MIT License © 2023-PRESENT Gregor Becker