asset-utils

Zero-dependency utilities for parsing, building, and resolving Sanity image and file asset IDs, URLs, and paths.

Library
npm
v2.3.0
47stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
41/100Fair
Development Activity48
Maintenance20
Community24
Maturity60
Momentum12

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
70/100Good
Architecture88
Code Quality90
Innovation55
Learning Curve45

@sanity/asset-utils is a small, dependency-free TypeScript library for working with Sanity’s image and file asset identifiers. It parses and builds the specialized ID and URL formats Sanity uses for CDN-hosted assets, extracting dimensions, extensions, and project/dataset information from strings, or reconstructing URLs and paths from those parts. Because it does no network calls, every function is synchronous and behaves identically in the browser, on the server, or inside build scripts.

The package exposes more than 50 focused functions and type guards covering three needs: parsing asset IDs/URLs/filenames into structured objects, building file and image URLs/paths from an asset-like shape and project details, and resolving arbitrary Sanity source values (documents, references, upload stubs, raw IDs) into a consistent shape. It also handles hotspot/crop defaults and narrowing through type-guard functions like isSanityImageAsset and isReference, making it useful anywhere a Sanity Studio plugin, frontend integration, or backend script needs to reason about asset shapes without pulling in the full Sanity client.

What You Get

  • Asset ID/URL parsers that decode Sanity’s image-<id>-<width>x<height>-<ext> and file-<id>-<ext> formats into typed objects
  • URL and path builders (buildImageUrl, buildFileUrl, buildImagePath, buildFilePath) that reconstruct CDN URLs from asset parts and project/dataset details
  • Resolver functions (getImage, getFile, getImageAsset, getFileAsset and their tryGet*/non-throwing counterparts) that normalize any Sanity asset-like source into a consistent shape
  • Type guard functions (isSanityImageAsset, isReference, isAssetId, etc.) for narrowing unknown values in TypeScript without manual type checks
  • Hotspot and crop default helpers (getDefaultHotspot, getDefaultCrop) for handling Sanity’s image cropping metadata

Common Use Cases

  • Custom image URL builders - Frontend developers building a custom urlFor()-style helper who need to parse or construct Sanity CDN URLs without pulling in @sanity/image-url’s full builder API
  • Sanity Studio plugin development - Plugin authors who need to validate or narrow asset-like values (documents, references, in-progress uploads) with type guards before rendering custom asset UI
  • Server-side asset processing - Backend scripts that ingest Sanity webhook payloads or query results and need to extract dimensions, extensions, or project/dataset info from asset IDs synchronously
  • Type-safe GROQ result handling - TypeScript codebases that query Sanity via GROQ and need to resolve loosely-typed asset references into strongly-typed image/file shapes before use

Under The Hood

Architecture The library is organized by concern into small, single-purpose modules: asserters.ts holds type-guard predicates, parse.ts decodes raw asset ID/URL/filename strings using patterns from constants.ts, paths.ts builds URLs and paths from asset-like shapes, and resolve.ts composes parsing, building, and asserting into the higher-level public resolvers (getImage, getFile, and their non-throwing tryGet* counterparts). hotspotCrop.ts and errors.ts handle cropping defaults and a single custom UnresolvableError type respectively, with index.ts re-exporting everything as flat named exports. This layered, function-only design (no classes, no hidden state) means every module can be reasoned about independently, and the public resolvers only depend on the lower-level parse/build/assert layers, never the reverse.

Tech Stack Written entirely in TypeScript targeting ES2020 with no runtime dependencies. The package is built and validated with @sanity/pkg-utils to produce dual ESM/CJS output with accurate type declarations, tested with Vitest, and linted via ESLint with eslint-config-sanity and Prettier. Documentation is generated from JSDoc comments through a custom scripts/generateDocs.ts script (using cheerio and typedoc) into both the checked-in README and a hosted API reference site. Releases are automated end-to-end via semantic-release and GitHub Actions, including npm provenance through OIDC.

Code Quality The test suite is extensive relative to the library’s size, with resolve.test.ts alone covering well over a thousand lines of cases against the public resolver API, alongside dedicated suites for parsing and hotspot/crop logic. tsconfig.json enables strict mode in full (strict, strictNullChecks, noImplicitAny, noUnusedLocals, noUnusedParameters), and CI runs the test suite across multiple Node.js versions on every push and pull request. Every exported function carries JSDoc with parameter, return, and throw documentation, and error handling favors a dedicated UnresolvableError type with an isUnresolvableError guard over generic thrown errors.

API Design The public API is unusually consistent for its size: nearly every resolver ships in matched pairs, a throwing form (getImage) and a non-throwing tryGet-prefixed form (tryGetImage) that returns undefined on failure, giving consumers a predictable choice between strict and forgiving handling without duplicating logic themselves. Naming follows a small number of verb prefixes (parse/build/get/tryGet/is) applied consistently across the file and image variants, so learning one function family transfers directly to the others. The library assumes familiarity with Sanity’s asset ID conventions, and offers no runtime validation feedback beyond the typed error, but for consumers who already work with Sanity data, the API requires minimal boilerplate to get useful output from a bare ID or URL string.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search