inei
EXIF and XMP metadata parser for JPEG, PNG, and HEIC/HEIF/AVIF. Written in TypeScript. Zero dependencies. Runs in browsers and Node.js.
Built as a modern replacement for exifr, which has been unmaintained since 2022.
Install
pnpm add inei
npm install inei
Quick start
import { parseExif } from "inei";
const buffer = await file.arrayBuffer();
const result = parseExif(buffer);
if (result.ok) {
const { image, tags, xmp } = result.data;
}
parseExif returns a discriminated union. On success, result.ok is true and result.data contains the parsed metadata. On failure, result.ok is false and result.error is an ExifError with a code and message.
type Result =
| { ok: true; data: ParsedExif }
| { ok: false; error: ExifError };
Format support
| JPEG | Yes | Yes (SOF) | Yes (APP1) | Yes |
| PNG | Yes (eXIf chunk) | Yes (IHDR) | Yes (iTXt) | Yes |
| HEIC/HEIF/AVIF | Yes (ISOBMFF) | Yes (ispe) | Yes (mime item) | Yes |
Format is auto-detected from magic bytes. You do not need to specify it.
API
parseExif(input, options?)
Synchronous. Accepts ArrayBuffer, Uint8Array, or Buffer.
import { parseExif } from "inei";
const result = parseExif(buffer);
parseExifFromBlob(input, options?)
Async. Accepts everything parseExif does, plus Blob and File. Use this in browsers when working with file inputs directly.
import { parseExifFromBlob } from "inei";
const input = document.querySelector("input[type=file]");
const file = input.files[0];
const result = await parseExifFromBlob(file);
gps(input)
Returns { latitude: number; longitude: number } or undefined.
import { gps } from "inei";
const coords = gps(buffer);
orientation(input)
Returns the EXIF orientation value (1-8) or undefined.
import { orientation } from "inei";
const o = orientation(buffer);
thumbnail(input)
Returns the embedded JPEG thumbnail as a Uint8Array, or undefined.
import { thumbnail } from "inei";
const bytes = thumbnail(buffer);
thumbnailUrl(input)
Browser only. Returns a blob: URL for the embedded thumbnail, or undefined.
import { thumbnailUrl } from "inei";
img.src = thumbnailUrl(buffer);
Lower-level thumbnail extraction. Use this when you already have a ParsedExif result and want to avoid parsing twice.
import { parseExif, extractThumbnail } from "inei";
const result = parseExif(buffer);
if (result.ok && result.data.thumbnail) {
const bytes = extractThumbnail(buffer, result.data);
}
Options
All options are optional. Defaults are shown.
parseExif(buffer, {
readBinaryTags: false,
resolveTagNames: true,
simplifyValues: true,
imageSize: true,
hidePointers: true,
returnTags: true,
includeFormatted: false,
});
Result shape
type ParsedExif = {
image?: { width: number; height: number };
thumbnail?: ThumbnailInfo;
tags: Partial<ExifTagMap>;
tagsRaw: Record<string, unknown>;
formattedTags?: Record<string, string>;
xmp?: XmpData;
};
Tags
With simplifyValues: true (the default), values are post-processed:
- Rationals (
[num, den]) become floats (num / den)
- Dates (
"2023:10:22 18:46:07") become epoch seconds (1697999167)
- GPS DMS arrays become decimal degrees (
37.7749)
- Timezone offsets are applied to date values
The tags object is typed as Partial<ExifTagMap>. Known tags have typed keys. Unknown tags appear as tag_0x{hex}.
Camera: Make, Model, Software, SerialNumber
Lens: LensMake, LensModel, LensInfo, LensSpecification, LensSerialNumber
Exposure: ExposureTime, FNumber, ExposureProgram, ISO, ExposureCompensation, ShutterSpeedValue, ApertureValue, MaxApertureValue, MeteringMode, Flash, ExposureMode
Focus: FocalLength, FocalLengthIn35mmFormat, FocalPlaneXResolution, FocalPlaneYResolution, FocalPlaneResolutionUnit
Image: ImageWidth, ImageHeight, Orientation, XResolution, YResolution, ResolutionUnit, ColorSpace, WhiteBalance, CustomRendered, SceneCaptureType
Date: DateTimeOriginal, CreateDate, ModifyDate, OffsetTime, OffsetTimeOriginal, OffsetTimeDigitized, SubSecTimeOriginal, SubSecTimeDigitized
GPS: GPSLatitude, GPSLongitude, GPSAltitude, GPSLatitudeRef, GPSLongitudeRef, GPSAltitudeRef, GPSTimeStamp, GPSDateStamp, GPSVersionID
When present, result.data.xmp contains:
type XmpData = {
title?: string;
description?: string;
creator?: string;
subject?: string[];
rating?: number;
label?: string;
createDate?: string;
};
XMP is extracted from JPEG (APP1) and PNG (iTXt) automatically.
Formatted output
Pass includeFormatted: true to get human-readable strings for common tags:
const result = parseExif(buffer, { includeFormatted: true });
result.data.formattedTags;
Individual formatters
Available from inei/format:
import {
formatShutter,
formatAperture,
formatFocalLength,
formatDate,
formatOrientation,
formatExposureCompensation,
formatExposureProgram,
formatMeteringMode,
formatFlash,
formatGPSCoordinate,
} from "inei/format";
formatShutter(0.00125);
formatAperture(2.8);
formatFocalLength(50);
formatDate(1697999167);
formatOrientation(6);
formatExposureCompensation(-0.7);
formatExposureProgram(3);
formatMeteringMode(5);
formatFlash(0x10);
formatGPSCoordinate(37.77, -122.42);
Batch formatter
Available from inei/format-tags:
import { computeFormattedTags } from "inei/format-tags";
const formatted = computeFormattedTags(result.data.tags);
Standalone XMP parser
Available from inei/xmp:
import { parseXmp } from "inei/xmp";
const data = parseXmp(xmlString);
Export paths
inei | parseExif, parseExifFromBlob, gps, orientation, thumbnail, thumbnailUrl, extractThumbnail, ExifError, types |
inei/format | Individual formatting functions |
inei/format-tags | computeFormattedTags |
inei/xmp | parseXmp, extractXmpFromApp1, extractXmpFromPngITxt |
Error handling
parseExif never throws. It returns { ok: false, error } on failure.
const result = parseExif(buffer);
if (!result.ok) {
console.error(result.error.code, result.error.message);
}
Error codes: NOT_JPEG, NO_EXIF, INVALID_TIFF, TRUNCATED, UNKNOWN.
The convenience functions (gps, orientation, thumbnail, thumbnailUrl) return undefined on failure.
Constraints
- ESM only. No CommonJS support.
- Reads the entire buffer into memory. No chunked or streaming reads.
- IPTC and ICC profile parsing are not supported.
- MakerNote data (vendor-specific extensions) is not decoded.
License
ISC