Skip to main content

Efficient Icon Delivery: Sprites, Inline SVGs & Caching

· 4 min read

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

MethodTransfer SizeRequestsFCP Impact
Inline SVG+8 KB to HTML0None
External sprite12 KB (cached)1Negligible
Dynamic import9 KB JS chunk1+40 ms (JS parse)
Icon font (baseline)94 KB1+180 ms (FOIT)

Caching Strategy

MethodCache-ControlNotes
Inline SVGCached with HTML (s-maxage or CDN TTL)No separate control
External spritepublic, max-age=31536000, immutableUse content hash in filename
Dynamic importSame as your JS bundle policyBundler adds content hashes

CDN Tips

  • Enable Brotli for .svg files—70–80 % compression.
  • Set Vary: Accept-Encoding to serve correct encoding.
  • For Cloudflare Pages, static assets are automatically cached at the edge.

Hybrid Approach

Combine methods for maximum performance:

  1. Inline the 4–6 icons visible above the fold (nav, hero).
  2. External sprite for remaining page icons (footer, sidebar).
  3. 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.