Open-source project
JonasKruckenberg/imagetools avatar
JonasKruckenberg/imagetools

Imagetools: image transformations as import directives in your bundler

Load and transform images using a toolbox :toolbox: of custom import directives!

1,147 stars83 forksTypeScriptMIT

At a glance

What is it?
Imagetools is a toolbox of import directives that transform and optimise images at compile time, with the pixel work done by sharp. It ships as two packages, a core library and a Vite plugin, in a pnpm monorepo, and the interesting design question is where the transformation happens relative to your dev server.
Who is it for?
Imagetools fits a Vite project that ships a lot of raster images and wants resized variants, srcset sets and modern formats generated during the build rather than uploaded to a service. It does not fit a project whose images change per request, since every transformation is baked into the output at build time, and it does not fit a team that cannot accept a native sharp dependency in its build.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 1 day ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Images enter the bundle as imports, not as files you upload

Imagetools is described as a toolbox of easy-to-use import directives that transform and optimize your images at compile-time, and all of the image transformations are powered by sharp.

The model is what makes it interesting. Rather than a build step that scans a folder of images and emits a directory of derivatives, you write an import, and the import carries its transformations. The bundler resolves it, the plugin performs the work, and the result is a file the bundler can fingerprint, hash and rewrite references to, exactly like any other asset.

That has consequences that matter in practice. Because the output goes through the bundler, it participates in cache busting, so a changed source image produces a changed output name and your CDN does not serve a stale derivative. Because the transformation is a directive rather than a filename convention, the size a component needs is stated where the component needs it, and the same source image can legitimately appear at three sizes in three places without any naming scheme to keep track of.

The feature list in the README is short and concrete: output modern formats, resize images, easy srcset generation, fast in development mode, remove image metadata, and extensible. Each of those is a task that teams currently solve with a mix of ImageMagick scripts, hand-uploaded variants and a service in the middle, and the pitch is that all of it can be expressed as one import.

The audience is a front end team on Vite who has grown a directory of resized copies and wants that to stop being a manual chore.

sharp does the pixels, the plugin does the directives

The division of labour is straightforward, and it is the reason the library is small.

sharp is the image processing engine, and it is a good one to depend on: it handles decoding, resizing, format conversion and encoding with libvips under the hood, and it is the part of the stack that has to be fast enough to run inside a build.

The plugin layer is the part this project owns, and it is where the interesting decisions live. A directive has to be parsed out of an import specifier, resolved to a set of requested transformations, executed, and then handed back to the bundler in a form that the bundler will treat as a module with a content hash. The plugin also has to decide what to do in development mode, where running the full pipeline on every request would make the dev server unusably slow.

That last point is why fast in development mode is on the feature list at all, and it is a real engineering problem rather than a checkbox. The trade is between fidelity and speed: a development build that skips or approximates some transformations will not be byte-identical to the production output, so a bug that only appears in the optimised image will not show up while you are editing. The README lists the feature without documenting the exact policy, so anyone relying on it should check the documentation for what is actually skipped.

The extensibility item is the third piece of the same story. A new transformation that is not one of the built-ins can be added, which is what makes the toolbox framing more than marketing, and it is also what separates imagetools from a fixed set of resize flags.

Two packages, and the names do not match the repository

The repository is a monorepo, and the README's packages table lists two of them.

imagetools-core is the library that holds the transformation logic, and vite-imagetools is the Vite plugin that wires it into the bundler. Each has its own changelog, and the README links the version badges straight to those changelogs.

The naming is a trap worth mentioning before anyone tries to install it. The repository is called imagetools, the core package is imagetools-core, and the Vite integration is published as vite-imagetools. The release history makes the third name visible, since the tags include [email protected] and [email protected], both dated 2022-12-17. So the project has shipped a Rollup plugin under a third name, and the README's package table only advertises the core and the Vite plugin.

That history matters for a practical reason. A project that started as a Rollup plugin and now ships a Vite plugin has had to absorb two bundler APIs, and the version numbers in the release list are not in step with each other. Anyone arriving at the repository looking for the Rollup plugin needs to check the changelogs rather than assume the table is complete.

The top level of the repository matches the monorepo shape. There is a packages directory, a pnpm-workspace.yaml, a pnpm-lock.yaml, an examples directory containing an example.jpg and a vite-simple example, a docs directory, and per-package changelogs.

The build scripts show how the packages are meant to be consumed

The root package.json is where the release and development workflow is visible, and it is short enough to read in one go.

json
  "scripts": {
    "ci:version": "pnpm changeset version",
    "ci:publish": "pnpm build && pnpm publish -r",
    "build": "pnpm run --recursive --filter !example-* build",
    "test": "pnpm run --recursive test",
    "lint": "prettier --check . && eslint .",
    "doc": "pnpm run --filter imagetools-core build && typedoc"
  },

Several things fall out of that. The repository is private and its name is imagetools, with the license set to MIT and the type module, so it is a workspace root rather than a published package. The build is recursive and explicitly excludes the examples with the filter, which means the example projects are not part of what gets published. The test and coverage scripts are recursive too, so each package carries its own suite. The doc script builds the core package first and then runs typedoc, with typedoc-plugin-markdown in the dev dependencies, so the API reference is generated from the core package's types rather than written by hand.

The publishing path is the changesets flow. The dev dependencies include @changesets/cli, there is a .changeset directory, and the version script runs pnpm changeset version before the publish step runs pnpm publish -r across the workspace. That means every published change comes from a changeset entry, which is the mechanism that keeps two packages with independent versions in step with each other.

The toolchain around it is current rather than historical: TypeScript 5.9, ESLint 10, Prettier 3, typedoc 0.28, and a packageManager field pinning [email protected] with engines requiring pnpm ^12.0.0. Renovate is in the tree as well, so dependency updates are automated.

Modern formats and metadata removal are the two features with teeth

Two entries on the feature list deserve more attention than the rest.

Output modern formats is the one everybody wants and everybody implements differently. A build-time pipeline that emits, say, an AVIF and a WebP alongside the original PNG means the choice of which one a browser downloads becomes a question about the markup rather than about a CDN configuration, and it means the conversion happens once instead of on every request.

Remove image metadata is the one that is about the contents of the file rather than its pixels. Image files routinely carry more than the picture: camera details, and in some cases location information, travel inside them from whatever device took the photo. A pipeline that rewrites every image is therefore also a decision about what metadata survives, and this project makes stripping it a default capability rather than something each team has to remember.

Easy srcset generation belongs in the same category of practical value. Producing a srcset with several widths is a small amount of logic that every project writes badly or not at all, and doing it from a single import means the widths declared in the component and the widths in the output cannot drift apart.

The trade-off of doing all of this at build time is that the output is fixed at build time. An image that needs to be resized per viewport, per user or per request is outside what this model can do, and a project that needs a runtime image service is looking at a different category of tool.

MIT, a sponsorship link, and release tags that trail the source

The licence is MIT, with the LICENSE file at the root of the repository and the README stating MIT © Jonas Kruckenberg. There is no copyleft obligation and no complication for a commercial site that uses the plugin to produce its own assets.

The README also has a Sponsoring section, which asks whether you are using imagetools in your company, in a commercial project or just feeling generous, and points at GitHub Sponsors with the promise of getting a company logo featured. The project acknowledges the-yamiteru for ideas and support on improving it, and there is a CONTRIBUTING.md alongside a CODE_OF_CONDUCT.md.

The one maintenance signal that needs reading carefully is the release history. The most recent tags in the list are [email protected] and [email protected], both dated 2022-12-17, and [email protected] from 2022-10-11 before that. The last push to the repository was on 2026-10-18, and the toolchain in the root package.json is several years newer than those tags.

So the repository is being worked on while the published versions in this list are old. For a plugin, that gap is the thing to check before you start: the version you install from npm is not the version in the working tree, and the documentation in the repository may describe behaviour that a published release does not have. The changesets setup means the next release will pick up whatever has accumulated, but until it does, the tag and the branch are different things.

Editorial conclusion

Imagetools fits a Vite project that ships a lot of raster images and wants resized variants, srcset sets and modern formats generated during the build rather than uploaded to a service. It does not fit a project whose images change per request, since every transformation is baked into the output at build time, and it does not fit a team that cannot accept a native sharp dependency in its build. Before you adopt it, check the published package names against the repository name, because the npm packages are vite-imagetools and imagetools-core while the repository is imagetools, and confirm that your build environment can install sharp for the platform it runs on.

Frequently asked questions

What does the imagetools project do?

It provides a toolbox of import directives that transform and optimize images at compile time, with all the image transformations powered by sharp. The feature list covers output modern formats, resizing, srcset generation, fast development mode, removing image metadata and extensibility.

What are the packages in the imagetools repository?

The packages table lists imagetools-core, which holds the core logic, and vite-imagetools, the Vite plugin, each with its own changelog. The release history also shows a rollup-plugin-imagetools package, and the repository itself is a private pnpm workspace named imagetools.

How do I install the imagetools Vite plugin?

The plugin is published to npm under the name vite-imagetools, and the core library as imagetools-core. The root package.json pins the package manager with a packageManager field of [email protected] and an engines requirement of pnpm ^12.0.0, so pnpm is the expected way to work with the repository.

How does the imagetools repository publish new versions?

It uses changesets. The root scripts run pnpm changeset version for the version step and pnpm build followed by pnpm publish -r for publishing, with @changesets/cli in the dev dependencies and a .changeset directory in the tree. The API documentation is generated with typedoc from the core package.

What licence is imagetools released under?

MIT, with the LICENSE file in the repository root and the license field set to MIT in the root package.json. The README also asks for sponsorship for commercial users, with a company logo featured in return.

Official sources

  1. Issues
  2. JonasKruckenberg/imagetools on GitHub
  3. License: MIT
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/jonaskruckenberg-imagetools.svg)](https://hysenlabs.com/projects/jonaskruckenberg-imagetools)

Was this useful?