A one-command CLI that turns one image into every icon an Expo app needs, with app.json updates included

In development since March 2026


The chore it removes

Every Expo project needs the same set of icons in the same set of sizes. The official docs walk through making each one separately in Figma or Photoshop, exporting at the right size, dropping into assets/images/, and editing app.json to point at them. It takes ten minutes per project, you make a typo in one path half the time, and the adaptive-icon background colour you eyeball never quite matches the actual dominant colour of the image.

add-expo-icon does all of that in one command.

๐Ÿš€ Usage

npx add-expo-icon ./my-logo.png

That's the whole interface. The script:

  1. Validates the source (must be at least 500ร—500, PNG or JPEG).
  2. Resizes to all the standard targets through sharp.
  3. Writes the resized files into assets/images/ in the current directory.
  4. Extracts the dominant colour from the source image using sharp's pixel statistics.
  5. Rewrites app.json if present โ€” sets expo.icon, expo.android.adaptiveIcon.foregroundImage, expo.android.adaptiveIcon.backgroundColor, expo.web.favicon, and expo.splash.image.
  6. Removes legacy android-icon-* files that older Expo SDKs generated and that confuse current SDKs if left behind.
  7. Prints a per-file summary with sizes and the extracted colour.

Output looks like:

Dominant colour: #1a1a2e

Source: /path/to/my-logo.png (1024x1024 png)

  โœ“ icon.png (1024x1024) โ€” iOS & general app icon
  โœ“ adaptive-icon.png (1024x1024) โ€” Android adaptive icon foreground
  โœ“ favicon.png (48x48) โ€” Web favicon
  โœ“ splash-icon.png (200x200) โ€” Splash screen icon

All icons saved to assets/images/

  โœ“ app.json updated with icon paths

What gets written into app.json

Key Value
expo.icon ./assets/images/icon.png
expo.android.adaptiveIcon.foregroundImage ./assets/images/adaptive-icon.png
expo.android.adaptiveIcon.backgroundColor extracted dominant colour as a hex string
expo.web.favicon ./assets/images/favicon.png
expo.splash.image ./assets/images/splash-icon.png
expo.splash.backgroundColor extracted dominant colour

The script is non-destructive โ€” if app.json doesn't exist or doesn't have an expo key, it skips the rewrite and just produces the icons.

Why sharp

Three reasons. First, it's fast โ€” single-image resize through libvips beats every alternative on Node. Second, the pixel-statistics API is a one-liner for dominant-colour extraction. Third, sharp is well-maintained and the binary builds for every OS Expo developers actually use.

The script ships with sharp as a dependency rather than as a peer-dependency because the install size on a CLI tool isn't the same constraint as on an application โ€” npx add-expo-icon is allowed to take an extra five seconds the first time as long as it just works.

๐Ÿ› ๏ธ Tech Stack

Category Stack
Runtime Node.js 18+
Image processing sharp
Distribution npx-runnable, single bin entry
Language TypeScript (compiled to JS for distribution)

Notes on the dominant-colour extraction

The first version sampled the centre pixel and called it the dominant colour. That worked for logos with one big block of colour and failed for everything else (gradients, intricate details, transparency). The current version downsamples to a 16ร—16 thumbnail, runs k-means with k=4 on the resulting pixels, and picks the cluster centroid with the largest population that isn't pure white, pure black, or fully transparent. It's still a heuristic but it's a much better one โ€” for the kind of simple logos most apps use, it produces a colour that actually matches what a designer would pick.


Built with โค๏ธ using Node.js, sharp


Add Expo Icon is a Node CLI that takes a single source image (PNG or JPEG, minimum 500ร—500) and produces all the icon variants an Expo project needs โ€” `icon.png` 1024ร—1024, `adaptive-icon.png` 1024ร—1024, `favicon.png` 48ร—48, and `splash-icon.png` 200ร—200. It also rewrites `app.json` to point at the new icons, sets adaptive-icon and splash-screen background colours from the dominant colour of the source image, and cleans up legacy filenames. Built with Node.js and sharp. In development since March 2026.