Those small but impactful icons displayed next to a website’s title in a browser tab seem like a minor detail, yet implementing favicons involves various considerations for different formats and sizes to fit a range of devices and browsers. Luckily, we can always count on Evil Martians to tell us which files are needed in modern times. Those findings can be implemented quite easy in Astro.
This procedure assumes you are fine with all sizes being generated from one big size. If you require more control e.g. over the smaller sizes you can use the following walkthrough as a starting point.
But you might wonder why there’s a need for a dynamic approach when these images could simply be added to the
public/ directory manually.
If you’re fine with never changing your favicon assets, the most simple approach would be to generate all files manually into the
public/ folder, including the
manifest.json. And then reference them with their absolute path in your
head as described further down, skipping the dynamic image generation and manifest creation.
One significant advantage of generating favicons dynamically is cache busting. When you update your favicon, browsers might still serve the old one from cache. By generating favicons dynamically, you can ensure that the latest version is served, as, if they have changed, each build will create new, uniquely named files that bypass the cache.
To begin, these are the source files we will deal with, with only 2 image assets:
Housing the original favicon images.
favicon.pngis a large-sized image (512px) that will be resized dynamically, whereas
favicon.svgcan be a SVG file that adapts to the user’s light or dark mode settings.
This can be any layout template or page that contains your HTML
headcontent, as we will add the links to the favicons and the manifest file in there.
This is an Astro Static File Endpoint that dynamically generates the
/manifest.jsonfile, referencing the generated favicons. This file uses Astro’s
getImage()function to create various sizes of PNG icons from a single source image, and then includes these in the generated manifest.
Final Generated Files
After building the project, the generated favicon files will be placed in the
dist/_astro/ directory (
dist/_image/ during development) with dynamic filenames, and correctly referenced in your
/manifest.json. This happens automatically during the site build, so there’s no need to keep track of these files manually.
This should be present in your
dist/ folder after following the rest of this article:
Adding Favicon References to the
To reference the manifest file and to generate required favicon sizes, let’s update the
head section of the site first.
In this example, we do this in a
src/layouts/index.astro file, assuming this is then used as a shared layout in one of your
src/pages/ files. But do this wherever your
head info gets populated in your site.
In this example layout file, let’s add:
getImage() function is used to generate an Apple Touch Icon (180x180 PNG) on build time for static builds, or during server-side rendering. Astro will then reference those generated images in the respective
head tags added above.
The SVG favicon is not generated anew but is essentially passed through the
getImage() function to benefit from cache busting.
Generating the Web Manifest
Add the following code to
This will generate the manifest file into
/manifest.json with additional favicon assets being created and referenced in the newly created manifest file.
.js file ending and removing the
: APIRoute type annotation.
With this, the
manifest.json also has the minimally required keys to make your site installable as a Progressive Web App.
Generating the Favicon.ico
Don’t get smart with the static asset folder structure and cache busters.
Yup, for legacy browsers we actually need a
favicon.ico at the site’s root, hence the reference to
/favicon.ico in the
The most simple way is to generate that ico file once with one of the many online or cli tools available, put it in
public/ and be done with it.
But to accomplish this without dealing with another source file and without having to worry about future favicon changes, we can make use of Astro’s Static File Endpoints again to generate and deliver this asset under
sharp does not support
ico output by default, we have to use
sharp under the hood so it should be installed already but if you get errors, you might have to add it to your dependencies too.
sharp-ico directly in
src/pages/favicon.ico.ts to resize and generate the final
favicon.ico from the source image:
Only one size in the final ico should be fine for most use cases. If you want to get more sizes into the final ico, you can pass more buffers to that array passed to
In the end, this will return our dynamically generated ico file under
We have to work around Astro’s native asset handling here, I could not get
sharp to work with
astro:assets generated urls, or with the raw old
?url import way. Which is why a Node.js native module
path is used, which might lead to problems during SSR depending on your setup so be aware. Would love to know a way of passing Astro-generated image URLs so sharp understands them, if you know a way, do let me know!
All the required favicon assets are now integrated in an Astro project, covering most modern browsers and devices. Make sure to look through all he other tidbits in the Evil Martian post and explore the Astro docs:
- How to Favicon in 2023: Six files that fit most needs
- Astro: Static File Endpoints
Have a comment?
Hit me up @firstname.lastname@example.org
Found something useful?
Say thanks with BTC or ETH
Edit on GitHub
Contribute to this post