You drew a shape with a gradient, put a Gaussian blur on it, and cut a circular mask out of the middle. You clicked Export SVG. You opened the file in a second tool and the gradient is a raster, the blur is a fuzzy image, and the mask has vanished. Meet the flattening problem.
The SVG has a defs block. Your exporter ignored it.
An SVG file describes effects in three places:
-
Attributes on a shape:
fill="#B5FF3A",stroke="black",opacity="0.8". Simple, always survives. -
A
<defs>block:<linearGradient>,<radialGradient>,<filter>,<mask>,<clipPath>,<pattern>,<symbol>. Each described once, ready to reference. -
References from shapes into defs:
fill="url(#lin1)",filter="url(#blur1)",mask="url(#msk1)".
An exporter that walks your shapes, renders each one to pixels, and writes the pixels back out will lose the second and third categories. That is what "flattening" is. The file is still SVG in the sense that it uses the SVG grammar, but the contents are a bitmap in a wrapper.
A serializing exporter walks the defs first, clones every definition with a fresh id, walks the shapes second, and rewrites every url(#...) reference to point at the new id. Nothing detaches.
Same drawing, two exports
A live-effect export looks like this (search for <defs>, <linearGradient>, <filter>, <mask>):
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200">
<defs>
<linearGradient id="lin1" x1="0" y1="0" x2="1" y2="1">
<stop offset="0%" stop-color="#B5FF3A"/>
<stop offset="100%" stop-color="#0B0B10"/>
</linearGradient>
<filter id="blur1" x="-10%" y="-10%" width="120%" height="120%">
<feGaussianBlur stdDeviation="4"/>
</filter>
<mask id="msk1">
<rect x="0" y="0" width="200" height="200" fill="white"/>
<circle cx="100" cy="100" r="40" fill="black"/>
</mask>
</defs>
<rect x="20" y="20" width="160" height="160"
fill="url(#lin1)" filter="url(#blur1)" mask="url(#msk1)"/>
</svg>
A flattened export looks like this:
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200">
<image x="0" y="0" width="200" height="200"
href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA...(hundreds of KB)"/>
</svg>
Nothing in that second file can be edited without going back to the source.
What each editor actually does
| Editor | Gradients | Filters | Masks and clipPaths |
|---|---|---|---|
| SVG Lab (svglab.app) | Live in defs | Live filter primitives | mask, clipPath, pattern, symbol all live |
| Inkscape (Save as Plain SVG) | Live | Live | Live, inkscape-namespaced attributes stripped |
| Figma (Export SVG) | Live for supported primitives | Rasterized where Figma has no SVG equivalent | Converted to clip-path or flattened |
| Illustrator (Export As SVG) | Live | Raster effects flattened to embedded image | Live |
A 20 second preflight before you hand off the file
- Open the exported
.svgin a text editor. Search for<defs>. If it is missing, the exporter flattened. - Search for
<imagewith adata:image/png;base64,href. Any hit is a rasterized effect. - Reopen the file in a second editor. If the gradient shows a stop list and the blur shows a filter, the round trip works.
- Check size. A serialized SVG with two or three effects is usually under 20 KB. A flattened one with embedded pixels is hundreds of KB.
Pick the source of truth on this criterion
I built SVG Lab as a browser SVG editor after losing effects work to a Figma export more than once. It serializes: gradients, filters, masks, clipPaths, patterns and symbols all end up in the exported defs with re-mapped ids, and every reference is rewritten. Every plan uses the same serializer, so effect preservation is not a Pro feature, only export volume is.
If your workflow needs the SVG to survive being reopened, keep the master in a tool that treats SVG as its native format (SVG Lab or Inkscape), and treat Figma or Illustrator SVG exports as handoff artifacts, not source files.
Full version with the FAQ: https://svglab.app/blog/export-svg-gradients-filters-masks
Top comments (0)