Reuse SVG Elements and Animations Without Duplicating Code
SVG and CSS animations are powerful tools, but repetitive markup can quickly become unwieldy. The <use> element, combined with CSS variables and reusable keyframe animations, lets you define a graphic once and clone it any number of times — with different positions, colors, and even animation behavior per clone.
Cloning with the <use> Element
The <use> element follows a simple pattern: define a shape or group once, give it an ID, then reference that ID wherever you want a copy. This keeps the markup compact and easier to maintain, especially when a complex graphic appears repeatedly.
To implement it:
- Identify the code you want to clone.
- Add an ID to that element (a
<g>is handy for grouping multiple shapes). - Reference it with
<use xlink:href="#id"/>and adjust thexandyattributes for positioning.
Consider building a large cube from a smaller unit cube. Start by defining the unit with basic shapes and transforms grouped inside a <g>:
<svg viewBox="-130 -20 300 100">
<g id="cube">
<rect width="21" height="24" transform="skewY(30)"/>
<rect width="21" height="24" transform="skewY(-30) translate(21 24.3)"/>
<rect width="21" height="21" transform="scale(1.41,.81) rotate(45) translate(0 -21)"/>
</g>
</svg>
To reuse it, place the unit inside the SVG's <defs> section — content there is not rendered until referenced. You can then clone it many times with different x and y values:
<use xlink:href="#cube" x="142" y="124"/>
<use xlink:href="#cube" x="100" y="124"/>
<!-- ... -->
In SVG2, xlink:href is deprecated in favor of href, but support varies; Safari does not handle href reliably at the time of writing. To stay compatible, keep xlink:href and include the namespace xmlns:xlink="http://www.w3.org/1999/xlink" on the root <svg> tag.
Variable-Based Color Palettes
Reusing a shape doesn't mean you are stuck with one color. When you inspect a <use> clone, you'll see it renders in the Shadow DOM, where external CSS cannot override the styles of the original definition. Any explicit fill or stroke values inside <defs> are inherited by every instance — unless you replace them with CSS variables, which can be redefined per context.
Replace hard-coded colors with meaningful variable names. This side definition:
<rect fill="#00affa" stroke="#0079ad" />
becomes:
<rect fill="var(--mainColor)" stroke="var(--strokeColor)" />
You can then duplicate the SVG and attach a different class to each, controlling the palette exactly where needed:
.blue-cube {
--mainColor: #009CDE;
--strokeColor: #0079ad;
--lightColor: #00affa;
--darkColor: #008bc7;
}
.pink-cube {
--mainColor: #de0063;
--strokeColor: #ad004e;
--lightColor: #fa0070;
--darkColor: #c7005a;
}
Adding more cubes later requires no extra markup — just another class and a few CSS custom properties.
Reusable Animations with Keyframes
Animations can be reused just like shapes. For an exploded-view effect on hover, define movement along the x- and y-axes as separate keyframe animations, so they can be assigned independently to any cube unit.
A basic movement could translate a cube 35px along an axis:
@keyframes moveX {
to { transform: translateX(-35px); }
}
But to make the animation flexible across instances, replace the numeric offset with a variable:
@keyframes moveX {
to { transform: translateX(var(--translate, 35px)); }
}
If that variable is never set, the animation falls back to the default value, 35px.
Applying movement in opposite directions requires separate classes, since one must use a negative translation:
.m-left, .m-right {
animation: 2s moveX alternate infinite;
}
Define the negative offset inside the class:
.m-left { --translate: -50px; }
The class declaration above attaches the moveX animation with a two-second duration, translating the element between its original position and -50px on the x-axis, then back — alternating forever.
Running those animations continuously would be distracting for any adjacent content, so pause them by default and run them only on hover:
svg:hover .m-left {
animation: 2s moveX alternate infinite;
}
The issue is that removing the cursor snaps the animation back to its start. To hold the last frame, append paused to the animation shorthand:
.m-left {
animation: 2s moveX alternate infinite paused;
}
Then trigger the animation on hover:
svg:hover * {
animation-play-state: running;
}
You can apply these movement classes to individual cube units or to groups of them, depending on how much of the figure should scatter.
Plan Before You Draw
The key takeaway is to analyze the structure before drawing every shape individually. Replacing raw paths with reusable compositions saves effort in the long run — a little upfront planning can eliminate a large amount of code and keep the markup clean across many repeated graphics.



