What the heck is an SVG sprite sheet?
If you work with me, you may have heard me mention SVG sprite sheets and wondered to yourself, “Man… he’s talking about SVG sprite sheets again, does Ryan not have any actually fun hobbies?”
And to that I say: “Thank you for asking about SVG sprite sheets.”
Now, am I a graphic designer? Not exactly, though I think I do a pretty well navigating SVGs.
SVG?
SVG, or Scalable Vector Graphic, is an XML-based way to describe two-dimensional graphics that stay crisp at any size (hence, “scalable” graphics).
The cool part, though, is browsers know what to do with SVGs and can render them like HTML elements. This allows developers to embed graphics directly, inline with other parts of a page. Also, as a generous bonus, we can style and adjust SVGs with cascading style sheets.
Sprite Sheets?
Sprite sheets are a way to have many graphics available from one resource and are used in many different ways.
In video games, sprites can be used for animating characters, actions, and environment elements. Sprite sheets have been used on the web for quite a long time as well.
An older technique for sprite sheets on the web involved having a grid of graphics within a single image file, then positioning the image in a way that showed only one graphic at a time. This was clever, though it was painful to manage.
SVG Sprite Sheets?
Combining the power of SVG with the concept of sprite sheets provides an effective way to deliver graphics, like icons, to our users.
SVG contains a feature called <symbol>
which wraps around a graphic and turns it into something reusable, ideal for graphics such as icons. Adding an id
attribute to the <symbol>
gives us a way to reference the symbol.
We can mostly add as many icons as we want, though there are some warnings here:
- Fairly complex graphics may add to the weight of the overall page.
- Similarly, having a excessive amount of icons (think hundreds or thousands), will also cause performance issues
Combined with the <symbol>
element, SVG also provides us a way to use the symbols, with <use>
(duh, I guess). MDN says:
The
<use>
element takes nodes from within the SVG document, and duplicates them somewhere else. The effect is the same as if the nodes were deeply cloned into a non-exposed DOM, then pasted where theuse
element is, much like cloned template elements.
Neat! But wait, you might be thinking to yourself:
“I just remembered - writing SVGs by hand is a surefire path to madness, I thought you said this was cool”
…and you would be correct, this would be a pain to manage.
Luckily there are some great tools that can help us accomplish creating SVG sprite sheets so much easier.
Generators
There are several SVG sprite sheet generators online where you can upload your icon SVG files and it’ll spit out a sprite sheet.
I have used https://svgsprit.es/ for this type of thing previously, mostly as a quick way to get something together. A much more preferable way of creating these sprite sheets might be something a bit more automated.
Automating
So, I’m sure there are several ways to go about this, but the way I do this is by implementing this as a step within the build process.
I primarily use Vite as my build tool of choice, so let’s use that to build a SVG sprite sheet.
Setup
Let’s initialize a new Vite project for the purposes of this demonstration, but if you are already working on an existing Vite project, this will fit in nicely to your build process.
To create a new Vite project, navigate to your project directory and run the following in the terminal:
npm create vite@latest
Provide a name for the project and follow the prompts to complete. I named my project svg-sprite-sheets
, selected to work with vanilla JavaScript, but feel free to select whatever matches your preferences or project requirements.
Navigate into the freshly created project and run npm install
to install dependencies.
Plugins
One of the many reasons I prefer Vite is due to it’s vast community of developers who make awesome plugins and… hey would you look at that:
npm install -D vite-svg-sprite-wrapper
Yep, there is a Vite plugin to do exactly what we want to do, isn’t that convenient?
I love open source software! Shoutout to vshepel on Github for this Vite plugin!
Open up your project’s vite.config.js
file, or if there is not one in your project directory, create a vite.config.js
file and include the following:
import { defineConfig } from 'vite';
import ViteSvgSpriteWrapper from 'vite-svg-sprite-wrapper';
export default defineConfig({
plugins: [
ViteSvgSpriteWrapper()
]
})
We need to pass in some options, specifically where to locate the icons and the output directory for the sprite sheet.
import { defineConfig } from 'vite';
import ViteSvgSpriteWrapper from 'vite-svg-sprite-wrapper';
export default defineConfig({
plugins: [
ViteSvgSpriteWrapper({
icons: './src/icons/**/*.svg',
outputDir: './public/'
})
]
})
The icons
property points to the directory containing your SVG icons. We can use the **/*.svg
syntax to recursively look for SVGs within the src/icons/
directory, if they happen to be nested into sub-directories.
For the outputDir
, we can send the generated sprite sheet to Vite’s public/
directory. Anything within public/
is automatically copied, unprocessed, into the built dist/
folder, making the sprite sheet available to use within pages.
Now, whenever we run npm build
to build the project, this plugin will do the following:
- Grab all of the icons out of
src/icons/
- Combine and format the icons as
<symbol>
elements - Output a
sprite.svg
file to thepublic/
directory
Adding new icons
Just drop them into the src/icons/
directory and re-run the build!
Using Sprite Sheets
Now that we have a sprite sheet, let’s figure out how to actually use it.
I mentioned earlier that SVG has another feature called <use>
that allows you to reference the id
of a <symbol>
inside our public/sprite.svg
file.
If the sprite sheet is small enough, one thing you can do is embed it directly on the page, the you could do this:
<svg class="icon" aria-hidden="true">
<use xlink:href="#arrow"></use>
</svg>
Which would copy the SVG symbol with the id
of arrow
inline, wherever you are using the icon.
This presents a problem though, anytime we update the sprite sheet, we would need to re-copy it’s code into the HTML somewhere - not ideal.
Instead, we can reference the path to the SVG and the id
of the icon:
<svg class="icon" aria-hidden="true">
<use xlink:href="./sprite.svg#arrow"></use>
</svg>
Note: Vite is smart enough to know where to find “public” files, so you do not need to include public/
within the path to the sprite sheet.
Now you should see the corresponding icon in the browser!
Other ways
You can embed an SVG sprite icon in several other ways as well:
<!-- as an image tag -->
<img src="./sprite.svg#arrow" />
<!-- as an iframe -->
<iframe src="./sprite.svg#arrow">
</iframe>
<!-- as an object -->
<object type="image/svg+xml" data="/sprite.svg#arrow">
<!-- provide a fallback graphic -->
<img src="./arrow.png">
</object>
Similarly, SVG sprite sheets can be used in CSS:
.element {
background: url('./sprite.svg#arrow');
}