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 Yadav

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

PropTypeDefault
sizenumber24
colorstringcurrentColor
strokeWidthnumber2
classstring

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 250,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

NeedRecommended
Lucide icons, simplest setuplucide-svelte
Multiple icon packs@iconify/svelte (bundled)
Auto-import, no per-file importsunplugin-icons
Custom/bespoke iconsInline SVG Svelte component
Share this post