mrkeyoor.com_
Thu 06 Aug 02:44 UTC
npmWeb Frontendupdated 06 Aug 2026

image-size

image-size tells you the pixel width and height of an image without decoding it. You hand it a Buffer or Uint8Array and it reads only the header bytes, matches them against a table of twenty formats (PNG, JPEG, WebP, GIF, SVG, HEIC and AVIF, TIFF, PSD, ICO, and more), and returns {width, height, type}. For JPEGs it also returns the EXIF orientation number, and for multi-image containers like ICO, CUR, and HEIF it returns an images array with every size in the set. A separate entry point, image-size/fromFile, reads a file from disk asynchronously and does the same thing. There are no dependencies and no native build step, and it ships both ESM and CommonJS builds with TypeScript types.

Verdict

Still the fastest and widest way to get width and height out of a byte range, and the code is small enough that an archived repository is a survivable risk for a build script. For anything user-facing that ingests untrusted files, plan a migration path now, because the maintainer has publicly stepped back from GitHub.

API stability3/5Version 1 held steady for years, then version 2 in February 2025 removed the synchronous file API, dropped the default export in favour of named imageSize, and split file reading into an image-size/fromFile subpath. That is a full call-site rewrite, and 1.2.1 remains published under the legacy tag because of it.
Docs4/5The README covers buffers, files, the CLI, multi-size containers, EXIF orientation, disabling formats, and has an explicit Limitations section naming SVG percentage units, the concurrency cap, and TIFF's need for a full header. There is no API reference beyond it, and no migration guide from 1.x.
Maintenance1/5The repository is archived on GitHub as of June 2026, with the maintainer stating he will not accept issues or advisories there and pointing at Codeberg for any future work. The last release, 2.0.2, was April 2025. The code is short and dependency-free, which limits the damage, but treat it as frozen.
Ecosystem5/5Around 35.6M weekly downloads and a transitive dependency of large parts of the JavaScript build tooling world, so it is almost certainly already in your lockfile. Twenty supported formats, an npx CLI, and no dependencies of its own make it trivial to drop into any Node project.

Use it if

  • You need width and height from an upload before you store it, and you do not want to pull in an image processing library with a native binary just to read four bytes
  • You already have the bytes in memory from a stream, a fetch response, or a multipart parser: imageSize(buffer) is synchronous and returns in microseconds because it never touches the pixel data
  • You need to handle a wide format spread, including the ones most libraries skip: HEIC and AVIF from phones, ICO and CUR favicon sets, KTX textures, JPEG-XL, and SVG viewBox dimensions
  • You want the full set of sizes inside a multi-resolution file, which the images array gives you for ICO, CUR, and HEIF containers
  • You are generating width and height attributes to stop layout shift and want a build-time or request-time helper with a near-zero footprint
Skip it if

Setup reality

npm install image-size is genuinely all of it: zero dependencies, no postinstall, no native compilation, bundled .d.ts, and an exports map with proper ESM and CommonJS conditions. Node 16 or newer is required by the engines field. The friction is entirely the version 2 rewrite. In 1.x you wrote sizeOf('photo.jpg') against a default export and got a synchronous answer from disk; in 2.x that call does not exist. You now import {imageSize} from 'image-size' for buffers and {imageSizeFromFile} from 'image-size/fromFile' for paths, and the file version returns a promise. The README recommends reading the file yourself with fs.readFileSync and passing the buffer if you truly need synchronous behaviour. File reads are also throttled to 100 concurrent operations by default, adjustable with setConcurrency from the same subpath, which you will notice if you fan out over thousands of files. Both entry points throw rather than returning null for an unrecognised header, so every call needs a try/catch. Version 1.2.1 is still published under the legacy dist-tag if you cannot migrate yet.

Patterns

Get dimensions from bytes you already havemeasure-a-buffer

import {imageSize} from 'image-size';
// or: const {imageSize} = require('image-size');

const {width, height, type} = imageSize(buffer);
console.log(width, height, type); // 1920 1080 'jpg'

Synchronous and cheap because only the header is parsed. The argument must be a Buffer or Uint8Array; passing a string path here does not read a file, it throws.

Read dimensions from diskmeasure-a-file

import {imageSizeFromFile} from 'image-size/fromFile';

const dimensions = await imageSizeFromFile('photos/image.jpg');
console.log(dimensions.width, dimensions.height);

This lives in the image-size/fromFile subpath, not the main entry, and it returns a promise. Version 1.x's synchronous sizeOf('path') was removed in 2.0.0 and is not coming back.

Wrap every call, because it throwshandle-errors

import {imageSize} from 'image-size';

function safeSize(bytes) {
  try {
    return imageSize(bytes);
  } catch (err) {
    // 'unsupported file type: undefined' for unrecognised headers
    // 'disabled file type: png' if you called disableTypes
    return null;
  }
}

There is no null-returning variant. Unrecognised bytes, truncated buffers, and disabled formats all raise, so an unguarded call in a request handler is a 500 waiting for its first bad upload.

Keep synchronous behaviour by reading the file yourselfsynchronous-file-read

import {readFileSync} from 'node:fs';
import {imageSize} from 'image-size';

const buffer = readFileSync('photos/image.jpg');
const dimensions = imageSize(buffer);

The README calls this out as not recommended: readFileSync blocks the event loop and pulls the whole file into memory when only the first few kilobytes matter. Fine in a build script, wrong in a server.

List every size inside an ICO, CUR, or HEIF filemulti-size-containers

import {imageSizeFromFile} from 'image-size/fromFile';

const result = await imageSizeFromFile('images/favicon.ico');

console.log(result.width, result.height); // the LARGEST image in the set
for (const img of result.images ?? []) {
  console.log(img.width, img.height);      // 16x16, 32x32, 48x48, ...
}

The top-level width and height report the largest entry, not the first. The images array is only present for multi-image formats, so guard the access.

Swap width and height for rotated photosjpeg-orientation

const {width, height, orientation} = imageSize(jpegBuffer);

// EXIF orientations 5-8 mean the image is stored rotated 90 degrees
const rotated = orientation !== undefined && orientation >= 5;
const display = rotated
  ? {width: height, height: width}
  : {width, height};

orientation is the raw EXIF number 1 to 8 and is undefined when the JPEG has no EXIF block, which is common for images that have been through a stripping pipeline. The library does not apply the rotation for you.

Turn off formats you never expectlimit-formats

import {imageSize, disableTypes, types} from 'image-size';

console.log(types); // ['bmp','cur','dds','gif','heif',...]

// Only accept the web formats; everything else now throws.
disableTypes(types.filter((t) => !['jpg', 'png', 'webp', 'gif'].includes(t)));

disableTypes mutates module-level state for the whole process, so it is a startup-time call, not a per-request one. It also shrinks the parser surface exposed to untrusted bytes, which is the main reason to bother.

Size an image fetched over the networkmeasure-from-a-response

const res = await fetch('https://example.com/photo.jpg');
const bytes = new Uint8Array(await res.arrayBuffer());

const {width, height} = imageSize(bytes);

This downloads the entire file to read its header. If bandwidth matters, probe-image-size aborts the request once it has enough bytes; image-size has no streaming mode.

Raise or lower the file-read concurrency captune-concurrency

import {setConcurrency, imageSizeFromFile} from 'image-size/fromFile';

setConcurrency(500);

const sizes = await Promise.all(paths.map(imageSizeFromFile));

The default limit is 100 simultaneous file reads. Raising it helps on fast SSDs and hurts on network filesystems; lowering it is the fix if you are hitting EMFILE from open file descriptors elsewhere in the process.

Know what you get back from an SVGsvg-dimensions

const svg = Buffer.from('<svg width="100" height="50" viewBox="0 0 200 100"/>');
const {width, height, type} = imageSize(svg);
// 100 50 'svg'

// Percentage units are not supported:
// <svg width="100%" height="100%"> falls back to the viewBox, or throws.

Only pixel width and height attributes and the viewBox are read. An SVG sized purely in percentages or with CSS has no intrinsic dimensions to report, which is a documented limitation rather than a bug.

Check dimensions from the terminalcli-usage

npx image-size image1.jpg image2.png

# image1.jpg: 1920x1080
# image2.png: 512x512

The bin is installed with the package, so a local devDependency gives you it without npx. Handy in CI checks that assert asset sizes; it exits non-zero on an unreadable file.

Stay on 1.x if the v2 migration is not worth itpin-legacy-version

npm install image-size@legacy   # resolves to 1.2.1

// the old API still works there:
const sizeOf = require('image-size');
const dimensions = sizeOf('photos/image.jpg'); // sync, reads from disk

1.2.1 was published in April 2025, on the same day as 2.0.2, and is tagged legacy rather than deprecated. It receives nothing now that the repository is archived, so treat this as a delay, not a plan.

Alternatives

PackageRegistryPick it when
probe-image-sizenpmYou want to read dimensions from a URL or a readable stream and stop downloading as soon as the header arrives.
sharpnpmYou need real metadata, format conversion, or resizing, and can accept a native binary in your install.
image-dimensionsnpmYou want a modern ESM-only package that runs unchanged in browsers, workers, and Node against a stream or blob.
file-typenpmYou only need to know what format the bytes are, not how big the image is.