Efficient Icon Delivery: Sprites, Inline SVGs & Caching
You've optimised every SVG to under 1 KB. Great—now how you deliver those icons determines whether your page loads in 1.2 seconds or 2.4. Delivery strategy is the unsexy work that separates fast sites from merely well-designed ones.
What You'll Learn
- Three delivery methods compared with real-world benchmarks
- Caching configurations for each approach
- When to use each method (and when to combine them)
Method 1: Inline SVG
Embed SVG markup directly in your HTML or component.
<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
<path d="M3 12h18M12 3v18"/>
</svg>
Pros:
- Zero additional HTTP requests
- Instant render (painted with the DOM)
- Full CSS and JS access to paths
Cons:
- Increases HTML size (matters for SSR pages with many icons)
- No separate caching—icons re-download with every HTML fetch
- Duplicated bytes if the same icon appears 10 times on a page
Best for: Critical above-the-fold icons (navigation, hero), 1–6 unique icons.
Method 2: External SVG Sprite
Combine icons into a single SVG file with <symbol> elements. Reference via <use>:
<!-- sprite.svg (loaded once, cached) -->
<svg xmlns="http://www.w3.org/2000/svg" style="display:none">
<symbol id="icon-arrow" viewBox="0 0 24 24">
<path d="M5 12h14M13 6l6 6-6 6"/>
</symbol>
<symbol id="icon-close" viewBox="0 0 24 24">
<path d="M6 6l12 12M6 18L18 6"/>
</symbol>
</svg>
<!-- Usage -->
<svg viewBox="0 0 24 24" aria-hidden="true">
<use href="/sprite.svg#icon-arrow"/>
</svg>
Pros:
- One HTTP request for all icons
- Cacheable independently from HTML
- De-duplicated (reference the same symbol 100 times, transfer once)
Cons:
- External file cross-origin restrictions (must be same domain or CORS headers)
- One extra HTTP request on first load
- Slightly more complex build pipeline
Best for: Medium icon sets (10–80 icons), multi-page sites.
Method 3: Dynamic Import (Code-Split)
Import icons as components, letting the bundler tree-shake and code-split:
import { ArrowIcon } from '@icojoy/icons/arrow';
import { CloseIcon } from '@icojoy/icons/close';
Pros:
- Only ships icons actually used on each page
- Tree-shaking eliminates unused exports
- Type-safe (IDE autocomplete, compile-time errors)
Cons:
- Requires JavaScript execution
- Slightly delayed render (after JS parse)
- Bundle split boundaries can create waterfalls
Best for: Large icon sets (80+), SPA/framework apps.
Benchmark: 40 Icons on a Dashboard Page
| Method | Transfer Size | Requests | FCP Impact |
|---|---|---|---|
| Inline SVG | +8 KB to HTML | 0 | None |
| External sprite | 12 KB (cached) | 1 | Negligible |
| Dynamic import | 9 KB JS chunk | 1 | +40 ms (JS parse) |
| Icon font (baseline) | 94 KB | 1 | +180 ms (FOIT) |
Caching Strategy
| Method | Cache-Control | Notes |
|---|---|---|
| Inline SVG | Cached with HTML (s-maxage or CDN TTL) | No separate control |
| External sprite | public, max-age=31536000, immutable | Use content hash in filename |
| Dynamic import | Same as your JS bundle policy | Bundler adds content hashes |
CDN Tips
- Enable Brotli for
.svgfiles—70–80 % compression. - Set
Vary: Accept-Encodingto serve correct encoding. - For Cloudflare Pages, static assets are automatically cached at the edge.
Hybrid Approach
Combine methods for maximum performance:
- Inline the 4–6 icons visible above the fold (nav, hero).
- External sprite for remaining page icons (footer, sidebar).
- Dynamic import for icons behind interactions (modals, dropdowns).
This gives you instant first paint, efficient caching, and minimal JS overhead.
Browse efficient, pre-optimised icon sets on the Icojoy icons page and download complete packs from the Icojoy packs page. Use the Icojoy tools to generate sprites from individual SVGs.
FAQ
Can I mix inline SVG and sprite references on the same page? Absolutely. Inline critical icons, reference the rest via sprite. There's no conflict.
Does <use href> work cross-domain?
No, unless the sprite is served with appropriate CORS headers. Keep sprites on the same domain.
How big can an SVG sprite get before it's a problem? Under 100 KB (uncompressed) is typical for 200 icons. After Brotli, that's ~20 KB—well within acceptable limits.
Should I preload the sprite file?
Only if icons are immediately visible above the fold. Use <link rel="preload" as="image" type="image/svg+xml" href="/sprite.svg">.
What caching headers should I set for SVG files?
For hashed filenames: Cache-Control: public, max-age=31536000, immutable. For unhashed: Cache-Control: public, max-age=86400. See Icojoy's licensing page for asset distribution terms.