UNPKG

vite-plugin-magical-svg

Version:

An all-in-one Vite plugin that magically makes working with SVGs and bundling them a breeze

136 lines (108 loc) 6.97 kB
# Magical SVG 🪄 [![License](https://img.shields.io/github/license/cyyynthia/vite-plugin-magical-svg.svg?style=flat-square)](https://github.com/cyyynthia/vite-plugin-magical-svg/blob/mistress/LICENSE) [![npm](https://img.shields.io/npm/v/vite-plugin-magical-svg?style=flat-square)](https://npm.im/vite-plugin-magical-svg) [![GitHub Sponsors](https://img.shields.io/badge/GitHub%20Sponsors-support%20me-EA4AAA?style=flat-square)](https://github.com/sponsors/cyyynthia) [![Weekly downloads](https://img.shields.io/npm/dw/vite-plugin-magical-svg?style=flat-square)](https://npm.im/vite-plugin-magical-svg) [![Monthly downloads](https://img.shields.io/npm/dm/vite-plugin-magical-svg?style=flat-square)](https://npm.im/vite-plugin-magical-svg) An all-in-one [Vite](https://vitejs.dev/) plugin that magically makes working with SVGs and bundling them a breeze. This plugin lets you [import SVG files as components](#use-in-code), with [advanced pre-processing](#svg-processing) of assets referenced within SVGs, without the inefficient approach used by other tools. SVGs will be bundled in a spritesheet that is fast and lightweight for the browser. See [the backstory](#backstory) for more details. > [!TIP] > Can mostly be used as a [drop-in replacement of SVGR](#migrating-from-svgr) and similar tools. ## Backstory Inspired by a [tweet](https://twitter.com/_developit/status/1382838799420514317) from Preact's creator Jason Miller, I've been looking at plugins that would let me work with SVGs, as I myself did the error of embedding SVGs as React components. Shame! What I wanted was a plugin that would let me import SVGs, and make a sprite of symbols and give me the identifier I can use in `<use href='???'/>`. And I couldn't find any decent plugin that makes working with them easy. They all had a problem that made using them a pain, or outright impractical. Here's a list of the problems I encountered: - References in SVG files are never processed. `<image href='...'/>` would never get processed and the referenced asset is ignored. - The generated sprite include ALL icons, even unused ones. Just picking the right icons from a pack isn't an option. - There are no options to output to a separate file and reference it. Inlining is apparently the only way. - Selectively tell to not process a specific SVG isn't possible (e.g.: A logo, or SVGs that break when encapsulated in a symbol). - You can't make different sprites, it's only all-in-one. So I decided to make my own tool to solve all these problems. Introducing: the Magical SVG plugin. 🪄 ## Install ``` pnpm i vite-plugin-magical-svg yarn add vite-plugin-magical-svg npm i vite-plugin-magical-svg ``` ## Usage > [!NOTE] > During development, to keep HMR fast and efficient, the sprite is not produced and instead the plugin falls back to > embedding the symbol inside the generated component. ### Vite plugin setup ```js import { defineConfig } from 'vite' import magicalSvg from 'vite-plugin-magical-svg' export default defineConfig({ plugins: [ magicalSvg({ // By default, the output will be a dom element (the <svg> you can use inside the webpage). // You can also change the output to react (or any supported target) to get a component you can use. target: 'preact', // By default, the svgs are optimized with svgo. You can disable this by setting this to false. svgo: false, // By default, width and height set on SVGs are not preserved. // Set to true to preserve `width` and `height` on the generated SVG. preserveWidthHeight: false, // *Experimental* - set the width and height on generated SVGs. // If used with `preserveWidthHeight`, will only apply to SVGs without a width/height. setWidthHeight: { width: '24', height: '24' }, // *Experimental* - replace all instances of `fill="..."` and `stroke="..."`. // Set to `true` for 'currentColor`, or use a text value to set it to this value. // When enabled, use query param ?skip-recolor to not alter colors. // Disabled by default. setFillStrokeColor: true, // *Experimental* - if a SVG comes with `width` and `height` set but no `viewBox`, // assume the viewbox is `0 0 {width} {height}` and add it to the SVG. // Disabled by default. restoreMissingViewBox: true, }) ] }) ``` #### Targets - `dom` (default): exports a function you can call (takes no arguments) and returns a DOM element. - `react19`: exports a functional React component (classic runtime) - `react19-jsx`: exports a functional React component (automatic runtime) - `react`: exports a functional React component (classic runtime; wrapped in `forwardRef`) - `react-jsx`: exports a functional React component (automatic runtime; wrapped in `forwardRef`) - `preact`: exports a functional Preact component (classic runtime; wrapped in `forwardRef`) - `preact-jsx`: exports a functional Preact component (automatic runtime; wrapped in `forwardRef`) - `vue`: exports a Vue component (as if it was a `.vue` file) - `vue-vapor`: exports a Vue Vapor[^vapor] component (as if it was a `.vue` file) - `solid`: exports a Solid component - `ember`: exports an Ember component [^vapor]: Vue's high-performance subset introduced in Vue 3.6 ### Use in code ```js import MySvg from './assets/icon.svg' // Basic import, as a sprite import MySvg from './assets/icon.svg?sprite=owo' // Named sprites import MySvg from './assets/icon.svg?sprite=inline' // Special sprite, inlined in the HTML document import fileUrl from './assets/icon.svg?file' // Works like .png and other file imports ``` ### SVG processing ```xml <svg viewBox='0 0 250 250'> <image href='./assets/image.png' /> <!-- Image will be imported, bundled, and the href will be replaced --> <image href='./assets/icon.svg' /> <!-- SVG will be imported as a file (implicit ?file) --> <use href='./assets/icon.svg' /> <!-- SVG will be imported and added to the sprite --> </svg> ``` ### Tree-shaking If you import an icon but end up not using it, the icon will not be included in the final sprites. If tree-shaking has been disabled in your config, then all imported icons will be included regardless if they've been used or not. ### `exports` note This plugin **does not** respect the `exports` field when importing svg files from third-party packages such as `simple-icons`, which do not expose them. ### Migrating from SVGR This plugin can mostly be used as a drop-in replacement of SVGR; unless you're dealing with complex SVGs or need to style/animate individual parts of the SVG, the code generated by this plugin should behave just as you're used to, plus the [preprocessing of your SVGs](#svg-processing). > [!IMPORTANT] > Make sure to configure the [`target`](#targets), so the plugin produces the correct type of component. Make sure to enable `preserveWidthHeight` for best compatibility. This option is not enabled by default as it was not done historically by the plugin, and suddenly doing it would be a breaking change.