An ability to define code in one location and reuse it in another helps you make your code more modular, maintainable, and readable. SVG provides that ability through elements that define the code and elements that reference it for reuse.
Last week I started a look at how you can structure and organize your SVG code. I introduced you to the generic <g> element along with the <title> and <desc> elements that add additional semantics to your graphics.
Today I want to look at two more grouping elements, <defs> and <symbol>, which allow you to define SVG content for reuse. I'll also talk about the aptly named <use> element, which references and uses the defined content.
The <defs> Element
The <defs> element serves as a container for referenced content. Graphics inside a <defs> element won’t display until referenced elsewhere. They're defined inside <defs>, but need to be called by another element or attribute before they're rendered to the screen.
The <defs> element shares the same content model as the <g> element so anything you can place inside one, you can place inside the other. Typically you’ll wrap <defs> around a <g> element containing graphic elements inside.
To later reference the content you’ll add an id (not a class) to the grouping element that contains the actual elements drawn to the screen and not on the <defs> element itself.
For example I could wrap the group of paths that created the fire in last week’s campfire example with <defs>. Notice I added id=“fire” to the group. The fire will no longer display until it’s referenced.
<svg width="300" height="300" viewBox="0 0 300 300"><title>Campfire</title><desc>A campfire burning in a pit</desc><g style="fill: #777;"><title>Fire Pit</title><desc>The fire pit in which the campfire is burning</desc><path d="M26.851,222.754 L0,222.754 L0,271.758 C0,286.751 14.555,299 32.443,299 L267.52,299 C285.408,299 300,286.751 300,271.758 L300,222.754 L273.112,222.754 L273.112,266.534 C273.112,272.067 267.816,276.484 261.27,276.484 L38.693,276.484 C32.147,276.484 26.851,272.058 26.851,266.534 L26.851,222.754 z" /></g><defs><g id="fire" transform="translate(0,10)"><title>Flames</title><desc>The crackling flames of a campfire</desc><path d="M101.138,160.949 C94.916,154.464 53.538,110.17 95.277,71.802 C130.054,39.868 135.137,13.003 123.434,-0 C123.434,-0 211.959,33.692 159.877,111.877 C150.998,125.163 128.702,140.843 140.369,173.129 L101.138,160.949 z" /><path d="M155.503,171.572 C153.624,165.019 145.142,150.746 171.021,122.303 C184.873,107.172 190.104,84.742 191.308,76.301 C191.308,76.301 237.167,100.662 191.576,160.215 L155.503,171.572 z" /></g></defs><g transform="translate(0,10)" fill="#530"stroke="#310" stroke-width="0"><title>Logs</title><desc>The logs burning in the campfire</desc><path d="M240.344,255.473 L240.344,216.874 L59.378,160.915 L59.378,199.513 z"/><path d="M165.259,180.707 L240.321,157.488 L240.321,196.087 L227.627,199.99 z"/><path d="M134.519,235.715 L59.419,258.9 L59.419,220.301 L72.151,216.433 z"/></g></svg>
Here’s how the entire campfire SVG looks now that the flames are placed inside an unreferenced <defs> element.
The flames no longer display, though I’ll bring them back later in this post.
How about another example, this time from the spec? Imagine you want to apply the same gradient to a variety of objects. First you define the gradient inside <defs> and then reference it as the fill of an object. Here I added the gradient to both a rectangle and a circle.
We haven’t talked about gradients yet so you’ll have to trust the gradient code. What's important in the example is how the id of gradient is referenced as part of the fill url on both the rectangle and the circle.
Here’s how the entire SVG looks. You can see the gradient that was defined once is applied to both the rectangle and the circle.
The <symbol> Element
The <symbol> element is another grouping element similar to the <defs> element. It’s also not rendered directly. More interesting perhaps, is that symbols can have their own viewBox and preserveAspectRatio attributes.
The attributes mean symbols can be scaled when referenced to work better with whatever is referencing them. Because of this they’ll sometimes make for better reusable content than the <g> element.
Symbols are good for defining anything that will be reused and meant to be independent of the viewport. Icons are one practical example.
Here’s how we could rewrite the <defs> and <g> combination we used to contain the flames in the campfire example using the <symbol> element.
1 2 3 4 5 6
<symbol id="fire"><title>Flames</title><desc>The crackling flames of a campfire</desc><path d="" fill=""/><path d="" fill=""/></symbol>
I'll show you how a viewBox works on referenced elements next week when I talk about the marker element. The advantage is that you can create symbols that are easier to scale when they're referenced and rendered. I'll talk about symbols more too later in the year when I talk about creating SVG icons.
Now that we’ve seen a few ways to prepare groups of SVG elements for later use, let’s start reusing them.
The <use> Element
The <use> element references another element or group of elements and displays the graphical content at the point the <use> element appears in the SVG document. Inside <use> you reference the id of the element, group, or symbol you want to display.
For example here’s how we could call the fire group from the campfire SVG that we set inside <defs> earlier. The use element references the group through the xlink:href attribute.
You'll notice the flames in the graphic show as an unrealistic black. While I set fills (in the form of inline CSS) on the logs and the fire pit, I didn't change the fill from the default black on the flames inside the <defs> element.
I could add a fill and stroke to the group, but I'll show you another way. Instead of adding them to the group you can define the styles in the <use> element when you reference the group.
Here I use the SVG attributes to define the color, but you could add the fill and stroke with inline or external CSS as well.
You have to pay attention to specificity, but this ability to define styles on the <use> element means you could instantiate multiple versions of an object each with a different color or stroke.
You can also add any of four optional attributes to the <use> element. The attributes, x, y, width, and height are used to map the referenced content to the current coordinate system. The values x=“0” and y=“0” will be relative to the location of the original referenced element.
Reusing Graphics More Than Once
If you could only reuse <defs> or <symbols> once they wouldn't be all that useful. However, you aren’t limited to a single instantiation with <use>.
In this example, I made a few changes from the previous one. First I set a fill of #000 on the #fire group. Next I added two <use> elements instead of one. On each I set fill, stroke and stroke-width values. The fill will override the fill set on the group. Notice that the fill color on each <use> element is slightly different.
I also located the two referenced graphics in different locations so we could see both. Notice that the second <use> element appears on top of the first one. The different fill color adds to the illusion of depth. Here's the resulting graphic with two sets of flames in different locations and with different fills.
One last thing I want to mention about the <use> element is you can reference groups and elements that aren't defined inside <defs> or <symbols>. You can reference any group or element with an id.
In the previous example I could have created the #fire group outside of <defs> and have one instance of it display by default. Then I could have called the other set of flames with a <use> element.
Closing Thoughts
An ability to define graphics in one place and reference them for use in another is a powerful tool when working with SVG. It gives you a way to write more modular code that you can more easily maintain.
It also makes it easy to build up larger graphics from several component parts. Imagine drawing a daisy. Instead of a defining a new graphic for each petal, you could define a petal once as a symbol or inside <defs> and reuse it multiple times varying x and y values and adding transforms.
Now imagine a field of daisies with hundred or even thousands of petals and know that the definition for all of them is located in one place making all of the petals easy to modify and maintain.
Hopefully you’re thinking of the possibilities. Next week I want to continue and talk about one more element that is used to hold graphic elements for later reuse. I’ll take a look at the marker element, which has a specific use case.