Library / SDK
itgalaxy/favicons avatar
itgalaxy/favicons

favicons: generating every icon size and a web manifest from one source image

Favicons generator for Node.js

1,240 stars164 forksTypeScriptMIT

At a glance

What is it?
A Node.js module that takes a logo and produces the Android, Apple, Windows and Yandex icon sets, the Apple startup images, a manifest.json, the HTML tags and optionally manifest shortcuts, in one call. Two claims in the README need checking against the package itself: it says the icons are generated in pure JavaScript with no external dependencies, and it says the library is tested on Node 14 and above, while the manifest says otherwise on both counts.
Who is it for?
favicons suits a Node.js team that ships a web app with an installable PWA and wants the icon set, the manifest and the link tags produced from one source image instead of maintained by hand.
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 8 days 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 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

One call returns images, files and HTML

Favicons is a Node.js module for generating favicons and their associated files, installed from npm as `favicons`. It was originally built for Google's Web Starter Kit and for Catalyst, and the author in the package manifest is Hayden Bleasel, which connects the two.

The API is a single async function. You call it with a source and an optional configuration, and it returns three things:

js
try {
  const response = await favicons(source, configuration);

  console.log(response.images); // Array of { name: string, contents: <buffer> }
  console.log(response.files); // Array of { name: string, contents: <string> }
  console.log(response.html); // Array of strings (html elements)
} catch (error) {
  console.log(error.message); // Error description e.g. "An unknown error has occurred"
}

The three return values are the whole output contract. Images are the binary icon files with their names, files are the text artefacts such as the manifest, and html is the list of link and meta elements you paste into your head. Nothing is written to disk by the library, so the caller decides whether the output goes to a public directory, gets inlined, or ends up in a build artefact.

The source is a string path, a buffer, or an array of strings, which is what makes it usable from a build pipeline where the image has already been processed into memory.

The library is MIT licensed, the primary language is TypeScript, and the recent releases are v7.3.1 from 2026-07-23, v7.3.0 from 2026-05-28 and v7.2.0 from 2024-03-15. The last push was on 2026-10-15.

The README's dependency claim does not match the manifest

The README says Favicons generates its icons locally using pure Javascript with no external dependencies. The package manifest says otherwise, listing three runtime dependencies: `escape-html` at 1.0.3, `sharp` at 0.35.3 and `xml2js` at 0.6.1.

Sharp is the important one. It is a native image processing library, so the package does depend on a compiled binary and on the platform it was built for. The claim about generating icons locally still holds in the sense that matters most, which is that no external service is called and no image is sent anywhere, but the stronger reading, that the module has no native or third-party pieces, does not hold.

That distinction has practical consequences. In a Lambda or a container with a slim base image, sharp needs its platform binaries present. In a build that installs from a lockfile, the native module has to be built or fetched for the right architecture and Node ABI. A library that claims to be pure JavaScript can be dropped into any environment, and this one cannot.

`xml2js` is there for reading and writing XML, which is what the Apple and Windows metadata formats need, and `escape-html` is for the text that ends up in the generated HTML tags.

The README also says Favicons is tested on Node 14 and above, while the `engines` field in the package manifest requires Node 20.0.0 or newer. The manifest is the enforceable one, since npm warns about an older Node and blocks the install only with engine-strict, so a team on Node 18 gets a warning at best and a failure at worst.

The manifest options are the interesting part of the configuration

The configuration object in the README is long, and most of it is the web app manifest rather than the icon generation.

The identity fields are `appName`, `appShortName`, `appDescription`, `developerName` and `developerURL`, with the note that `appShortName` falls back to `appName` if not set. `dir` sets the primary text direction for the name, short name and description, with the default `auto`, and `lang` sets the primary language with the default `en-US`. Those two are the ones people forget on a localised app, and the defaults are English-centric.

The presentation fields are `background`, a background colour for flattened icons defaulting to `#fff`, and `theme_color`, described as the theme colour used for example in Android's task switcher. `appleStatusBarStyle` accepts `black-translucent`, `default` or `black`, and `display` accepts `fullscreen`, `standalone`, `minimal-ui` or `browser`, defaulting to `standalone`.

Then there are the behavioural ones. `scope` sets the set of URLs the browser considers within the app, and `start_url` is the URL when launching from a device, with the default `/?homescreen=1`, which is the conventional way to detect a home screen launch. `preferRelatedApplications` decides whether the browser should prompt the user to install the native companion app, and `relatedApplications` carries that information as an array of id, url and platform triples, used only when the preference is true.

Two more are worth singling out. `cacheBustingQueryParam` is a query parameter added to all the URLs so that a new icon is not served from a cache. And `loadManifestWithCredentials` exists because browsers do not send cookies when fetching a manifest, so a manifest that depends on a session breaks silently.

`manifestMaskable` accepts a boolean, a string, a buffer or an array of strings, and points at the maskable icon source, with the documentation reference to the web.dev maskable icon guidance.

Each platform group can be disabled, recoloured or repointed

The `icons` object is where the per-platform control lives, and each entry follows the same three-part shape.

Each platform can be `true` to use the default sources, `false` to skip it entirely, an object with `offset` and `background`, or an array of source images. The offset is a percentage, and the background can be `false` to use the default, `true` to force the default background, or a colour to set the background for those icons specifically.

The platform groups are `android` for the Android homescreen icon, `appleIcon` for the Apple touch icons, `appleStartup` for the Apple startup images, `favicons` for the regular favicons, `windows` for the Windows 8 tile icons, and `yandex` for the Yandex browser icon.

The default source lists are published in the README, grouped by platform, and they are long. The Android group runs from `android-chrome-36x36.png` up to `android-chrome-512x512.png`, in nine sizes. The Apple touch icon group runs from `apple-touch-icon.png` and the precomposed variant up to `apple-touch-icon-1024x1024.png`, in thirteen entries. The Apple startup group is the largest, with the full set of device portrait and landscape startup images, and the list continues past the point where the README was truncated.

That list is also the reason the configuration is worth reading. A team that ships a PWA and wants a smaller output can turn off `appleStartup` entirely, which is by far the biggest number of files, or point `android` at its own source images with a different background, which matters for adaptive icons where the platform crops the artwork.

Manifest shortcuts take their own icon sources

The `shortcuts` array carries the manifest shortcuts, with a link to the MDN documentation for the web manifest shortcuts feature. Each entry is an object with a name, a short name that falls back to the name, a description, a url and an icon.

The README's example gives a shortcut named View your Inbox, with the short name `inbox`, the description, the url `/inbox` and the icon source `test/inbox_shortcut.png`, and then notes that more shortcut objects follow.

The icon field takes the same source types as everywhere else, a string, a buffer or an array of strings, so a shortcut can have its own artwork rather than reusing the app icon. That matters for a shortcut that represents a specific destination, since a generic icon in a long-press menu is not useful.

One detail in the configuration is worth flagging, and it is in the comment rather than the code. The `description` field of a shortcut is marked as not used in any implementation yet. So if you write a description for each shortcut expecting it to appear in a context menu, it will not, at least not with this library's default output.

The `pixel_art` option is also easy to miss. It keeps pixels sharp when scaling up, for pixel art, and the README notes that it is only supported in offline mode.

The build produces a dual CommonJS and ESM package

The package manifest is a modern dual package. The `type` is `module`, the `main` field points at `./dist/index.cjs` for CommonJS, the `module` field at `./dist/index.mjs` for ESM, and the `types` at `./dist/index.d.mts`. The `exports` map ties those together with separate `import` and `require` conditions, so a bundler and Node both resolve the right file.

The build is `tsdown`, with the configuration in `tsdown.config.ts` at the root, and a `prebuild` that runs `clean`, which is `rimraf dist/`. The published `files` list is just `dist/`.

The test setup is the part that has clearly been thought about. Jest runs with `NODE_OPTIONS=--experimental-vm-modules`, which is what makes ESM work inside Jest. The `pretest` script runs lint and the format check, so a test run fails on style before it runs a single assertion.

And there are two extra test packages, `test-mjs/` and `test-cjs/` under `test-packages/`, each with its own entry script, exercised by `test-import` and `test-require`. That is a deliberate check of the published entry points rather than of the source, which is exactly the kind of test that catches a broken `exports` map, and it is the failure mode a dual package hits after an otherwise green build.

The `prepublishOnly` script runs the build and then `test-all`, so the same gate runs before a release. The repository also carries a `.babelrc`, a `tsconfig.json`, an `eslint.config.cjs` in the flat config format, and a `.prettierrc`.

Where favicons is the wrong choice

The first wrong case is a build environment that cannot take a native module. Sharp pulls in platform binaries, and although the README describes the generation as pure JavaScript with no external dependencies, the manifest lists sharp as a runtime dependency. A serverless deployment that installs only production dependencies on a platform without a prebuilt sharp binary will fail at runtime, and the failure will look like a missing file rather than a dependency problem.

The second wrong case is a project on an old Node runtime. The `engines` field requires 20.0.0 or newer, which is stricter than the Node 14 the README mentions, and a team on Node 18 will hit the discrepancy at install time.

The third is a team that only needs a favicon.ico. Generating nine Android sizes, thirteen Apple touch icons and the full set of Apple startup images is the point of the library, and if you need one file, a static asset in your repository is simpler and has no build step.

The fourth is a project that already has an icon pipeline, for example one that uses a design tool's export or a build plugin, and has standardised on it. Two icon pipelines in one repository produce conflicting artefacts in the public directory.

The fifth is a developer who needs a result the library does not produce. It covers the manifest and the platform icon sets, and the README does not describe shortcuts icons for every platform, nor a service worker, nor offline caching, so a team that needs a complete PWA build has to assemble the rest itself.

What it does well is the tedious part. One source image, one call, and every artefact a browser will ask for, with the platform groups and backgrounds configurable enough to adapt rather than merely accept.

Editorial conclusion

favicons suits a Node.js team that ships a web app with an installable PWA and wants the icon set, the manifest and the link tags produced from one source image instead of maintained by hand. It does not suit a project that cannot accept a native image processing dependency, since the manifest lists `sharp` as a runtime dependency even though the README describes the generation as pure JavaScript with no external dependencies, and it does not suit a build running on Node below 20, since the `engines` field requires 20.0.0 or newer. Before adopting it, check the `engines` field against your CI image rather than the README, and look at the `icons` options before assuming the default set is what you want, because each platform group can be disabled, given a different background or pointed at different source images.

Frequently asked questions

How do I use the favicons package?

Import the `favicons` function and call it with a source image and an optional configuration object. The promise resolves with `images`, the binary icon files, `files`, the text artefacts such as the manifest, and `html`, the link and meta elements to paste into your head.

Which Node version does favicons require?

The `engines` field in the package manifest requires Node 20.0.0 or newer. The README states it is tested on Node 14 and above, so the manifest is the stricter and enforceable of the two.

Does favicons have any runtime dependencies?

Yes, despite the README describing pure JavaScript generation. The manifest lists `escape-html`, `sharp` and `xml2js`, and sharp is a native image processing library, so a platform binary is needed at install time.

Can I disable the Apple startup images?

Yes. Each platform group in the `icons` configuration accepts `false` to skip it, and the Apple startup group is the largest set of files. A group can also take an offset and a background, or an array of your own source images.

How do I add manifest shortcuts with favicons?

Use the `shortcuts` array in the configuration, with a name, an optional short name, a description, a url and an icon source. The icon takes the same source types as the main image, so a shortcut can have its own artwork.

Official sources

  1. Issues
  2. itgalaxy/favicons 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/itgalaxy-favicons.svg)](https://hysenlabs.com/projects/itgalaxy-favicons)

Was this useful?