Create and manage SVG symbols for your projects
Drop SVG files here
or
Tip: drag symbols between file cards once loaded
SVG symbols are the most efficient way to manage icons on a website. Define once, reuse everywhere. The key step is getting the sprite into your page, choose the method that matches your stack.
Including the SVG file
include Recommended for PHPPHP injects the SVG directly into the HTML at render time, zero extra requests, and <use href="#id"> works with simple fragment references.
style="display:none" to the root:<svg xmlns="http://www.w3.org/2000/svg" style="display:none"><symbol id="icon-arrow" viewBox="0 0 24 24"><path d="M5 12h14M12 5l7 7-7 7" stroke="currentColor" stroke-width="2"/></symbol></svg>
<body>:<?php include $_SERVER['DOCUMENT_ROOT'] . '/assets/icons.svg'; ?>
<svg width="24" height="24" aria-hidden="true"><use href="#icon-arrow"/></svg>
fetch inject Static / any hostFetches the SVG file once, injects it into the DOM. Browser caches it on repeat visits. Requires same-origin or a CORS header.
fetch('/assets/icons.svg')
.then(r => r.text())
.then(svg => {
const div = document.createElement('div');
div.style.display = 'none';
div.innerHTML = svg;
document.body.prepend(div);
});
href reference Simple but limitedReference the external SVG directly in <use>. Works in modern browsers but CSS currentColor does not work across the file boundary, icons won't inherit your page colors.
<svg width="24" height="24" aria-hidden="true"><use href="/assets/icons.svg#icon-arrow"/></svg>
mask No sprite neededEach SVG is referenced as an image mask. The element's background-color becomes the icon color, currentColor still works via this trick. One request per icon file.
.icon-arrow {
display: inline-block;
width: 24px; height: 24px;
background-color: currentColor;
mask: url('/assets/icon-arrow.svg')
center / contain no-repeat;
}
Quick reference
| Method | Best for | currentColor | Extra request | CORS needed |
|---|---|---|---|---|
PHP include |
PHP sites | ✓ Yes | ✓ None | ✓ No |
fetch inject |
Static sites, SPAs | ✓ Yes | 1 (cached) | Same origin |
External href |
Simple decorative icons | ✗ No | 1 (cached) | Same origin |
CSS mask |
Single-color, cross-origin | ✓ Yes* | 1 per icon | ✓ No |
* CSS mask uses background-color: currentColor rather than SVG's native currentColor, limiting icons to a single flat color.
Styling & accessibility
Symbols inherit color via currentColor, making them easy to theme and animate.
.icon { width: 1em; height: 1em; }
.icon-primary { color: var(--c-accent); }
.icon-danger { color: var(--c-rose); }
.btn:hover .icon {
color: white;
transform: translateX(2px);
transition: transform .15s ease;
}
@media (prefers-color-scheme: dark) {
.icon { color: #f1f5f9; }
}
Decorative icons must be hidden from screen readers. Meaningful icons need a label.
<svg width="20" height="20"
aria-hidden="true" focusable="false">
<use href="#icon-arrow"/>
</svg>
<button aria-label="Next page">
<svg width="20" height="20" aria-hidden="true">
<use href="#icon-arrow"/>
</svg>
</button>
export function Icon({ id, size=20, className='' }) {
return (
<svg width={size} height={size}
className={`icon ${className}`}
aria-hidden="true">
<use href={`/icons.svg#${id}`} />
</svg>
);
}
<template>
<svg :width="size" :height="size"
class="icon" aria-hidden="true">
<use :href="`/icons.svg#${id}`" />
</svg>
</template>
<script setup>
defineProps({ id: String, size: { default: 20 } })
</script>
// vite.config.js
import svgSprite from 'vite-plugin-svg-sprite'
export default {
plugins: [
svgSprite({
symbolId: 'icon-[name]',
include: 'src/icons/**/*.svg'
})
]
}