SVG conversion sounds simple until you're doing it at scale. After running 80 million+ conversions in production, we learned everything that can go wrong — and built LaraSVG to make it right.
This is a deep dive into how LaraSVG works, why we made the architectural decisions we did, and how you can use it to handle SVG conversion in your Laravel application.
The Problem with SVG Conversion in PHP
SVG is the web's native vector format, but getting it into PNG, PDF, or EPS requires an external renderer. PHP can't do it natively. Your options are:
- Imagick — Relies on Ghostscript, poor SVG spec coverage, inconsistent output
- Inkscape CLI — Excellent quality, but slow startup time (~1 second per conversion) and requires a desktop environment
- rsvg-convert — Fast, lightweight, but limited to Linux, inconsistent CSS support
- headless Chromium — Nuclear option, works well but heavy resource footprint
- External APIs — Adds latency, costs money, creates a dependency on third parties
Most teams pick one and commit to it forever. Then they hit its limitations and have no easy way out.
The Multi-Provider Architecture
LaraSVG's core insight: decouple your application code from the converter. Your business logic should never care whether Resvg or Inkscape is doing the actual work.
// This code works with ANY provider
$result = SvgConverter::open('logo.svg')
->setFormat('png')
->setDimensions(1024, 1024)
->toFile('output/logo.png');Under the hood, LaraSVG routes this to whichever provider is configured. Change your provider in config/larasvg.php, and nothing else changes.
Supported Providers
| Provider | Best For | Speed | Quality |
|---|---|---|---|
| Resvg | Most use cases | Fast | Excellent |
| Inkscape | PDF, complex SVGs | Slower | Best |
| rsvg-convert | High-volume Linux workloads | Very fast | Good |
| CairoSVG | Python environments | Fast | Good |
Installing LaraSVG
composer require laratusk/larasvg
php artisan larasvg:setupThe setup command auto-detects which converters are available on your system and writes a sensible default configuration. No manual setup required.
Publish the config to customize:
php artisan vendor:publish --tag=larasvg-configCommon Conversion Patterns
Basic PNG Export
use Laratusk\Larasvg\Facades\SvgConverter;
SvgConverter::open('path/to/source.svg')
->setFormat('png')
->setDimensions(800, 600)
->toFile('output/result.png');Streaming to an HTTP Response
This is the pattern we use most in production — no temp files, no cleanup:
public function downloadLogo(Request $request): Response
{
return SvgConverter::open(storage_path('logos/brand.svg'))
->setFormat('png')
->setDimensions(2000, 2000)
->toStdout();
}The output streams directly to the HTTP response without writing to disk. At scale, this matters — disk I/O is often the bottleneck, not the conversion itself.
Storing to Any Laravel Disk
LaraSVG integrates with Laravel's filesystem abstraction, so you can write directly to S3, GCS, or any configured disk:
SvgConverter::open('template.svg')
->setFormat('png')
->setDimensions(1200, 630)
->toDisk('s3', 'og-images/post-123.png');Choosing a Provider Per Call
Sometimes you need different providers for different tasks. Inkscape for PDF, Resvg for PNG:
// High-quality PDF with Inkscape
SvgConverter::using('inkscape')
->open('diagram.svg')
->setFormat('pdf')
->toFile('exports/diagram.pdf');
// Fast PNG with Resvg
SvgConverter::using('resvg')
->open('icon.svg')
->setFormat('png')
->setDimensions(64, 64)
->toFile('icons/icon.png');Handling SVG from User Input
One of our most common use cases is converting SVGs that users upload. A few things to keep in mind:
// Validate before converting
$request->validate([
'file' => 'required|mimes:svg|max:2048',
]);
$path = $request->file('file')->store('uploads/svg', 'local');
// LaraSVG reads from storage paths
$result = SvgConverter::open(Storage::disk('local')->path($path))
->setFormat('png')
->setDimensions(1024, 1024)
->toFile(storage_path('app/exports/' . Str::uuid() . '.png'));Always sanitize user-uploaded SVGs before processing. SVG files can contain embedded JavaScript. Consider using a sanitization library before passing files to LaraSVG.
Queueing Conversions
For high-volume or slow conversions (large files, PDF output, Inkscape), push to a queue:
class ConvertSvgJob implements ShouldQueue
{
public function __construct(
private string $sourcePath,
private string $outputPath,
private string $format = 'png',
) {}
public function handle(): void
{
SvgConverter::open($this->sourcePath)
->setFormat($this->format)
->setDimensions(2000, 2000)
->toFile($this->outputPath);
}
}
// Dispatch
ConvertSvgJob::dispatch(
sourcePath: storage_path('app/uploads/logo.svg'),
outputPath: storage_path('app/exports/logo.png'),
)->onQueue('conversions');Performance at Scale
At 80 million+ conversions, performance details matter. A few things we've learned:
Resvg is your default. It starts in ~50ms vs Inkscape's ~800ms. For PNG output, Resvg quality is indistinguishable from Inkscape for 95% of SVGs.
Batch when you can. If you're generating the same SVG in multiple sizes (thumbnails, OG images, print resolution), do it in a single job rather than dispatching three separate ones.
Avoid disk writes on the hot path. Use toStdout() for HTTP responses. Disk writes add latency and wear.
Scale horizontally, not vertically. SVG conversion is embarrassingly parallel. Add more queue workers rather than a bigger server.
What's Next
We're actively developing:
- WebP output — Already in progress, coming in the next minor release
- Batch processing API — Convert multiple sizes in a single call
- Async/queue-friendly helpers — First-class integration with Laravel's queue system
- Image optimization — Post-processing hooks for PNG/JPEG compression
Star the repo on GitHub and follow along. Issues, PRs, and feedback are always welcome.