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

@portable/image-size

v0.1.0

Published

Read an image's type and dimensions from its header bytes or a stream, in any JavaScript runtime.

Readme

@portable/image-size

Reads an image's type and dimensions from its header bytes or from a stream, reading only as far as the header. Runs in browsers, Cloudflare Workers, Node 24+, Bun, and Deno.

Replaces image-size.

npm install @portable/image-size
import * as ImageSize from '@portable/image-size';

const response = await fetch('https://example.com/photo.jpg');
const { width, height, type } = await ImageSize.parseStream(response.body!);
// { width: 1600, height: 1200, type: 'jpg', orientation: 1 }

parseStream stops reading and cancels the stream once it has the bytes that hold the dimensions. For a JPEG that means the segments up to its frame header (5 KB of the 730 KB large.jpg fixture), and for a PNG the first 24 bytes.

API

parse(bytes: Uint8Array): Size

Returns the size of the image in bytes. The bytes need to include only the header.

parseStream(stream: ReadableStream<Uint8Array>, options?): Promise<Size>

Reads stream until the size is known, then cancels it.

| Option | Default | Description | | --- | --- | --- | | maxBytes | 524288 | The most bytes that parseStream buffers. If the header isn't complete by then, parseStream rejects with MaxBytesError, which stores the limit as maxBytes. ICNS files list their icons throughout the file, so a large one needs a higher limit. A negative or NaN value throws a RangeError. | | signal | none | Aborting rejects with signal.reason and cancels the stream. |

Size

| Field | Description | | --- | --- | | width, height | Pixels. For a file with several images, the largest by area. | | type | bmp, cur, dds, gif, icns, ico, j2c, jp2, jpg, jxl (container), jxl-stream (bare codestream), ktx, ktx2, png, pnm, psd, svg, tga, tiff, bigtiff, or webp. HEIF files report their brand: avif, avis, heic, heix, hevc, hevx, mif1, or msf1. | | orientation | The EXIF orientation of a JPEG (1 to 8), when it has one. width and height are the stored dimensions, before rotation. | | images | Every image in an ICO, CUR, or ICNS file with more than one. ICNS entries also have their icon type, such as ic08. |

The size of an animated PNG is the canvas size, with type png. The size of a HEIF file is the size of its primary image, cropped by its clean aperture (clap) and rotated by its irot property.

Errors

| Error | Thrown when | | --- | --- | | UnsupportedError | The bytes don't match a supported format. | | TruncatedError | The input ends before the dimensions, including an empty input. | | InvalidInputError | The header is malformed, such as a PNG without an IHDR chunk or an SVG with neither size nor viewBox. | | MaxBytesError | parseStream read maxBytes without finding the dimensions. Its maxBytes holds the limit. |

Their names are ImageSize.UnsupportedError, ImageSize.TruncatedError, ImageSize.InvalidInputError, and ImageSize.MaxBytesError.

Types

| Type | Description | | --- | --- | | ImageSize.Size | What parse and parseStream return, described above. | | ImageSize.Type | The type values listed under Size. | | ImageSize.Image | { width, height, type? }, one entry of images for a file that holds several images. |

Why use this over image-size

parseStream reads a fetch body or a File only up to the header and then cancels it, in any JavaScript runtime. The package has no dependencies outside @portable/*.

  • parseStream reads 5 KB of the 730 KB large.jpg. Collecting the same stream and passing it to image-size was about 70× slower, and image-size's imageSizeFromFile reads a fixed 512 KiB whatever the format needs.
  • parse read large.jpg about 3,800× faster than image-size, and was 3.6 to 360× faster on the PNG, HEIC, WebP, SVG, and TIFF fixtures.
  • Input that ends early gives TruncatedError. image-size throws a RangeError from DataView.
  • HEIF sizes come from the primary item, with the clean aperture and rotation applied. image-size reports a 16×64 image as 63×64 and gives landscape dimensions for a portrait photo stored rotated.
  • parse reads lossless and arithmetic-coded JPEGs, which image-size rejects. There is no global disableTypes setting, so no other module in the process can turn off a format for you.
  • image-size throws a TypeError for an unsupported file type. parse throws UnsupportedError instead, and TruncatedError or InvalidInputError for a broken file, so if your catch checks for TypeError, check for these errors instead.
  • image-size skips the JPEG marker right after the start-of-image marker without reading it, so it misses a frame header there, and it assumes the Exif IFD0 starts 8 bytes into the TIFF header. parse reads that marker and follows the IFD0 offset the header stores.
  • image-size lists every HEIF ispe property in images, which for a tiled photo means every tile. parse returns no images list for HEIF files, and it detects animated AVIF (avis), which image-size doesn't.
  • image-size reads TIFF tags past the IFD's entry count until it finds a zero tag, so data after the IFD can overwrite the width and height, and it returns an undocumented compression field. parse reads only the entries the first IFD declares and returns no compression field.
  • image-size lists ICNS entries that aren't icons, such as the table of contents, and returns the icons found so far when the file is cut short. parse skips those entries and throws TruncatedError for a short file.
  • image-size splits an SVG viewBox on single spaces only, so it gives no size for 0,0,100,100. parse accepts commas and repeated spaces.
  • image-size needs a PNM file's width and height on one line, separated by one space, and returns NaN for dimensions that aren't numbers. parse reads them across lines with comments between them, and throws InvalidInputError for dimensions that aren't numbers.

Operations per second from pnpm bench, higher is better, in Node 24.22.0, on Intel(R) Xeon(R) Processor @ 2.10GHz (4 cores). Each benchmark lists only the libraries that can do the same work, fastest first. The relative speed compares each library with @portable/image-size, and the bars are scaled to the fastest library.

| Library | Version | Minified and gzipped | Runtime dependencies | Node built-ins | TypeScript types | | --- | --- | ---: | ---: | --- | --- | | @portable/image-size | – | 5.3 kB | 2 | none | included | | image-size | 2.0.4 | 4.6 kB | 0 | none | included |

Sizes cover the imports in the usage example, or the equivalent imports of each library, bundled for the browser by scripts/bundle-size.ts. Runtime dependencies count every package installed with the published package. A library that uses Node built-ins needs polyfills in a browser and the nodejs_compat flag in Cloudflare Workers.