Angular 18 Favicon Guide: Configuration and Best Practices
Step-by-step guide to adding favicons in Angular 18, including angular.json configuration and asset management.
Angular 18 Favicons: Configuration Over Convention
Angular takes a different approach to favicons than its counterparts. While React says "just drop it in public," Angular says "declare it in your configuration." This might seem like extra work, but it offers precise control over asset handling, build optimisation, and multi-project workspaces.
This guide covers Angular 18's approach, including the latest features like improved build performance and the new application builder. If you're migrating from older Angular versions, pay attention to the configuration changes.
The Angular Way: angular.json Configuration
Basic Favicon Setup
Your Angular project structure:
my-angular-app/
├── src/
│ ├── favicon.ico
│ └── index.html
├── angular.json
└── package.json
In angular.json
, ensure your favicon is included in assets:
{
"projects": {
"my-app": {
"architect": {
"build": {
"options": {
"assets": [
"src/favicon.ico",
"src/assets"
]
}
}
}
}
}
}
Reference it in src/index.html
:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>My Angular App</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
<app-root></app-root>
</body>
</html>
Multiple Favicon Sizes and Types
For comprehensive device support:
Step 1: Add Favicon Files
src/
├── favicon.ico
├── favicon-16x16.png
├── favicon-32x32.png
├── apple-touch-icon.png
├── android-chrome-192x192.png
├── android-chrome-512x512.png
└── site.webmanifest
Step 2: Update angular.json
{
"assets": [
"src/favicon.ico",
"src/favicon-16x16.png",
"src/favicon-32x32.png",
"src/apple-touch-icon.png",
"src/android-chrome-192x192.png",
"src/android-chrome-512x512.png",
"src/site.webmanifest",
"src/assets"
]
}
Step 3: Update index.html
<head>
<!-- Basic favicon -->
<link rel="icon" type="image/x-icon" href="favicon.ico">
<!-- PNG favicons -->
<link rel="icon" type="image/png" sizes="32x32" href="favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="favicon-16x16.png">
<!-- Apple Touch Icon -->
<link rel="apple-touch-icon" sizes="180x180" href="apple-touch-icon.png">
<!-- Android Chrome Icons -->
<link rel="icon" type="image/png" sizes="192x192" href="android-chrome-192x192.png">
<link rel="icon" type="image/png" sizes="512x512" href="android-chrome-512x512.png">
<!-- Web App Manifest -->
<link rel="manifest" href="site.webmanifest">
</head>
Asset Copying with Glob Patterns
Angular 18's improved asset handling allows sophisticated copying:
{
"assets": [
{
"glob": "**/*",
"input": "src/favicons/",
"output": "/"
},
{
"glob": "favicon.ico",
"input": "src/",
"output": "/"
}
]
}
This approach keeps favicons organised in a dedicated folder.
Dynamic Favicons in Angular
Service for Favicon Management
// favicon.service.ts
import { Injectable, Inject } from '@angular/core';
import { DOCUMENT } from '@angular/common';
@Injectable({
providedIn: 'root'
})
export class FaviconService {
constructor(@Inject(DOCUMENT) private document: Document) {}
setFavicon(href: string): void {
const link: HTMLLinkElement = this.document.querySelector("link[rel*='icon']") ||
this.document.createElement('link');
link.type = 'image/x-icon';
link.rel = 'shortcut icon';
link.href = href;
this.document.head.appendChild(link);
}
setMultipleFavicons(favicons: { href: string; sizes?: string; type?: string }[]): void {
// Remove existing favicons
const existingLinks = this.document.querySelectorAll("link[rel*='icon']");
existingLinks.forEach(link => link.remove());
// Add new favicons
favicons.forEach(favicon => {
const link = this.document.createElement('link');
link.rel = 'icon';
link.href = favicon.href;
if (favicon.type) link.type = favicon.type;
if (favicon.sizes) link.sizes = favicon.sizes;
this.document.head.appendChild(link);
});
}
}
Usage in Components
// app.component.ts
import { Component, OnInit } from '@angular/core';
import { FaviconService } from './services/favicon.service';
@Component({
selector: 'app-root',
template: '<router-outlet></router-outlet>'
})
export class AppComponent implements OnInit {
constructor(private faviconService: FaviconService) {}
ngOnInit(): void {
// Set favicon based on environment
const favicon = environment.production ? 'favicon.ico' : 'favicon-dev.ico';
this.faviconService.setFavicon(favicon);
}
}
Notification Badge Example
// notification.service.ts
@Injectable({
providedIn: 'root'
})
export class NotificationService {
private canvas: HTMLCanvasElement;
private ctx: CanvasRenderingContext2D;
constructor(private faviconService: FaviconService) {
this.canvas = document.createElement('canvas');
this.canvas.width = 32;
this.canvas.height = 32;
this.ctx = this.canvas.getContext('2d')!;
}
updateFaviconBadge(count: number): void {
const img = new Image();
img.onload = () => {
// Clear canvas
this.ctx.clearRect(0, 0, 32, 32);
// Draw original favicon
this.ctx.drawImage(img, 0, 0, 32, 32);
if (count > 0) {
// Draw red circle
this.ctx.fillStyle = '#ff0000';
this.ctx.beginPath();
this.ctx.arc(24, 8, 8, 0, 2 * Math.PI);
this.ctx.fill();
// Draw count
this.ctx.fillStyle = '#ffffff';
this.ctx.font = 'bold 11px Arial';
this.ctx.textAlign = 'center';
this.ctx.textBaseline = 'middle';
this.ctx.fillText(count > 99 ? '99+' : count.toString(), 24, 8);
}
// Update favicon
this.faviconService.setFavicon(this.canvas.toDataURL('image/png'));
};
img.src = 'favicon-32x32.png';
}
}
PWA Configuration
For Progressive Web App support:
manifest.json
{
"name": "My Angular App",
"short_name": "AngularApp",
"theme_color": "#1976d2",
"background_color": "#fafafa",
"display": "standalone",
"scope": "./",
"start_url": "./",
"icons": [
{
"src": "android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "android-chrome-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
]
}
Angular Service Worker
ng add @angular/pwa
This automatically:
- Adds service worker support
- Creates manifest.json
- Updates index.html with PWA meta tags
- Configures icon assets
Environment-Specific Favicons
Different favicons for dev/staging/production:
Configure Environments
// environments/environment.ts
export const environment = {
production: false,
faviconPath: 'favicon-dev.ico'
};
// environments/environment.prod.ts
export const environment = {
production: true,
faviconPath: 'favicon.ico'
};
File Replacements
In angular.json
:
{
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
},
{
"replace": "src/favicon-dev.ico",
"with": "src/favicon.ico"
}
]
}
Angular 18 Build Optimisations
The new application builder in Angular 18 handles favicons more efficiently:
{
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:application",
"options": {
"outputPath": "dist/my-app",
"index": "src/index.html",
"browser": "src/main.ts",
"polyfills": ["zone.js"],
"tsConfig": "tsconfig.app.json",
"assets": [
{
"glob": "**/*",
"input": "public",
"output": "/"
}
]
}
}
}
}
Testing Favicon Implementation
Development Server
ng serve
# Check: http://localhost:4200/favicon.ico
Production Build
ng build
# Check dist folder for favicon files
E2E Testing
// favicon.e2e-spec.ts
import { browser, by, element } from 'protractor';
describe('Favicon Tests', () => {
it('should have favicon link in head', async () => {
await browser.get('/');
const favicon = element(by.css('link[rel="icon"]'));
expect(await favicon.isPresent()).toBe(true);
expect(await favicon.getAttribute('href')).toContain('favicon.ico');
});
});
Common Issues and Solutions
Favicon Not Updating?
- 1Clear Angular cache:
```bash
rm -rf .angular/cache
ng serve
```
- 1Check output path:
Ensure favicon is copied to dist folder
- 1Browser cache:
Add version query: href="favicon.ico?v=2"
Works Locally, Not in Production?
Check base href configuration:
<!-- For subdirectory deployment -->
<base href="/my-app/">
Or build with base href:
ng build --base-href /my-app/
Creating Favicons for Angular
Need all these favicon sizes? Use Unwrite's Favicon Generator. Upload your logo and get a complete favicon package ready for Angular, including all sizes and the manifest.json file. Everything processes privately in your browser.
Angular Favicon Checklist
- [ ] Add favicon.ico to src folder
- [ ] Configure assets in angular.json
- [ ] Include multiple sizes for devices
- [ ] Add apple-touch-icon for iOS
- [ ] Create site.webmanifest for PWA
- [ ] Test in production build
- [ ] Consider environment-specific icons
- [ ] Implement FaviconService for dynamic needs
The Angular approach might require more configuration than dropping a file in a folder, but it provides the control and consistency that large applications need. Embrace the configuration - it's there to help you build better apps.