Making SVGs scale responsively
How to size SVG icons and logos with CSS so they fill containers, follow text size and keep their shape without stretching or shifting the layout.
5 min read
An SVG can be drawn at any size, but that does not mean it will size itself the way you want. Fixed width and height attributes, a missing viewBox, flexbox shrinking and baseline gaps all get in the way. The fix is a simple division of labor: the file describes the shape and its proportions, and your CSS decides how big it is.
Below are the patterns that cover nearly every real case: a logo that fills its container, an icon that follows the text around it, logos in fixed slots, and keeping all of it from making the page jump.
The rule: proportions in the file, size in CSS
Every icon file should have a viewBox. The viewBox gives the SVG its aspect ratio and lets it scale; without it, resizing just changes the canvas around a fixed drawing. If you are unsure what the numbers mean, understanding the SVG viewBox covers it.
The width and height attributes are different. They are a default size, and CSS overrides them. That makes them useful as a fallback, as long as your CSS is explicit about both dimensions or sets one and lets the other be auto.
A logo that fills its container
For a partner logo in a card, or a hero mark that should shrink on phones:
<div class="card-logo">
<svg viewBox="0 0 120 40" width="120" height="40" role="img" aria-label="Partner name">
...
</svg>
</div>
.card-logo { max-width: 240px; }
.card-logo svg { display: block; width: 100%; height: auto; }
width: 100% fills the container, and height: auto lets the browser compute the height from the viewBox ratio (3:1 here).
The classic bug is setting only the width:
.card-logo svg { width: 100%; } /* height is still the attribute: 40px */
Now the SVG box is as wide as the card but still 40 px tall. The drawing scales to fit inside that box and sits centered with empty space either side, which looks like "the SVG won't grow". Add height: auto.
The same rules work for <img>. For images, max-width: 100%; height: auto; is the usual pattern, so a logo shrinks on small screens but never scales up past its natural size.
An icon that follows the text size
Icons next to text should usually be sized in em, so they grow and shrink with the font:
.icon {
width: 1em;
height: 1em;
flex-shrink: 0;
vertical-align: -0.125em;
}
<p class="balance">
<svg class="icon" viewBox="0 0 24 24" aria-hidden="true">...</svg>
0.25 ETH
</p>
At a 16 px font size the icon is 16 px; in a 32 px heading it is 32 px, with no extra CSS. The vertical-align offset nudges the icon down slightly so it sits visually centered on the text rather than resting on the baseline. Adjust it by eye for your font; values between -0.1em and -0.2em are typical.
If the icon needs to be a little larger than the cap height, use 1.25em or similar. The point is that the size stays relative. Sizing token icons in lists, tables and inputs has concrete pairings of text and icon sizes.
Logos in fixed slots
Lists and tables need consistency more than fluidity. Two cases come up all the time.
Square slots for token icons. Fix both dimensions:
.token-icon { width: 24px; height: 24px; flex-shrink: 0; }
Fixed-height rows of wordmarks, such as a strip of exchange logos with different widths. Fix the height and let the width follow the logo's own ratio:
.exchange-logo { height: 20px; width: auto; }
This works because the viewBox gives the SVG an intrinsic aspect ratio. If you want every logo in an identical box regardless of shape, give the box both dimensions and use object-fit: contain on an <img>, which letterboxes the logo inside without stretching it.
For containers that must keep a shape while their width varies, such as a grid of icon tiles, use aspect-ratio:
.tile { aspect-ratio: 1; display: grid; place-items: center; }
.tile img { width: 50%; height: auto; }
The tile stays square at every column width, and the logo stays at half the tile.
Flexbox and the baseline gap
Two layout quirks cause most "my icon is the wrong size" reports.
Flex items shrink. An icon inside a flex row next to long text will be squeezed narrower when space runs out, because flex items shrink by default. Add flex-shrink: 0 to icons in flex layouts, as in the examples above.
Inline elements sit on the baseline. An <img> or <svg> is inline by default, so it leaves space below it for text descenders. That shows up as a few mysterious pixels under icons in tiles and buttons. Use display: block when the icon is on its own, or vertical-align: middle when it is inline with text.
Preventing layout shift
Layout shift happens when the browser does not know how big something is until it has loaded.
For <img>, always set width and height attributes, even if CSS will change the size:
<img src="/icons/usdc.svg" width="32" height="32" alt="USDC">
Browsers use the two attributes to compute an aspect ratio before the file arrives and reserve the right amount of space. With height: auto in CSS, the ratio still holds at any width. Without the attributes, the image takes up no space until it loads, then pushes the content below it down. This matters most in long token lists with lazy-loaded icons, where images load while the user scrolls.
For inline SVG, keep width and height attributes as a fallback. An inline <svg> with only a viewBox and no size will expand to the full width of its container until your CSS loads. On a slow connection, that is a full-screen arrow for a moment. The attributes cap it at a sensible size; your CSS then takes over.
For fluid containers, reserve the space with aspect-ratio rather than waiting for content to define the height.
A quick reference
| Goal | CSS |
|---|---|
| Fill the container, keep proportions | width: 100%; height: auto; |
| Shrink on small screens, never upscale (img) | max-width: 100%; height: auto; |
| Follow text size | width: 1em; height: 1em; |
| Fixed square slot | width: 24px; height: 24px; flex-shrink: 0; |
| Fixed height, natural width | height: 20px; width: auto; |
| Fit any logo in a fixed box (img) | fixed width and height plus object-fit: contain |
| Square tile at any width | aspect-ratio: 1; |
| Remove the gap under an icon | display: block; |