How to Optimize SVG Files in Bulk

·6 min read

When you have a folder of icons, a downloaded icon library, or an SVG asset pipeline, you need to optimize files in bulk — not one at a time. Here are the most effective approaches.

SVGO CLI batch mode

SVGO supports folder input natively. Point it at a directory and it processes every.svg file it finds:

# Optimize all SVGs in ./icons, output to ./icons-min
npx svgo -f ./icons -o ./icons-min --multipass

# Optimize in place (overwrites originals!)
npx svgo -f ./icons --multipass

# Recursive: include subdirectories
npx svgo -r -f ./icons --multipass

SVGO processes files sequentially by default. For large libraries (500+ icons), this can be slow. Use a parallel Node.js script for better throughput.

Parallel Node.js script

Process multiple files concurrently with a concurrency limit to avoid memory pressure:

// scripts/optimize-all-svgs.js
import { optimize } from 'svgo';
import fs from 'node:fs';
import path from 'node:path';

function collectSvgs(dir, results = []) {
  for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
    const full = path.join(dir, entry.name);
    if (entry.isDirectory()) collectSvgs(full, results);
    else if (entry.name.endsWith('.svg')) results.push(full);
  }
  return results;
}

async function optimizeFile(filePath) {
  const input = fs.readFileSync(filePath, 'utf-8');
  const result = optimize(input, { multipass: true });
  fs.writeFileSync(filePath, result.data);
  return input.length - result.data.length;
}

async function run() {
  const files = collectSvgs('./icons');
  const CONCURRENCY = 10;
  let saved = 0;
  
  for (let i = 0; i < files.length; i += CONCURRENCY) {
    const batch = files.slice(i, i + CONCURRENCY);
    const savings = await Promise.all(batch.map(optimizeFile));
    saved += savings.reduce((a, b) => a + b, 0);
  }

  console.log(`Optimized ${files.length} files, saved ${(saved / 1024).toFixed(1)} KB`);
}

run();

svg.dog batch API endpoint

The svg.dog API has a /v1/optimize/batch endpoint that accepts multiple files in one request and returns them as a ZIP archive:

# CLI: batch upload with curl (up to 50 files)
curl -X POST https://api.svg.dog/v1/optimize/batch \
  -H "Authorization: Bearer YOUR_KEY" \
  -F "files=@icon1.svg" \
  -F "files=@icon2.svg" \
  -F "files=@icon3.svg" \
  -o optimized.zip

Node.js with the SDK:

import { optimizeBatch } from '@svg.dog/svgdog';
import fs from 'node:fs';
import path from 'node:path';

const iconDir = './icons';
const files = fs.readdirSync(iconDir)
  .filter(f => f.endsWith('.svg'))
  .map(f => fs.readFileSync(path.join(iconDir, f)));

const results = await optimizeBatch(files, {
  apiKey: process.env.SVGDOG_API_KEY,
});

results.forEach((result, i) => {
  fs.writeFileSync(path.join(iconDir, `${i}.svg`), result.svg);
  console.log(`${i}.svg: ${result.savingsPercent.toFixed(1)}% saved`);
});

Optimizing an icon library (e.g., Heroicons, Lucide)

If you downloaded an icon library and want to pre-optimize it before committing to your repo, here’s a one-liner that processes the entire node_modules install:

npx svgo -r -f ./node_modules/lucide-static/icons --multipass

A better pattern: add an npm postinstall script that runs SVGO on the icon package after installation. This ensures the optimized version is always what your build picks up.

Shell script for simple pipelines

For projects without Node.js, a bash loop works fine for small icon sets:

#!/bin/bash
# optimize-icons.sh
for file in ./icons/*.svg; do
  curl -s -X POST https://api.svg.dog/v1/optimize \
    -H "Authorization: Bearer $SVGDOG_API_KEY" \
    -F "file=@$file" \
    -o "$file.tmp" && mv "$file.tmp" "$file"
  echo "Optimized: $file"
done

Tracking savings over time

For icon libraries that evolve, log the total size before and after each optimization run:

# Before
du -sh ./icons/

# After optimization
du -sh ./icons/

Wiring this into a CI comment on pull requests gives your team a clear picture of asset health over time.

Integration with CI

Batch optimization works well as a scheduled CI job (e.g., nightly) rather than per-commit, especially for large icon libraries. See CI/CD automation for full workflow examples.

Optimize SVGs instantly — no setup needed

500 free optimizations per month. No credit card required.

Get a free API key →