Svelte & SvelteKit Favicon Guide
Master favicon setup in Svelte: SvelteKit static directory, svelte:head component, app.html configuration, Vite setup, and optimization for modern reactive framework.
Svelte Favicon Setup
SvelteKit
Place in static/ folder
Vite/Svelte
Place in public/ folder
Custom Head
Use svelte:head
SvelteKit Setup (Recommended)
Static Directory Method
File Structure
my-sveltekit-app/
static/
favicon.ico ? Place here
favicon.png
favicon-16x16.png
favicon-32x32.png
apple-touch-icon.png
android-chrome-192x192.png
android-chrome-512x512.png
site.webmanifest
src/
routes/
+layout.svelte
+page.svelte
app.html
svelte.config.jsapp.html Configuration
File: src/app.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<!-- Favicons -->
<link rel="icon" type="image/x-icon" href="%sveltekit.assets%/favicon.ico" />
<link rel="icon" type="image/png" sizes="32x32" href="%sveltekit.assets%/favicon-32x32.png" />
<link rel="icon" type="image/png" sizes="16x16" href="%sveltekit.assets%/favicon-16x16.png" />
<link rel="apple-touch-icon" sizes="180x180" href="%sveltekit.assets%/apple-touch-icon.png" />
<link rel="manifest" href="%sveltekit.assets%/site.webmanifest" />
<meta name="theme-color" content="#ffffff" />
%sveltekit.head%
</head>
<body data-sveltekit-preload-data="hover">
<div style="display: contents">%sveltekit.body%</div>
</body>
</html>Note:
%sveltekit.assets% automatically resolves to correct path, supporting adapter-specific deployments.
Dynamic Favicon with svelte:head
File: src/routes/+layout.svelte
<script>
import { page } from '$app/stores';
$: isDarkMode = $page.data.theme === 'dark';
</script>
<svelte:head>
<link
rel="icon"
type="image/png"
href={isDarkMode ? '/favicon-dark.png' : '/favicon.png'}
/>
</svelte:head>
<slot />Vite + Svelte Setup
Non-SvelteKit Projects
File Structure
my-svelte-app/
public/
favicon.ico ? Place here
favicon-16x16.png
favicon-32x32.png
apple-touch-icon.png
src/
App.svelte
main.js
index.html
vite.config.jsindex.html Configuration
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<!-- Favicons -->
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
<link rel="icon" type="image/png" sizes="96x96" href="/favicon-96x96.png" />
<link rel="icon" type="image/png" sizes="512x512" href="/favicon-512x512.png" />
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
<title>Svelte App</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>vite.config.js (Optional)
import { defineConfig } from 'vite'
import { svelte } from '@sveltejs/vite-plugin-svelte'
export default defineConfig({
plugins: [svelte()],
publicDir: 'public', // Default, ensures favicons are copied
build: {
assetsInlineLimit: 0, // Don't inline favicons
},
})Component-Based Favicon
Reusable Component
Create Favicon Component
File: src/lib/Favicon.svelte
<script>
export let theme = 'light';
export let basePath = '';
$: faviconPath = theme === 'dark'
? `${basePath}/favicon-dark.png`
: `${basePath}/favicon.png`;
</script>
<svelte:head>
<link rel="icon" type="image/x-icon" href="{basePath}/favicon.ico" />
<link
rel="icon"
type="image/png"
sizes="32x32"
href="{basePath}/favicon-32x32.png"
/>
<link
rel="icon"
type="image/png"
sizes="16x16"
href="{basePath}/favicon-16x16.png"
/>
<link
rel="apple-touch-icon"
sizes="180x180"
href="{basePath}/apple-touch-icon.png"
/>
<link rel="manifest" href="{basePath}/site.webmanifest" />
{#if theme === 'dark'}
<meta name="theme-color" content="#1a1a1a" />
{:else}
<meta name="theme-color" content="#ffffff" />
{/if}
</svelte:head>Use in Layout
File: src/routes/+layout.svelte
<script>
import Favicon from '$lib/Favicon.svelte';
import { writable } from 'svelte/store';
const theme = writable('light');
function toggleTheme() {
theme.update(t => t === 'light' ? 'dark' : 'light');
}
</script>
<Favicon theme={$theme} />
<button on:click={toggleTheme}>
Toggle Theme
</button>
<slot />Web App Manifest
PWA Configuration
site.webmanifest
File: static/site.webmanifest (SvelteKit) or public/site.webmanifest (Vite)
{
"name": "My Svelte App",
"short_name": "SvelteApp",
"icons": [
{
"src": "/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/android-chrome-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
],
"theme_color": "#FF3E00",
"background_color": "#ffffff",
"display": "standalone",
"start_url": "/"
}Svelte Favicon Best Practices
? Recommendations
- Use
static/folder in SvelteKit - Use
%sveltekit.assets%for paths - Place favicons in
app.htmlfor global use - Use
svelte:headfor dynamic favicons - Include web manifest for PWA support
- Test with different adapters (node, static, vercel)
- Optimize images before adding to project
- Use stores for theme-based favicon switching
? Common Mistakes
- Placing favicons in
src/instead ofstatic/ - Not using
%sveltekit.assets%placeholder - Hardcoding paths (breaks with adapters)
- Forgetting to add favicon to
app.html - Not testing after build
- Missing manifest file
- Using
public/in SvelteKit (wrong directory) - Not clearing browser cache when testing
Deployment Considerations
Adapter-Specific Notes
@sveltejs/adapter-static
- Static files copied to build output
- Deploy entire
build/folder - Favicons accessible at root level
@sveltejs/adapter-vercel
- Static assets automatically served via CDN
- Cache headers optimized automatically
- No additional configuration needed
@sveltejs/adapter-node
- Static files served by Node.js server
- Configure cache headers in server if needed
- Assets available at
/path
Generate Svelte-Ready Favicons
Create optimized favicon packages for SvelteKit and Vite projects
Generate Favicons