Disclaimer - This post assumes full browser support for the latest SVG spec. If you are building for Internet Explorer (I'm sorry), there will be parts of this post that may not work for you.
What we will cover in this post
- What is an SVG?
- Common methods for using SVG (
img
, inline,url
, etc.) - How to control the size and scale of an SVG
- What is
viewBox
? - Common SVG elements, what they do, and how to use them
- Styles -
fill
,stroke
- Shapes -
path
,<circle>
,<line>
,<rect>
- Structural -
<svg>
,<g>
,<defs>
,<use>
- Text -
<text>
,<title>
- Gradients -
<linearGradient>
,<stop>
- Animations (basic) -
<animate>
- Styles -
- Some common questions
<svg>
vs.<canvas>
- Is SVG different than HTML?
What we will not cover
- Advanced SVG animations
- Advanced SVG elements such as
<feBlend>
,<mask>
, and<polyline>
(just to name a few)
What is an SVG (Scalable Vector Graphic)?
Before I answer that, here are some must-know, quick facts about SVGs:
- They are an XML specification (see Is SVG the same as HTML?)
- The current spec is
1.1, Version 2
and is maintained by the W3C SVG Working Group- 1.1 Version 2 official spec document (supported in all major browsers)
- SVG 2 official spec recommendation (not supported yet)
- If you ever need an "all-encompassing" guide to SVGs, check out Everything you need to know about SVG from CSS Tricks which has a fairly comprehensive and friendly approach to the topic. MDN's Guide is also a great resource.
- Finally, here is the SVG reference documentation
Don't want to read about SVGs for the next week of your life? In this post, I'm going to take the most practical and important aspects of SVG and break them down throughout this post. To start, let's ask the question again... What is an SVG?
An SVG is an image built with math.
Think of a 2-dimensional rectangle (that is an SVG below, check your dev-tools).
This rectangle has a width of 300px
and a height of 80px
.
Now imagine that the top-left corner has coordinates of x=0, y=0
. How do we build this image with math?
A naive approach might be:
- Start at
0, 0
- Draw a 2px black line from
0, 0
towidth, 0
- Draw a 2px black line from
width, 0
towidth, height
- Draw a 2px black line from
width, height
to0, height
- Draw a 2px black line from
0, height
to0, 0
While incredibly oversimplified, this is the basic concept of SVGs, and under the hood, here is the actual implementation of the rectangle above!
<svg
viewBox="0 0 300 80"
width="300"
height="80"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M0 0 L300 0 L300 80 L0 80 L0 0 Z"
style="stroke-width: 2; stroke: black; fill: none;"
/>
</svg>
Popular software like Adobe Illustrator and Inkscape use SVGs as their native file formats, and as you can imagine, SVGs can be infinitely complex (here's someone drawing an RV Van with SVGs)
All major browsers support SVGs and they are the core of popular visualization libraries such as D3.js.
Why use SVG?
SVG is a great choice of web graphic when the following things matter to you:
- You want the graphic to maintain clarity at all sizes
- You need a really small graphic (logo, icon, etc.)
- You need a graphic with flexible sizing and you don't want to create many versions of the same file to achieve this
- You want advanced graphic colors and animations
My rule of thumb is to use an SVG when a basic png
or jpg
graphic just won't cut it (sizing, clarity, animations, etc.).
5 ways to render SVGs in the browser
So... How do you use SVGs?
- HTML
img
element - easy to use and fully responsive (very common usage) - HTML
iframe
element - generally, iframes should be for embedding external webpages (not SVGs), so at the risk of getting yelled at by someone more knowledgeable on this subject, I don't see any use-case foriframe
+SVG
- HTML
embed
andobject
elements - I would not recommend either of these as they are outdated with better alternatives. There is one arguable use case forobject
though, which is a PDF embed. - CSS
url
- great for setting backgrounds, custom bullet point icons, and site logos inside ana
tag - Inline SVG element - great for when you want full control of the SVG (scaling, colors, animations, etc.)
Below is a Codepen with examples of each usage.
Note: when inserting a raw SVG element into HTML, you must provide a namespace equal to xmlns="http://www.w3.org/2000/svg"
. This ensures that when different dialects of XML (what SVG is uses) are used within a single HTML document, there are no clashes between tags. For example, both MathML and the SVG spec support a <title>
tag. Without this namespace, the browser wouldn't know which tag to use!
How to control the size and scale of an SVG
Scaling SVGs depends on how you render it in the DOM. While we saw many methods for doing this, for the remainder of this post, we'll be focusing on the inline and img
usage of SVGs as they are the most practical and common ways to use SVG.
Scaling an Image SVG
If you want a fast and easy way to responsively scale an SVG, use an img
tag and set a width on it.
<!-- This SVG will scale to 200px and maintain its aspect ratio and clarity -->
<!-- Note: this assumes that either explicit dimensions or a viewBox attributes has been set on the referenced SVG (more on this soon) -->
<img src="some-graphic.svg" width="200px" />
<!-- This also works -->
<img src="some-graphic.svg" style="width: 200px;" />
Here are some considerations to this approach:
- By using
img
, you get the benefit of image caching - By using
img
, you lose the ability to style and animate the SVG
Note: the SVG file you reference must have either dimensions already set or the viewBox
attribute set. If none of these are present, the SVG will be a fixed size and will not scale with the image size. We will talk more about these attributes in subsequent sections.
What is an SVG "Viewport"?
One of the most important concepts to understand with SVG is the viewport.
You can think of this as the "lens" or "portal" that you are viewing the actual SVG from. Below, I have created 2 SVG graphics and have put a gray border on the "viewport". The SVG is exactly the same in both, but as you can see, the viewport is 48px x 32px
in the left image and 64px x 64px
in the right image.
Even though the SVG is the same, the left image appears to be cropped because the "lens" / "portal" / "viewport" that we're looking through is smaller than the actual SVG.
Go ahead, drag the bottom right corner of both and see what happens.
Think of this like an airplane window. Does the sky still exist when the window is closed? Of course! You just have to open it wider to see the sky.
An analogy to remember:
- Plane Window = SVG Viewport
- Sky = SVG contents
Note: did you notice what happened to the SVG size when you changed the size of the viewport? It stayed the same... We'll talk about ways to scale the SVG as its viewport size changes later in this post.
How to adjust the SVG viewport size
There are several ways to set the viewport of an SVG:
<!-- Use the default (this will either be 350 x 150 or the full width of the container depending on the browser and render method) -->
<svg />
<!-- Set the width and height attributes -->
<svg width="200px" height="200px" />
<!-- Set CSS styles (will override the width and height attributes) -->
<svg style="width: 200px; height: 200px;" />
How SVG View Box works
If the SVG viewport defines the size in pixels of that proverbial "window" / "portal" / "lens" that we are viewing the SVG from, then what does viewBox
do?
It sounds awfully similar to "viewport" doesn't it? There are hundreds of tutorials about this stuff online and many of them try to visualize this concept. I personally find these explanations confusing and misdirected in some cases because what viewBox
really does is two things, and both are mathematical by nature:
- It mathematically links or "maps" the viewport pixels to internal "units"
- It defines the aspect ratio of the SVG
The viewBox attribute accepts 4 parameters, defined as viewBox="min-x min-y width height"
:
min-x
- the X origin coordinate of the view boxmin-y
- the Y origin coordinate of the view boxwidth
- the width of the SVG (internal units)height
- the height of the SVG (internal units)
So what are these "internal units"? You might see them defined as "user space units" in official documentation, but I don't find this concept very intuitive. Let's look at the mathematical relationship between SVG viewport and SVG viewBox
with a simple example:
<svg width="200px" height="100px" viewBox="0 0 200 100" />
This is what I call the "base case" because both the absolute size and the aspect ratio are the same between the viewport and the view box.
- Viewport aspect ratio is
200px / 100px = 2:1
viewBox
aspect ratio is200 units / 100 units = 2:1
Let's start with min-x
and min-y
because these are relatively easy. If you created an X/Y coordinate system on your viewport, the min-x
and min-y
values say, "the SVG internal coordinate system will start at these values relative to the browser (viewport) coordinate system."
The more challenging values are width
and height
because they are a mapping of values from "browser space" (viewport width and height) to "user space" (internal SVG width and height). Remember, SVGs are "graphics made by math", so in order to infinitely scale their size, we need to have a "mapping" from non-infinitely-scalable browser pixels to infinitely scalable SVG "units".
To map pixels to "units", we simply use the formula:
viewport dimension / viewBox dimension = internal px / unit
In this case, the viewport width is 200px
divided by the viewBox
width of 200units
= 1px / unit
. So we can say that for every viewport pixel, there will be one internal SVG pixel, and therefore, our "zoom" level is 100%.
So what is this "base case" example telling us? Let's look at a simple ellipse SVG and walk through how each viewBox
property is affecting the final result.
View Box Example - Rectangle
Take a look at the following SVG:
<svg
xmlns="http://www.w3.org/2000/svg"
width="200px"
height="100px"
viewBox="0 0 200 100"
style="border: 1px solid #64748b;"
>
<rect x="50" y="25" width="100" height="50" style="fill: #cbd5e1" />
</svg>
Here's what that looks like:
Breaking this down:
- Viewport dimensions are
200px x 100px
(what we see in the browser, represented with the dark blue border) - 1 width "unit" =
200px / 200 = 1px
(viewport width / viewBox width
) - 1 height "unit" =
100px / 100 = 1px
(viewport height / viewBox height
) - Ellipse dimensions are
100 units x 50 units
, or translated,100px x 50px
(designated by the radius attributesrx
andry
)
Using our knowledge of viewBox
, how could we make the rectangle occupy the full viewport (200px x 100px) without changing the internal height or width units?
First off, here's what we don't want to do:
- I do not want to change the size of the viewport, so we won't change the width or height of the svg element
- I should not alter the internal SVG (ellipse) dimensions because this is impractical with most SVG elements as they are far more complex than this one, and changing the internal values for each SVG element would be impossible on this large of a scale!
To get my intended result, there are two steps.
Step 1: Find the correct rectangle size
My target rectangle dimensions are 200px x 100px, so all I need to do is re-map the ratio between viewport dimensions and viewBox
unit values! It is important to keep the same aspect ratio (2:1).
- What is my new internal unit mapping?
- width:
200px / 100units = 2px / unit
(i.e. each internal unit is now 2 pixels rather than our previous 1 pixel) - height:
100px / 50units = 2px / unit
- width:
- What are the new internal pixel values of the rectangle?
- width:
100units * 2px/unit = 200px
- height:
50units * 2px/unit = 100px
- width:
Step 2: Find the new X/Y coordinates
The rectangle is mapped 50 units
offset from the left edge of the viewport, and 25 units
offset from the top edge of the viewport. With our new mapped values, this means...
- Left offset =
50units * 2px/unit = 100px
- Top offset =
25units * 2px/unit = 50px
If we don't make any changes to the min-x
and min-y
values and only update the viewBox
width and height values, here's what we get:
Remember, the SVG rect
element is still there and correctly sized, you just can't see all of it because the viewport isn't oriented correctly.
What we want is for the rectangle's top left edge not to be offset by 100px
and 50px
, but by 0
pixels! Intuitively, you'd think that setting viewBox="0 0 100 50"
would achieve this, but that would only work if our rect
element had values of x=0 y=0
. To make our rectangle start at the 0,0
origin point of the viewport, we need to reverse the offsets that exist on rect
(x=50 y=25
) by adding these to our view box. So in summary, we end up with viewBox="50 25 100 50"
.
Here is the final code and result:
<svg
xmlns="http://www.w3.org/2000/svg"
width="200px"
height="100px"
viewBox="50 25 100 50"
style="border: 1px solid #64748b;"
>
<rect x="50" y="25" width="100" height="50" style="fill: #cbd5e1" />
</svg>
To bring this full circle, let's take another look at our SVG from earlier. This time, I've added slider controls so you can adjust the viewBox
attribute on the SVG. Not only can we resize the SVG viewport (size in browser pixels), but now, we can also resize the SVG View Box.
Remember:
- If you want the "portal" from which you're viewing the SVG to be larger/smaller in the web browser, adjust the viewport (drag the bottom right corner)
- If you want the SVG to appear larger / smaller, adjust the
viewBox
width and height values - If you want the SVG position within the viewport to change, adjust the
min-x
andmin-y
values ofviewBox
That's it! Have fun playing around with the demo and I'll see you in the next section.
Maintaining SVG aspect ratios
Building off the section above, let's look at a trickier example:
<svg
viewBox="0 0 100 200"
width="100"
height="100"
xmlns="http://www.w3.org/2000/svg"
>
<circle r="50" cx="50" cy="50" />
</svg>
Notice anything interesting here?
- Viewport width = 100px
- Viewport height = 100px
- View Box = 0 0 100 200
The viewBox
dimensions (100px x 200units = 1:2) have a different aspect ratio than the viewport (what user sees in browser) dimensions (100px x 100units = 1:1). That means...
- One internal SVG width "unit" equals
100px / 100units = 1px/unit
- One internal SVG height "unit" equals
100px / 200units = 0.5px/unit
In other words, the height is going to appear smaller because we're allocating fewer pixels per unit to it. In the demo below, you can see this effect happening in the first SVG. What you'll also notice is that I've added an attribute called preserveAspectRatio which is specifically designed for scenarios like this where the viewBox
aspect ratio is different than the viewport aspect ratio. I've pre-selected the none
which shows what happens when aspect ratio is not preserved, but the browser default is the xMidYMid
value, which you can select in the dropdown and see how the browser "intelligently" keeps this SVG looking good.
What are the most common SVG elements?
The list below is a hand picked list of SVG elements that I think every developer should at least be familiar with. We'll walk through each with a few examples.
- Styles -
fill
,stroke
- Shapes -
path
,<circle>
,<line>
,<rect>
- Structural -
<svg>
,<g>
,<defs>
,<use>
- Text -
<text>
,<title>
- Gradients -
<linearGradient>
,<stop>
- Animations (basic) -
<animate>
How to style SVG elements with presentational attributes
SVG elements can be styled very similarly to HTML elements. You can select them with CSS selectors and apply many of the built-in CSS styling properties.
The two properties that differ that you'll want to remember are fill
and stroke
. Typically, with HTML, you would use something like this to style a div
element:
<div
style="width: 200px; height: 20px; background-color: red; border: 2px solid black;"
/>
Which gives you this:
But if we try that same thing on an SVG rect
, it doesn't work so well:
<svg width="200px" height="20px" xmlns="http://www.w3.org/2000/svg">
<rect
style="width: 200px; height: 20px; background-color: red; border: 2px solid black;"
/>
</svg>
That is because with SVG elements, we control the styling via presentational attributes, which can also be used in pure CSS. There are many presentational attributes to choose from, but the two that you'll want to focus on are stroke
and fill
. Let's try that example again:
<svg width="200px" height="20px" xmlns="http://www.w3.org/2000/svg">
<rect
style="width: 200px; height: 20px; stroke-width: 2; stroke: black; fill: red;"
/>
</svg>
Using the Style Tag
One handy way to define styles within an SVG is using a <style>
tag, which is an SVG element that behaves similarly to an HTML style tag. We can convert the example above to a much cleaner version like this:
<svg width="200px" height="20px" xmlns="http://www.w3.org/2000/svg">
<style>
.rectangle {
width: 200px;
height: 20px;
stroke-width: 2;
stroke: black;
fill: red;
}
</style>
<rect class="rectangle" />
</svg>
SVG Primitives
Shape elements are the "primitives" of SVG. For all shape possibilities, see the shape reference and the graphic reference.
What is an SVG Path?
While you will generally not use path
by hand (that's what Adobe Illustrator / Inkscape / Figma, etc. are for), it is important to understand what it does.
You can think of a path
as a "group of lines". They can be connected, but don't have to be. A path
has a very simple attribute API that accepts a single attribute, d
. The d
attribute is a complex attribute that accepts an arbitrarily-sized list of "commands" that will "draw" the path to the screen. You can see the full list of commands here, but just to give you a taste, let's walk through a few examples.
<!-- Example 1 -->
<path d="M10,10 L10,50 H80 V20 Z" style="fill: none; stroke: red;" />
This can be interpreted as:
M10,10
- Move to coordinates10,10
, relative to the0,0
origin at the top left corner of the SVG viewportL10,50
- Draw a line from the starting point of10,10
to an ending point of10,50
H80
- Draw a horizontal line from the prior ending point of10,50
to an X coordinate of80
V20
- Draw a vertical line from the prior ending point of80,50
to a Y coordinate of20
Z
- close the path from the prior ending coordinate of80,20
to the start coordinate of10,10
<!-- Example 2 -->
<path d="M0,0 L50,50 M80,20 L40,10" style="fill: none; stroke: red;" />
This can be interpreted as:
M0,0
- start at the origin point of the SVG viewportL50,50
- Draw a line from the starting point of0,0
to an ending point of50,50
M80,20
- without drawing anything, move back to a coordinate of80,20
L40,10
- From the prior end point of80,20
, draw a line to40,10
SVG Path vs Line
A line
is an implementation of a path
. The following two SVG elements will create the same result:
<!-- This path... -->
<path d="M0,80 L100,20" />
<!-- is equivalent to this line -->
<line x1="0" y1="80" x2="100" y2="20" />
SVG Path vs Rect
Likewise, you can also replicate a rect
with a path.
<!-- This path... -->
<path d="M0,0 L100,0 L100,100 L0,100 Z" />
<!-- is equivalent to this rectangle-->
<rect width="100" height="100" />
SVG Path vs. Circle
While I could keep replicating all these built-in shapes with a basic path
element, you'll start to see with more complex shapes like a circle
that this quickly becomes tedious:
<!-- This path... -->
<path d="M 0,25 a 25,25 0 1,1 50,0 a 25,25 0 1,1 -50,0" />
<!-- is equivalent to this circle -->
<circle r="25" cx="25" cy="25" />
The point here is that all shape elements are just implementations of the primitive path
element.
How to use SVG Structural Elements
You can think of these structural elements as "organizational tools" for your SVGs. I won't dive too deep here, but let's walk through one example that incorporates all the concepts you need to know in one.
<svg width="200" height="200" xmlns="http://www.w3.org/2000/svg">
<defs>
<g id="custom-elements" fill="skyblue" stroke-width="2" stroke="navy">
<circle r="50" cx="50" cy="50" />
<rect width="20" height="20" x="100" y="160" />
</g>
<g id="more-elements">
<!-- A cleaner way to style a group -->
<style>
.red-circle {
fill: red;
stroke: black;
stroke-width: 4;
}
</style>
<circle r="10" cx="150" cy="100" class="red-circle" />
</g>
</defs>
<!-- Without these, nothing is rendered to the browser -->
<use href="#custom-elements" />
<use href="#more-elements" />
</svg>
A couple of important notes with this example:
- You can see we are defining via
defs
a "group" (g
) of SVG elements, which does not render the elements to the browser. Alone, this SVG would not show anything.defs
is purely an organizational tool that makes your SVGs more readable and accessible. - We then take the
id=custom-elements
andid=more-elements
set on theg
and<use href="#custom-elements" />
them, which is the code that actually renders the SVG elements to the browser. - Notice how we've set several presentational attributes such as
fill="skyblue"
to the entireg
group, which then applies to all child elements of the group. This is an efficient way to style or animate multiple SVG elements at once and is one of the main appeals to usingg
to group SVG elements. You can also use<style>
for cleaner stylings as I have done in the secondg
group.
Using Text With SVGs
Using text is fairly straightforward with SVG. The one thing to look out for is the font styles as they are adjusted using CSS and do not have many sizing controls.
In the example below, I've included title
and desc
elements which are used mainly for accessibility concerns (hover over the text, you'll see the title
appear in a browser tooltip).
How to apply gradients to SVGs
With SVG, you have fine-grained control when applying gradients to your elements. Using what we've learned from prior sections, here is an example of a real-world gradient use case.
<linearGradient id="myGradient" gradientTransform="rotate(90)">
<stop offset="0%" stop-color="#4d7c0f" />
<stop offset="50%" stop-color="#d9f99d" />
<stop offset="100%" stop-color="#4d7c0f" />
</linearGradient>
With this gradient, we're using the stop
element to define a gradient that starts at the top/bottom using the border color and linearly fades to a lighter hue variant as we reach the middle of the text.
Kind of reminds you of the early 2000s Microsoft PPT text right? I'm no designer, but you can see the possibilities with this simple example.
How to animate an SVG
The most basic form of SVG animations is with the animate
property. You can generally put this inside another SVG element and use the animateProperty
+ values
to animate a certain property of that SVG. For example, we can animate the radius of a circle:
<svg width="400" height="200" xmlns="http://www.w3.org/2000/svg">
<circle r="50" cx="50" cy="50"/>
<!-- Animating the radius of the circle back and forth between 50 and 60 units indefinitely -->
<animate attributeName="r" values="50;60;50" dur="5s" repeatCount="indefinite" />
</circle>
</svg>
Not all attributes are eligible for animations, so be sure to check the attribute reference on MDN, which usually says something like Animatable: yes | no
.
But that's just a basic animation. You can do so much more that I won't get into. As a quick example of what's possible, here's how you'd create an SVG path and animate another SVG element on that path:
Common SVG Questions
What is the difference between Canvas and SVG Specs?
Take a look at the two rectangles below. Pretty similar looking right?
The first image is created with a <canvas />
element while the second is created with an <svg />
element. Check the dev tools!
Here is the code for each. First up, canvas
(a basic React component):
// Canvas component
import { useEffect, useRef } from 'react';
export function CanvasExample() {
const ref = useRef<HTMLCanvasElement | null>(null);
useEffect(() => {
const ctx = ref.current?.getContext('2d');
ctx!.lineWidth = 2;
ctx?.beginPath();
ctx?.rect(0, 0, 300, 80);
ctx?.stroke();
}, []);
return (
<div className="flex justify-center w-full">
<canvas width="300" height="80" ref={ref} />
</div>
);
}
And here is the svg
:
<!-- SVG -->
<svg viewBox="0 0 300 80" xmlns="http://www.w3.org/2000/svg">
<rect width="300" height="80" style={{ fill: 'transparent', strokeWidth: 2,
stroke: 'rgb(0,0,0)' }} />
</svg>
When do I use SVG and when do I use Canvas?
As you can see from the examples above, the svg
spec is very declarative (i.e. "hey browser, here is what I want, build it for me") while canvas
is very imperative (i.e. "hey browser, here is a list of steps that I want you to take to build this").
In general, you can do way more with the canvas
spec at the cost of accessibility and performance. So to answer this question more briefly than I should, you should generally use SVG as a default and fallback to using the canvas
spec when SVG no longer can support the complexity of what you are doing (e.g. a console game).
Is SVG the same as HTML?
No, SVGs are created with XML, which is similar looking to HTML, but without pre-defined tags such as h1
, a
, or p
tags, and the syntax is case-sensitive (unlike HTML). The user creates arbitrarily named tags and uses this to represent structured data such as SVGs.
In other words, an SVG is an implementation, or dialect of XML that defines arbitrary shapes that have been covered in this post (e.g. rect
, g
, circle
, etc.). width="320" height="150" -->