Skip to content

kiwilan/unplugin-svg-transformer

Repository files navigation

Banner with unplugin logo and unplugin-svg-transformer title, a SVG logo inserted for svg in title

unplugin-svg-transformer

unplugin version downloads license

tests codecov style

Import easily your SVG. Powered by unplugin.

Designed to be a replacement of kiwilan/nuxt-svg-transformer, a Nuxt module.

Use SVG into modern tools is not easy, especially when you want to use SVG as component. This plugin will parse your SVG files and create a cache file to import them easily with a library index file. It works with any framework with Vite/Webpack, but some components are ready-to-use for Vue, Nuxt and React. You could also create your own component for your favorite framework. Built for TypeScript, but works with JavaScript.

Note

If you want to use SVG from icon sets, you should use unplugin-icons, a very good plugin from unplugin. unplugin-svg-transformer is only for custom SVG.

import type { SvgName } from "unplugin-svg-transformer/icons";
import { importSvg, svgList } from "unplugin-svg-transformer/icons";

const icon: SvgName = "svg-name";
const svg = await importSvg("svg-name"); // param fully typed (SvgName), string output
const list = svgList; // as Record<SvgName, () => Promise<{ default: string }>>

Note

A demo is available on Stackblitz or directly on github.com/ewilan-riviere/unplugin-svg-transformer-example.

Features

Roadmap

  • Add Nuxt 2 support
  • Add more tests
  • Add SVGO support

Install

npm i unplugin-svg-transformer -D
# or
pnpm install unplugin-svg-transformer -D
# or
yarn add unplugin-svg-transformer -D
Vite
// vite.config.ts
import svgTransformer from "unplugin-svg-transformer/vite";

export default defineConfig({
  plugins: [
    svgTransformer({
      /* options */
    }),
  ],
});

Examples for Vue, React and Svelte:


Rollup
// rollup.config.js
import svgTransformer from "unplugin-svg-transformer/rollup";

export default {
  plugins: [
    svgTransformer({
      /* options */
    }),
  ],
};


Webpack
// webpack.config.js
module.exports = {
  /* ... */
  plugins: [
    require("unplugin-svg-transformer/webpack")({
      /* options */
    }),
  ],
};


Nuxt
// nuxt.config.ts
export default defineNuxtConfig({
  buildModules: [
    "unplugin-svg-transformer/nuxt", // https://github.com/kiwilan/unplugin-svg-transformer
  ],
  svgTransformer: {
    /* options */
  },
});

This module works for Nuxt 3 only

Example: live/nuxt3


Vue CLI
// vue.config.js
module.exports = {
  configureWebpack: {
    plugins: [
      require("unplugin-svg-transformer/webpack")({
        /* options */
      }),
    ],
  },
};


esbuild
// esbuild.config.js
import { build } from "esbuild";
import svgTransformer from "unplugin-svg-transformer/esbuild";

build({
  plugins: [
    svgTransformer({
      /* options */
    }),
  ],
});


Usage

unplugin-svg-transformer works with any framework with Vite/Webpack, but some components are ready-to-use for Vue and React. You could also create your own component for your favorite framework (only Javascript, TypeScript, Vue 3, React, Svelte and Nuxt 3 have been tested).

Options

Note

Nuxt 3 have some built-in options defined statically, because it uses amazing Nuxt 3 features. If ✅ option is available, otherwise is static.

Name Type Description Default Nuxt
cacheDir string SVG files will be added to cache directory. ./node_modules/unplugin-svg-components/cache ./.nuxt/icons
fallback string Default SVG displayed when the SVG is not found. <svg>...</svg>
global boolean Create icons.d.ts file at the root of your project. false
libraryDir string Directory where icons.ts will be created. ./src ./.nuxt
svgDir string Directory where your SVG files are located. ./src/svg ./assets/svg
svg.classDefault string[] Add default classes to all SVG. undefined
svg.clearSize all, parent, none Clear width and height attributes from SVG (can be all, just parent on <svg> or none). none
svg.clearClass all, parent, none Clear classes from SVG (can be all, just parent on <svg> or none). none
svg.clearStyle all, parent, none Clear inline styles from SVG (can be all, just parent on <svg> or none). none
svg.currentColor boolean Add fill="currentColor" or stroke="currentColor" to all SVG. false
svg.inlineStyleDefault string[] Add default inline style to all SVG. undefined
svg.sizeInherit boolean Add inline style height: inherit; width: inherit;. false
svg.title boolean Add title to SVG string. false
useTypes boolean Use types, if you want to use JavaScript instead of TypeScript. true true
warning boolean Trigger a warning when the SVG is not found. false

Add your SVG files

In plugin options, you can add a directory to watch SVG files: svgDir. By default, it's ./src/svg (for Nuxt 3, it's ./assets/svg). Just put your SVG files into this directory and they will be parsed and added to library file, icons.ts.

Note

You can use SVG nested directories, but you can't have two SVG files with the same name.

An example of svgDir directory:

├── src
│   ├── svg
│   │   ├── download.svg
│   │   ├── social
│   │   │   └── twitter.svg
│   │   └── vite.svg
│   └── ...
└── ...

This example will give you this list: ['download', 'social/twitter', 'vite'].

Library file

In plugin options, you can add a directory to choose where to create library file: libraryDir. By default, it's ./src (for Nuxt 3, it's ./.nuxt). A library file will be created, icons.ts (or icons.js if useTypes is set to false), into this directory. This file will list all SVG files, used by importSvg function.

With TypeScript, SvgName type is available. And with JavaScript or TypeScript, you can use svgList and importSvg function. SVG list is updated when you add, remove or update a SVG file.

Note

A symlink of this file will be created into unplugin-svg-transformer/icons.

An example of icons.ts file:

export type SvgName = "download" | "social/twitter" | "vite" | "default";
export const options = {...};
export const svgList: Record<SvgName, () => Promise<{ default: string }>> = {
  download: () => import(".../cache/download"),
  "social/twitter": () => import(".../cache/social/twitter"),
  vite: () => import(".../cache/vite"),
  default: () => import(".../cache/default"),
};

export async function importSvg(name: SvgName): Promise<string> {
  // ...
}

if (typeof window !== "undefined") {
  window.ust.options = options;
  window.ust.svgList = svgList;
  window.ust.importSvg = importSvg;
}

Import SVG

You can easily import a SVG file with importSvg function from unplugin-svg-transformer/icons and use SvgName type (globally registered) to validate your SVG file name. svgList function list all SVG files, used by importSvg function.

import type { SvgName } from "unplugin-svg-transformer/icons";
import { importSvg, options, svgList } from "unplugin-svg-transformer/icons";

// `SvgName` type represents SVG file name
const icon: SvgName = "svg-name";
// importSvg function is async, you can use `await` or `then` method
const icon = await importSvg("svg-name");
// or
const icon = "";
importSvg("svg-name").then((svg) => {
  icon = svg;
});

const fallback = options.fallback; // All options are available

You can use Window to access svgList and importSvg functions from ust (not SSR compatible).

const svg = await window.ust.importSvg("svg-name");

Ready-to-use components

With some frameworks, you don't have to create your own component, you can use ready-to-use components.

Warning

Assure you have import unplugin-svg-transformer/icons into main.ts or app.ts (or app.js) when you use ready-to-use components: import 'unplugin-svg-transformer/icons' (except for Nuxt). Why? Because ready-to-use components use window to access to importSvg function (of course for Nuxt 3, you don't have to import unplugin-svg-transformer/icons because component use #icons alias).

// main.ts
import "unplugin-svg-transformer/icons";
  • For Vue 3, you can use a plugin to register globally SvgIcon component with SvgTransformerPlugin from unplugin-svg-transformer/vue and use SvgIcon component directly. But you can just import SvgIcon component from unplugin-svg-transformer/vue and use SvgIcon component.
  • For React, you can import SvgIcon component from unplugin-svg-transformer/react.
  • For Nuxt 3, you have a globally registered SvgIcon component, you can use SvgIcon component directly. You have an alias to use easily icons: #icons, same as unplugin-svg-transformer/icons.

All ready-to-use components have a name prop, based on SVG file name. You can use name prop to validate SVG file name.

Prop name Prop type Required Prop description
name SvgName SVG file name
title string Add title to SVG
reactive boolean Add watch to update SVG if name update (not available on React)

Create your own component

  • For Svelte, no component available, you have to create your own, you can use example: ./examples/svelte/src/lib/SvgIcon.svelte.
  • For vanilla JS or TS, you can import importSvg function from unplugin-svg-transformer/icons to import SVG file.
  • For Vue or React, you can create your own component like with Svelte.

TypeScript or JavaScript

To use JavaScript only, set useTypes option to false, but this plugin is built for TypeScript first. You can use SvgName type to validate your SVG file name.

import type { SvgName } from "unplugin-svg-transformer/icons";

const icon: SvgName = "svg-name";

If you use only JavaScript you can import your SVG with same way without type validation.

Global type

If you use Vite (with Vue, React or Svelte) or Nuxt, SvgName is globally imported by default. But if you use another bundler or vanilla JS/TS and you want to globally import SvgName, you can add global option to true in plugin options to create icons.d.ts at root of project to add SvgName globally. You might have to add include: ["icons.d.ts"] into tsconfig.json.

Note

With Vue, React and Svelte, this plugin use vite-env.d.ts to add SvgName globally, global option is not needed (same for Nuxt).

// tsconfig.json, if you enabled `global` option.
{
  "include": ["icons.d.ts"]
}

And enable global option in plugin options.

// vite.config.ts (or webpack.config.js, rollup.config.js, ...)
import svgTransformer from "unplugin-svg-transformer/vite";

export default defineConfig({
  plugins: [
    svgTransformer({
      global: true,
    }),
  ],
});

Advanced examples

Vue 3

An example with Vue 3 and Vite.

Click to see example

You can skip SvgTransformerPlugin registration, this plugin will only load SvgIcon component globally, you can import SvgIcon component from unplugin-svg-transformer/vue and use SvgIcon component. But you have to import unplugin-svg-transformer/icons in main.ts if you want to use SvgIcon component.

// main.ts
import { createApp } from "vue";
import App from "./App.vue";
+import { SvgTransformerPlugin } from 'unplugin-svg-transformer/vue'
+import "unplugin-svg-transformer/icons";

createApp(App)
+ .use(SvgTransformerPlugin)
  .mount("#app");


Inertia

An example with Laravel Jetstream (Inertia) and Vite. This example will use TypeScript, but it works with JavaScript. For TypeScript, you will have to create tsconfig.json file at root of project, here a example.

Click to see example

Note

This example use Vue 3, but it works with React or Svelte.

To use TypeScript, update vite.config.js to vite.config.ts and just add unplugin-svg-transformer/vite to plugins array.

Warning

Don't forget to replace resources/js/app.js to resources/js/app.ts into laravel-vite-plugin options.

import { defineConfig } from "vite";
import laravel from "laravel-vite-plugin";
import vue from "@vitejs/plugin-vue";
+import svgTransformer from "unplugin-svg-transformer/vite";

export default defineConfig({
  resolve: {
    alias: {
      "@": "/resources/js",
      "~": "/",
    },
  },
  plugins: [
    laravel({
+     input: ["resources/js/app.ts"],
      ssr: "resources/js/ssr.js",
      refresh: true,
    }),
    vue({
      template: {
        transformAssetUrls: {
          base: null,
          includeAbsolute: false,
        },
      },
    }),
+   svgTransformer({
+     svgDir: "./resources/js/Svg",
+     libraryDir: "./resources/js",
+     global: true,
+   }),
  ],
});

Just replace app.js to app.ts into resources/js (and root Blade file).

// app.ts
import type { DefineComponent } from "vue";
import { createApp, h } from "vue";
import { createInertiaApp } from "@inertiajs/vue3";
import { resolvePageComponent } from "laravel-vite-plugin/inertia-helpers";
+import { SvgTransformerPlugin } from "unplugin-svg-transformer/vue";
+import 'unplugin-svg-transformer/icons'

createInertiaApp({
  title: (title) => `${title} - App Name`,
  resolve: (name) =>
    resolvePageComponent(
      `./Pages/${name}.vue`,
      import.meta.glob("./Pages/**/*.vue")
    ) as Promise<DefineComponent>,
  setup({ el, App, props, plugin }) {
    const app = createApp({ render: () => h(App, props) })
      .use(plugin)
+     .use(SvgTransformerPlugin);

    app.mount(el);
  },
});

And you can use globally registered SvgIcon component.

<template>
  <div>
    <SvgIcon name="svg-name" />
  </div>
</template>


React

An example with React and Vite.

Click to see example

You have to import unplugin-svg-transformer/icons only once in main.tsx (or app.tsx), you can use SvgIcon component if you import it from unplugin-svg-transformer/react.

// main.tsx
import React from 'react'
import ReactDOM from 'react-dom/client'
+import { SvgIcon } from 'unplugin-svg-transformer/react'
import './index.css'
+import 'unplugin-svg-transformer/icons'

const root = document.getElementById('root') as HTMLElement
ReactDOM.createRoot(root).render(
  <React.StrictMode>
    hello
+   <SvgIcon name="svg-name" className='icon' />
  </React.StrictMode>,
)


Nuxt 3

An example with Nuxt 3.

Click to see example

Installation on Nuxt 3 is easily with nuxt.config.ts file.

// nuxt.config.ts
import { defineNuxtConfig } from "nuxt3";

export default defineNuxtConfig({
  buildModules: [
    "unplugin-svg-transformer/nuxt", //
  ],
  svgTransformer: {
    /* options */
  },
});

And you can use globally registered SvgIcon component or #icons alias.

<script setup lang="ts">
import { importSvg } from "#icons"; // `#icons` alias

const svg = ref<string>("");
importSvg("svg-name").then((icon) => {
  svg.value = icon;
});
</script>

<template>
  <div>
    <div v-html="svg" />
    <SvgIcon name="svg-name" />
  </div>
</template>


Testing

pnpm test

Local

pnpm package

In package.json:

{
  "devDependencies": {
    "unplugin-svg-transformer": "file:~/unplugin-svg-transformer.tgz"
  }
}

Changelog

Please see CHANGELOG for more information on what has changed recently.

Credits

License

The MIT License (MIT). Please see License File for more information.