The Dart image library uses pure Dart to run on the web
Dart Image Library for opening, manipulating, and saving various different image file formats.
At a glance
- What is it?
- The Dart Image Library decodes and encodes multiple image formats in pure Dart with no native dependencies, so the same code runs in a command line tool, a server, a Flutter app on every platform and in a browser. It also applies EXIF orientation during resize, which is the detail that separates it from a naive codec wrapper.
- Who is it for?
- Adopt the Dart image library if you need image processing in Dart or Flutter and want one codebase for mobile, server and web, since it has no native dependencies. Skip it if you need HDR or 32-bit float pipelines at production throughput, where a native library will be faster.
- 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 4 days ago.
- What is it written in?
- Mainly Dart, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
No native code is the whole design decision
The Overview says it in one sentence: the library is written in pure Dart with no native dependencies, so it works everywhere Dart does.
It then names the four places. Command line apps, servers, Flutter on every platform, and the web.
That last one is what makes the choice interesting. Because the library is pure Dart, a Flutter application on mobile, a server and a browser application can share the same decoding code without native dependencies.
The cost is performance, and the project is not pretending otherwise. There is a dedicated documentation page titled Performance and memory, which is the page you read before using this in a batch pipeline. Pure Dart decoding is not what a native library does, and for large images in a tight loop the difference is measurable.
The library can be used wherever Dart works without native dependencies.
The feature list is correspondingly broad. Resizing, cropping, rotating and flipping. Dozens of filters for colour adjustment, blurring and effects, plus colour quantization and dithering. Drawing shapes, lines, text and other images. Direct pixel access in 1 to 32-bit integer and 16 to 64-bit floating point formats with optional palettes. EXIF metadata, ICC profiles and HDR images.
Animated GIF, PNG and WebP are all supported for both decoding and encoding, which is more than most libraries manage for any one of them.
The format list is grouped by what you can actually do
The supported formats section is grouped into three headings, and the grouping is more useful than a flat list.
Read and write covers nine formats: JPEG, PNG with animated PNG, GIF with animated GIF, WebP with animated WebP in both lossless and lossy, plus BMP, TIFF, TGA, ICO and PVR.
Read only is PSD, EXR and the PNM family of PBM, PGM and PPM.
Write only is CUR, the Windows cursor format.
That structure tells you two things a flat list would not. First, WebP here is not lossless-only, which is where most libraries stop. Second, PSD is readable but not writable, which is what you would expect from a library that is not a design tool.
The three read-only formats are worth pausing on. PSD matters if you are processing game art or design exports. EXR matters if you are doing HDR work, and it is read-only in the same way PSD is. PNM is the Netpbm family, which is what you get when a tool writes the simplest possible raster format.
So if your pipeline ends in an EXR or a PSD, this library is the wrong tool for that stage, and knowing it before you build the pipeline saves a rewrite.
There is a separate documentation page for formats, and the README links to it rather than reproducing the full table, which suggests the page carries detail beyond what is shown here.
copyResize applies EXIF orientation, and that is the correctness detail
The second example in the README is a thumbnail, and one comment in it is the most valuable line in the documentation.
final bytes = await File('photo.jpg').readAsBytes();
final image = img.decodeJpg(bytes);
if (image == null) {
return;
}
final thumbnail = img.copyResize(image, width: 120);
await File('thumbnail.jpg').writeAsBytes(img.encodeJpg(thumbnail, quality: 85));The comment on the resize line says the EXIF orientation of the photo is applied.
This matters more than it appears. A camera or a phone stores the orientation of the image in an EXIF tag rather than rotating the pixel data. A decoder that ignores it gives you pixels in the sensor's orientation, so a portrait photo comes out sideways, and then a naive thumbnail service produces a landscape thumbnail of a portrait photo and stores it that way.
Because the resize applies the orientation, the output is right. And because it applies it rather than baking it into the data, the source image object still carries the original orientation for anything else that reads it.
Two other details in the same example are worth noting. Decoding can fail, and the failure is handled in two ways: the return is null or an ImageException is thrown, so `decodeJpg` returning null is the documented path you have to check. And the JPEG quality is passed as 85 on encode, which is the same default you would want from any encoder.
The first example is the other half: creating a 256 by 256 8-bit RGB image, iterating over its pixels with `for (final pixel in image)`, and setting channels with the cascade syntax.
`pixel.x` and `pixel.y` as channel values, producing a gradient. The image is iterable, so the pixel model is built for iteration rather than only for indexed access.
The Command API moves work off the main isolate
The third example exists to solve a specific problem, and the name of the method tells you what it does.
final cmd = img.Command()
..decodeImageFile(path)
..copyResize(width: 64)
..writeToFile('thumbnail.png');
await cmd.executeThread();The chain builds a pipeline without executing it. `decodeImageFile` reads and decodes, `copyResize` resizes, and `writeToFile` encodes to a PNG with the format taken from the file extension. Then `executeThread()` runs the whole thing.
The naming is deliberate. On platforms that support isolates, the commands execute on a separate isolate. Otherwise, on the web, they run on the main thread.
That fallback is the honest part. Dart isolates are how you keep a long CPU-bound job off the UI isolate in a server or a Flutter app, and they are why a Command pipeline is better than calling the functions directly. On the web there are no isolates in the same sense, so the same API runs in place, which means the performance benefit evaporates exactly where you are most likely to be blocked.
The Command API is listed in the features as a way of running image processing in a separate isolate, and it has its own documentation page.
It also gives you a different shape for the code. Instead of a sequence of awaits, the API lets you build a processing chain and execute it together.
Documentation is the API here
The README is short and almost everything is a link, which is a deliberate choice for a library with this much surface.
The feature list points at twelve separate documentation pages, one per capability area: formats, animation, transform, filters, color quantization, draw, fonts, image data, exif, hdr and commands, plus a README index for the whole set.
There is a tutorial, a page on using the library with Flutter, a page on performance and memory, and an API reference hosted on pub.dev rather than in the repository.
The reason for one page per feature is that this library has more surface than a typical package. Filters alone are described as dozens, and drawing covers shapes, lines, text and compositing other images, with a separate fonts page for the text rendering. None of that fits in a README, and a README that tried would be the worse document.
The repository layout follows the same logic. `lib/` for the source, `doc/` for those pages, `test/` for tests, `benchmark/` for benchmarks, `tool/` for tooling, `example/` with a `main.dart` and sample output files including a WebP and a PNG thumbnail, and `web/` plus a `build_web.bat` for the web build.
The repository also contains `web/` and a `build_web.bat` file.
The package is published on pub.dev as `image`, the primary language is Dart with a `dart-web` topic among others, and the licence is MIT with a `LICENSE-other.md` alongside it for the third-party code.
Where this library is the wrong choice
Three cases.
If you need maximum throughput on large images, pure Dart is not the answer. The documentation has a performance and memory page precisely because this is the concern, and a native library with SIMD will beat it on a batch job. Use this where the platform matters more than the milliseconds.
If you need to write PSD or EXR, this is the wrong tool. Both are read-only here, and a pipeline that ends in a layered Photoshop file or a floating point EXR needs something else.
The web case deserves a separate word because it is the library's strongest claim and its weakest performance. Running on the web with no native code is what makes it shareable, and it is also where there are no isolates and no SIMD. Whether that trade is right depends on whether your web image work is a thumbnail or a photo editor.
The library is actively worked on, with the last push on 2026-10-11 and no GitHub releases, so the pub.dev version rather than a tag is what you track. That is normal for a Dart package, and it is the right place to pin anyway.
Editorial conclusion
Adopt the Dart image library if you need image processing in Dart or Flutter and want one codebase for mobile, server and web, since it has no native dependencies. Skip it if you need HDR or 32-bit float pipelines at production throughput, where a native library will be faster. Verify first which of your formats are read-only or write-only, since PSD, EXR and PNM can be read but not saved and CUR can be written but not read.
Frequently asked questions
Which image formats can the Dart image library read and write?
Read and write covers JPEG, PNG and animated PNG, GIF and animated GIF, WebP and animated WebP in lossless and lossy, BMP, TIFF, TGA, ICO and PVR. PSD, EXR and the PNM formats are read only, and CUR is write only.
How do I keep image processing off the UI isolate in Dart?
Use the Command API, which builds a chain of decode, transform and write steps and runs them with `executeThread()`. On platforms that support isolates the commands run on a separate isolate, while on the web they run on the main thread.
Does the Dart image library have native dependencies?
No. It is written in pure Dart with no native dependencies, so it works in command-line apps, servers, Flutter and web applications.
Official sources
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.
[](https://hysenlabs.com/projects/brendan-duncan-image)Was this useful?