SvelteKit SVG Plugin

This plugin makes it possible to import SVG files as Svelte components, inline SVG code or urls.

NOTE! For Svelte 4 projects, use version 4 of this plugin

NOTE! This plugin isn't just for SvelteKit, but works for any Svelte project using Vite

Install

  • yarn add --dev @poppanator/sveltekit-svg
  • npm install -D @poppanator/sveltekit-svg

Usage

In your vite.config.js

import { sveltekit } from '@sveltejs/kit/vite'
import svg from '@poppanator/sveltekit-svg'

/** @type {import('vite').UserConfig} */
const config = {
  plugins: [
    sveltekit(),
    svg(options), // Options are optional
  ],
}

export default config

You can also pass multiple svg transformers based on paths if you want to apply different SVGO options for different SVGs

const config = {
  plugins: [
    sveltekit(),
    svg({
      includePaths: ['./src/lib/icons/', './src/assets/icons/'],
      svgoOptions: {
        multipass: true,
        plugins: [
          {
            name: 'preset-default',
            // by default svgo removes the viewBox which prevents svg icons from scaling
            // not a good idea! https://github.com/svg/svgo/pull/1461
            params: { overrides: { removeViewBox: false } },
          },
          { name: 'removeAttrs', params: { attrs: '(fill|stroke)' } },
        ],
      },
    }),
    svg({
      includePaths: ['./src/lib/graphics/'],
      svgoOptions: {
        multipass: true,
        plugins: ['preset-default'],
      },
    }),
  ],
}

Svelte usage

Import as a Svelte component:

NOTE! It's recommended that you use the ?component query string if you use the suggested type definition below. The reason is that Vite ships a type definition for *.svg which states that import Comp from './file.svg returns a string.

So providing a default type definition for *.svg is in most cases causing a conflict which will lead to TSC errors when treating such an import as a Svelte component.

So the best way to avoid errors, current and future, is to always use import Comp from './file.svg?component with the suggested type definition at the end of this file.

<script>
import Logo from "./logo.svg?component";
</script>

<Logo />

When used as a component you can also pass attributes to the SVG

<Logo width="200" />

Import as file path:

<script>
import logoUrl from "./logo.svg?url";
</script>

<img src={logoUrl} />

Import as data URL:

<script>
import logoDataUrl from "./logo.svg?dataurl";
</script>

<img src={logoDataUrl} />

In contrast to ?url this will apply SVGO optimization/transform before the the SVG is turned into a data URL

You can also pass the SVGO config option datauri as value to ?dataurl. This will, for instance, generate an URI encoded string

<script>
import logoDataUrl from "./logo.svg?dataurl=enc";
</script>

<img src={logoDataUrl} />

Import as code:

<script>
import logo from "./logo.svg?src";
</script>

{@html logo}

Options

interface Options {
  /**
   * Output type
   *
   * `dataurl` can also take the following options, which are verbatim SVGO
   * `datauri` options:
   *
   * - `?dataurl=base64` (default, same as `?dataurl`)
   * - `?dataurl=enc` URL encoded string
   * - `?dataurl=unenc` Plain SVG
   *
   * @default "component"
   */
  type?: 'src' | 'url' | 'component' | 'dataurl'
  /**
   * Verbatim [SVGO](https://github.com/svg/svgo) options
   *
   * If no options are given, the SVG will be optimized with the default SVGO
   * options.
   * If `false` SVGO will be bypassed altogether
   */
  svgoOptions?: Config | false
  /**
   * Paths to apply the SVG plugin on. This can be useful if you want to apply
   * different SVGO options/plugins on different SVGs.
   *
   * The paths are path prefixes and should be relative to your
   * `svelte.config.js` file.
   *
   * @example
   * ```
   * {
   *   includePaths: ['src/assets/icons/', 'src/images/icons/']
   * }
   * ```
   */
  includePaths?: string[]
  /**
   * Hook that lets you transform the svg to a raw Svelte component yourself,
   * before being passed to the Svelte compiler.
   *
   * @param rawSvg The raw SVG data as read from disk
   * @param splitSvg The SVG split into parts, e.g its attributes and
   *  its content
   * @returns This should return a complete Svelte component that can be passed
   *  to the Svelte compiler
   */
  preCompileHook?(rawSvg: string, splitSvg: SplitSvg): string
}

Typescript

For Typescript not to complain about file.svg?component et.al, add the following import statement to src/app.d.ts (or any .d.ts file somewhere in the path of your project where tsc can find it).

import '@poppanator/sveltekit-svg/dist/svg.d.ts'

NOTE! If you have module/moduleResolution set to NodeNext in your Typescript config, you MUST include .d.ts in the import of the SVG type definition.

Notes on using with Jest

I don't know if this still applies, but it's kept here for good measure

According to a report Jest will have trouble transforming .svg files when such is imported as a Svelte component.

The solution seems to be to add a module name mapper entry in the the jest.config.cjs file, like so:

module.exports = {
  // ... other config
  moduleNameMapper: {
    // ... other mappers
    '^.+\\.svg$': '<rootDir>/src/lib/EmptyIcon.svelte',
  },
}

where src/lib/EmptyIcon.svelte can contain just <svg />.

See the reported issue and solution

Top categories

svelte logo

Need a Svelte website built?

Hire a professional Svelte developer today.
Loading Svelte Themes