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.
[code type=html]
<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>
[/code]
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.
[code type=html]
<svg width="600" height="100">
<defs>
<linearGradient id="gradient">
<stop offset="20%" stop-color="#39F" />
<stop offset="90%" stop-color="#F3F" />
</linearGradient>
</defs>
<rect x="1cm" y="1cm" width="6cm" height="1cm" fill="url(#gradient)" />
<circle rx="50" cx="25" cy="25" fill="url(#gradient)" />
</svg>
[/code]
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.
[code type=html]
<symbol id="fire">
<title>Flames</title>
<desc>The crackling flames of a campfire</desc>
<path d="" fill=""/>
<path d="" fill=""/>
</symbol>
[/code]
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.
[code type=html]
<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>
<use xlink:href="#fire" />
</svg>
[/code]
Here the xlink:href attribute points to a named anchor. You can reference something in another file too.
[code type=html]
[/code]
Unfortunately external references don’t work in IE10 and below.
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.
[code type=html]
[/code]
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>
.
[code type=html]
<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)" fill="#000">
<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>
<use x="0" y="-10" xlink:href="#fire" fill="#c00" stroke="orange" stroke-width="5px" />
<use x="20" y="-5" xlink:href="#fire" fill="#e00" stroke="orange" stroke-width="5px" />
</svg>
[/code]
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.
Download a free sample from my book, Design Fundamentals.
It woild be good to have info about reusing external defs.
Like in one file.
And in another one.
Even if it is not supported, it is very interesting to find workaround.
I thought I mentioned referenced external files in the post. I didn’t use them in the examples, but I do mention you can reference external files.
Damn, they silently removed svg tags (
Huh? I’m not following.
Can I create defs id=”mydefs” in the one file and use xlink:href=”path-to-file.svg#mydefs” in another?
I think I am not.
About silently tags removing – if you will write svg tags in these comments, they will be silently eaten.
As far as I know you should be able to create and link to the defs the way you mentioned. I’ll dig into it. I’m working on another series at the moment, but then I was going to dig more into SVG again.
I was under the impression that what you want to do with the external files works the way you described.
My bad. I should have realized that’s what you meant about the tags being removed. You have to use html entities for the less than and greater signs instead of using the angle brackets themselves. You might also need to use the html entities for things like quotes too.
You can always email me the code if you want me to take a look. It’s probably easier for me to add it into a comment too.
Ok, If you will find a solution, please, answer here:
http://stackoverflow.com/questions/31258878/how-to-use-external-defs-in-svg-to-inject-identificators-into-includers-scope
Hello Steven,
Even on a single HTML 5 page, wouldn’t it be more practical if we could define once and reuse SVG elements, instead of repeating them everytime?
Do you know if it’s possible? E.g., in your example, the element “fire” definition appears 3 times.
Thanks,
Jose Tepedino.
Brasilia – Brazil
Just dipping my tie into the SVG pool. Your article helped me actually manipulate svg elements in a meaningful way. Thanks!
I’m glad I could help.
Note that this doesn’t work unless your tag includes the namespace reference xmlns:xlink=”http://www.w3.org/1999/xlink”
Really informative post and I think your writing style is absolutely fantastic. Very clear and methodical. Thank you so much for sharing your knowledge.