Your images are processed in your browser — Cloud HD (Pro, opt-in) is the only exception
angular.json assets array + src/index.html

Angular Favicon Generator

Angular is the one framework here where dropping files in a folder is not enough. The CLI only copies what the assets array in angular.json tells it to copy, and index.html resolves icon paths against a base href.

Drop an image
Drop a logo or image
PNG, JPG, WebP, GIF · square 512×512+ works best · non-square images are padded, never cropped · processed in your browser

Favicon Setup for AngularFree, Fast & Private

Newer Angular CLI versions scaffold a public/ directory at the project root and give it a single catch-all entry in the assets array. Older projects put favicon.ico directly in src/ and everything else under src/assets, with two separate entries. Both are still perfectly valid; what matters is that whatever directory you use appears in angular.json.

Where each generated file goes in Angular

The generator above emits eight files with fixed names, site.webmanifest included. Static assets in Angular live in public/ (Angular 18+) or src/assets (earlier), and the markup is written in src/index.html. This is the mapping between the two.

Generated fileGoes toNotes
favicon.icopublic/favicon.icoPre-18 layout: src/favicon.ico, with its own entry in the assets array.
favicon-16x16.pngpublic/favicon-16x16.pngPre-18: src/assets/favicon-16x16.png.
favicon-32x32.pngpublic/favicon-32x32.pngPre-18: src/assets/favicon-32x32.png.
favicon-48x48.pngpublic/favicon-48x48.pngOptional — duplicated inside the .ico.
apple-touch-icon.pngpublic/apple-touch-icon.pngiOS home screen, 180×180.
android-chrome-192x192.pngpublic/android-chrome-192x192.pngManifest icon, needed for ng add @angular/pwa.
android-chrome-512x512.pngpublic/android-chrome-512x512.pngManifest icon and splash screen.
site.webmanifestpublic/site.webmanifestOr overwrite the manifest.webmanifest that ng add @angular/pwa generated. Must be in the assets array like everything else.

The assets array is the whole game

Angular does not have a magic static folder. The CLI builder copies exactly the paths listed under projects.<name>.architect.build.options.assets in angular.json, and anything not listed is invisible to the build no matter how sensibly it is named. This is the number one reason an Angular favicon works with ng serve on one machine and 404s in production on another: someone added the file, someone else changed the assets array, and the two never met.

The modern shape is a single glob entry that treats public/ as a copy-everything directory. The older shape names src/favicon.ico explicitly and adds src/assets as a directory with an output path. Read your angular.json before you move any files — the layout you have is the layout the build expects, and converting between them is a deliberate change, not a cleanup.

If you run a multi-project workspace, remember that each project has its own assets array. A favicon added to the library-adjacent demo app will not appear in the real one.

angular.json — both layouts

// Angular 18+ scaffold: one glob covers the whole public/ folder
"assets": [
  { "glob": "**/*", "input": "public" }
]

// Older layout: favicon.ico named explicitly, assets copied as a directory
"assets": [
  "src/favicon.ico",
  { "glob": "**/*", "input": "src/assets", "output": "/assets" }
]

Relative hrefs and the base href

Open a scaffolded src/index.html and you will find <base href="/"> in the head and a favicon link written as href="favicon.ico" with no leading slash. That is not sloppiness. Angular applications are routinely deployed under a path prefix, and the CLI supports that with a single build flag, --base-href, which rewrites that base element. Every relative URL in the document then resolves against it automatically, favicons included.

Write href="/favicon.ico" instead and you have opted out of that machinery: the icon will resolve to the domain root regardless of where the app actually lives, which is fine at the root and broken under a prefix. Keeping the hrefs relative is the whole reason the base element is there.

The APP_BASE_HREF injection token you may have seen is a runtime concern for the router, not a build-time one for HTML. It does not affect where the browser looks for your icon files.

src/index.html — relative paths under <base>

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Your App</title>
  <base href="/">
  <link rel="icon" href="favicon.ico" sizes="16x16 32x32 48x48">
  <link rel="icon" type="image/png" sizes="32x32" href="favicon-32x32.png">
  <link rel="icon" type="image/png" sizes="16x16" href="favicon-16x16.png">
  <link rel="apple-touch-icon" sizes="180x180" href="apple-touch-icon.png">
  <link rel="manifest" href="manifest.webmanifest">
  <meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body><app-root></app-root></body>
</html>

The build output moved

Since the application builder became the default, ng build writes to dist/<project-name>/browser/ rather than straight into dist/<project-name>/. The extra directory exists to make room for a server/ sibling when SSR is enabled. Deployment scripts written against the older layout will happily upload the parent folder, and the result is a site where index.html is one level too deep and every relative icon path resolves to nothing.

When a favicon mysteriously disappears after an Angular upgrade, check the deploy target before you touch any markup. Compare the file listing under dist against what your host is actually serving; nine times out of ten the icon is present in the build and the wrong directory got shipped.

PWA support and the manifest

Running ng add @angular/pwa wires up the service worker and drops a manifest.webmanifest into the project along with a pile of Angular-branded PNGs under an icons directory. It also adds the manifest link tag to index.html and registers the icons at eight sizes, most of which no current platform requests.

Trim it, and the generator above has already written the trimmed version. Its contents are below: paste them over the generated manifest.webmanifest, or keep the downloaded site.webmanifest under its own name and repoint the link tag. Either way, delete the Angular logo PNGs and confirm the file is covered by the assets array, because a manifest the CLI does not copy is as invisible as an icon it does not copy.

The relative src values are not an oversight — they are what makes this work under --base-href, and Angular is the framework where that matters most, since its whole icon story is built on relative hrefs resolving against the <base> element. That element governs the HTML document. It does not reach inside a JSON file, so a manifest with "/android-chrome-192x192.png" in it would pin those two icons to the domain root while every other icon on the page followed the base href. Relative paths inside the manifest are resolved against the manifest's own URL instead, so the two stay in step. Keep the manifest in the same directory as the PNGs.

The service worker adds a second cache layer in front of everything, so after a favicon change expect to need a full reload — or a service worker update cycle — before the new icon appears, even in a fresh tab.

manifest.webmanifest — the downloaded site.webmanifest, trimmed to what platforms ask for

{
  "name": "Your App",
  "short_name": "App",
  "start_url": ".",
  "display": "standalone",
  "icons": [
    {
      "src": "android-chrome-192x192.png",
      "sizes": "192x192",
      "type": "image/png"
    },
    {
      "src": "android-chrome-512x512.png",
      "sizes": "512x512",
      "type": "image/png"
    }
  ]
}

Whatever you paste, the artwork itself never leaves your machine. The set above is rendered with the Canvas API and the .ico container is assembled in JavaScript on your device, so an unreleased logo stays unreleased. If the 16px preview looks like mush, that is the honest signal to simplify the mark before it ships to a Angular tab near you.

How it works

  1. Generate the set: Drop a square 512px logo into the tool above and download the full set.
  2. Read angular.json first: Find the assets array for your project. Newer scaffolds glob public/; older ones name src/favicon.ico and src/assets separately.
  3. Copy files into the listed directory: Put all eight files wherever that array points, and add an entry if you are introducing a new folder. A manifest the CLI does not copy is as invisible as an icon it does not copy.
  4. Edit src/index.html with relative hrefs: No leading slash — the <base href> element is what makes subpath deployments work.
  5. Point the manifest tag at the right file: Use the downloaded site.webmanifest, or paste its contents over the manifest.webmanifest that ng add @angular/pwa generated and delete the Angular logo PNGs. Its src values are relative for the same reason your icon hrefs are.

Frequently asked questions

Where does the favicon go in an Angular project?
Wherever angular.json says. Angular 18 and newer scaffold a public/ directory covered by a single glob entry in the assets array; earlier projects keep src/favicon.ico with its own entry and everything else under src/assets. Files outside the assets array are never copied into the build.
Why is my Angular favicon missing after ng build?
Almost always the assets array. The CLI copies only the paths listed there, so a file dropped into a folder the array does not mention is silently ignored. Check angular.json, then check that you edited the array for the right project in a multi-project workspace.
Should the href start with a slash?
No. The scaffolded index.html contains <base href="/"> and writes icon paths relative, so a build made with --base-href /subpath/ still resolves them correctly. A leading slash pins the icon to the domain root and breaks any subpath deployment.
My favicon vanished after upgrading Angular.
The application builder writes to dist/<project>/browser/ rather than dist/<project>/. Deploy scripts written for the old layout ship the parent directory, leaving index.html a level too deep. Compare the build output with what your host is actually serving before editing any markup.
What about the icons ng add @angular/pwa generated?
They are Angular-branded placeholders at eight sizes, most of which nothing requests any more. The generator downloads a manifest already trimmed to the two android-chrome PNGs: paste it over manifest.webmanifest, or ship it as site.webmanifest and repoint the tag. Delete the Angular logos, keep the link tag the schematic added, and make sure the file is inside the assets array.
Should the manifest icon paths have a leading slash?
No, for the same reason your icon hrefs in index.html do not. The <base href> element governs the HTML document and cannot reach inside a JSON file, so an absolute src pins those two icons to the domain root while everything else follows --base-href. A relative src is resolved against the manifest URL, so the two stay in step — provided the manifest sits in the same directory as the PNGs.
Why does the old icon persist with a service worker installed?
The Angular service worker caches assets in front of the browser cache, so a favicon change needs a service worker update cycle before it is visible. A hard refresh alone may not be enough — unregister the worker in DevTools if you are testing.
Do I need a separate entry for favicon.ico?
Only in the older layout, where it sits directly in src/ rather than under an asset directory. With the public/ glob entry, favicon.ico is covered like everything else in that folder.

All Image Tools

AI Tools

Solutions by use case