SVG Drop Shadow
This lesson shows how to build a realistic drop shadow in SVG by hand with feOffset, feGaussianBlur, and feMerge, then again with the compact feDropShadow shorthand.
Why SVG Needs Filters for Shadows
Unlike CSS, SVG shapes don't have a shadow property built in. Instead, shadows are produced with the <filter> element, a container for one or more filter primitives that reshape how a shape is rendered before it reaches the screen. A filter is defined once inside <defs>, given an id, and then attached to any shape with filter="url(#id)". Because the filter is only referenced by id, the same shadow recipe can be reused on as many shapes as you like without repeating any markup.
A classic shadow is assembled from three primitives working in sequence. feGaussianBlur reads the shape's silhouette (SourceAlpha) and blurs it into a soft, colorless smudge. feOffset then nudges that blurred smudge a few pixels down and to the right so it peeks out from behind the shape. Finally feMerge stacks layers in the order their feMergeNode children appear, painting the offset blur first and the original SourceGraphic on top of it. It also helps to widen the filter's own canvas with x, y, width, and height, since a filter's default region is clipped tightly to the shape's bounding box and can cut off a blur that extends past the original edges.
Example
<!DOCTYPE html>
<html>
<body>
<svg viewBox='0 0 200 140' xmlns='http://www.w3.org/2000/svg'>
<defs>
<filter id='softShadow' x='-50%' y='-50%' width='200%' height='200%'>
<feGaussianBlur in='SourceAlpha' stdDeviation='4' result='blurred'/>
<feOffset in='blurred' dx='6' dy='6' result='shadow'/>
<feMerge>
<feMergeNode in='shadow'/>
<feMergeNode in='SourceGraphic'/>
</feMerge>
</filter>
</defs>
<rect x='30' y='30' width='120' height='70' rx='10' fill='#4f9dde' filter='url(#softShadow)'/>
</svg>
</body>
</html>Example
<!DOCTYPE html>
<html>
<body>
<svg viewBox='0 0 260 140' xmlns='http://www.w3.org/2000/svg'>
<defs>
<filter id='cardShadow' x='-40%' y='-40%' width='180%' height='180%'>
<feGaussianBlur in='SourceAlpha' stdDeviation='3' result='blur'/>
<feOffset in='blur' dx='4' dy='5' result='offsetBlur'/>
<feMerge>
<feMergeNode in='offsetBlur'/>
<feMergeNode in='SourceGraphic'/>
</feMerge>
</filter>
</defs>
<circle cx='60' cy='70' r='40' fill='#e8a33d' filter='url(#cardShadow)'/>
<rect x='140' y='30' width='90' height='80' rx='8' fill='#5bc48c' filter='url(#cardShadow)'/>
</svg>
</body>
</html>Because the filter lives in <defs> with its own id, it is not tied to a single element - any shape can point at the same filter and receive an independently rendered shadow sized to its own geometry, as shown above with the circle and rounded rectangle sharing one cardShadow filter.
The feDropShadow Shortcut
Because offset-blur-merge is such a common pattern, SVG defines a single primitive that performs all three steps at once: feDropShadow. It accepts dx and dy for the offset, stdDeviation for the blur amount, and flood-color plus flood-opacity to tint and fade the shadow, replacing the entire three-primitive chain with one line.
Example
<!DOCTYPE html>
<html>
<body>
<svg viewBox='0 0 200 140' xmlns='http://www.w3.org/2000/svg'>
<defs>
<filter id='quickShadow' x='-50%' y='-50%' width='200%' height='200%'>
<feDropShadow dx='5' dy='5' stdDeviation='3' flood-color='#1a1a2e' flood-opacity='0.5'/>
</filter>
</defs>
<polygon points='100,25 150,110 50,110' fill='#c85c8e' filter='url(#quickShadow)'/>
</svg>
</body>
</html>- Use feDropShadow for a quick, single-primitive shadow in modern browsers.
- Build the manual feOffset/feGaussianBlur/feMerge chain when you need extra steps, such as recoloring the shadow with feFlood and feComposite.
- Always widen the filter region (x/y/width/height) so blur and offset are not clipped.
- Apply the same filter id to multiple elements to keep shadow styling consistent across a whole scene.
Exercise: SVG Drop Shadow
What does the <feDropShadow> filter primitive do?