SVG Icons in Svelte and SvelteKit: The Complete 2026 Guide

How to use SVG icons in Svelte and SvelteKit, lucide-svelte, @iconify/svelte, unplugin-icons, inline SVGs, and dark mode with Tailwind. Full code patterns included.

Amit Yadav
Amit Yadav4 min read
On this page

Svelte’s reactivity model makes SVG icons uniquely clean to work with: no useState, no useEffect, just reactive props flowing straight to SVG attributes. lucide-svelte, @iconify/svelte, and unplugin-icons all integrate with zero boilerplate. This guide covers every approach with SvelteKit-specific patterns.

Option 1: lucide-svelte

The official Lucide port for Svelte. Same 1,500+ icons, same visual style, Svelte component output.

npm install lucide-svelte

Basic usage

<script>
  import { Search, Settings, Bell } from 'lucide-svelte';
</script>

<div class="flex gap-4 text-gray-700 dark:text-gray-300">
  <Search size={20} />
  <Settings size={20} />
  <Bell size={20} strokeWidth={1.5} />
</div>

Props

Prop Type Default
size number 24
color string currentColor
strokeWidth number 2
class string ,

Reactive icon props in Svelte

Svelte’s reactivity makes size/color toggling extremely clean:

<script>
  import { Heart } from 'lucide-svelte';
  let active = false;
</script>

<button on:click={() => active = !active}>
  <Heart
    size={24}
    color={active ? '#ef4444' : 'currentColor'}
    fill={active ? '#ef4444' : 'none'}
  />
</button>

No useState, no render cycle, Svelte’s compiled reactivity updates the SVG attributes directly.

Option 2: @iconify/svelte

Access all 414,000+ Iconify icons through a single Svelte component.

npm install @iconify/svelte
<script>
  import Icon from '@iconify/svelte';
</script>

<Icon icon="lucide:search" class="w-5 h-5" />
<Icon icon="tabler:dashboard" class="w-5 h-5" />
<Icon icon="ph:heart-duotone" class="w-5 h-5 text-blue-500" />

Offline/SSR mode

For SvelteKit SSR, bundle icons instead of fetching at runtime:

npm install @iconify/json
<script>
  import Icon from '@iconify/svelte';
  import searchIcon from '@iconify-icons/lucide/search';
</script>

<Icon icon={searchIcon} class="w-5 h-5" />
SvelteKit SSR and Iconify

@iconify/svelte’s default runtime mode fetches icons from a CDN. For SvelteKit with SSR enabled, use the bundled approach above or set mode="svg" to inline the SVG at build time.

Option 3: unplugin-icons with Svelte

unplugin-icons resolves icon components at build time, no imports needed in component files.

npm install -D unplugin-icons @iconify/json

SvelteKit setup

// vite.config.ts
import { sveltekit } from '@sveltejs/kit/vite';
import Icons from 'unplugin-icons/vite';
import { defineConfig } from 'vite';

export default defineConfig({
  plugins: [
    sveltekit(),
    Icons({ compiler: 'svelte' }),
  ],
});

Usage

<script>
  import LucideSearch from '~icons/lucide/search';
  import TablerDashboard from '~icons/tabler/dashboard';
</script>

<LucideSearch class="w-5 h-5" />
<TablerDashboard class="w-5 h-5" />

Only the icons you import are bundled, identical tree-shaking behavior to lucide-svelte named imports.

Inline SVG pattern for Svelte

For custom icons not in any library, or icons that need prop-driven path changes, write them inline:

<script>
  export let size = 24;
  export let color = 'currentColor';
  export let strokeWidth = 2;
</script>

<svg
  xmlns="http://www.w3.org/2000/svg"
  width={size}
  height={size}
  viewBox="0 0 24 24"
  fill="none"
  stroke={color}
  stroke-width={strokeWidth}
  stroke-linecap="round"
  stroke-linejoin="round"
>
  <circle cx="12" cy="12" r="10" />
  <path d="M12 8v4l3 3" />
</svg>

Note the Svelte-specific attribute format: stroke-width (hyphenated) not strokeWidth.

Dark mode with Tailwind

All three options use currentColor by default. Tailwind’s dark mode just works:

<!-- Icon inherits text color from parent -->
<div class="text-gray-700 dark:text-gray-300">
  <Search size={20} />
</div>

For CSS variable-based dark mode (without Tailwind):

<style>
  :global(:root) { --icon-default: #374151; }
  :global(.dark) { --icon-default: #d1d5db; }
</style>

<Search size={20} color="var(--icon-default)" />

Accessibility

<!-- Decorative icon inside labeled button -->
<button>
  <Download size={18} aria-hidden="true" />
  Export CSV
</button>

<!-- Icon-only button -->
<button aria-label="Delete item">
  <Trash2 size={18} aria-hidden="true" />
</button>

<!-- Standalone meaningful icon -->
<Search
  size={20}
  role="img"
  aria-label="Search"
/>

SvelteKit-specific: server-side rendering

All three libraries render correctly during SSR, the SVG markup is included in the initial HTML response. No hydration flash for icons.

Verify by checking your SSR output:

curl http://localhost:5173 | grep -o '<svg[^>]*>' | head -5

You should see <svg> elements in the HTML before any JavaScript runs.

Choosing an approach

Need Recommended
Lucide icons, simplest setup lucide-svelte
Multiple icon packs @iconify/svelte (bundled)
Auto-import, no per-file imports unplugin-icons
Custom/bespoke icons Inline SVG Svelte component

Related posts

Share this post