SVGFlow
Open editor
guides / viewbox-width-height

viewBox, width, height: which one actually wins

These three attributes get conflated constantly, and the resulting bugs — an icon that won't scale, one that scales but crops, one that renders at 300×150 for no obvious reason — all come from the same misunderstanding. They answer different questions.

1. viewBox defines the coordinate system

viewBox="0 0 24 24" says: the drawing occupies a 24×24 region starting at the origin. It says nothing about pixels. Every coordinate inside the file is interpreted against this box, and the box is then mapped onto whatever space the element occupies.

This mapping is what makes SVG resolution-independent. Without a viewBox there is no mapping, so the drawing renders at its literal coordinates and stops scaling.

2. The 300×150 default

An <svg> with no width, no height and no viewBox falls back to the CSS default for replaced elements: 300×150. That is where the mystery aspect ratio in a broken embed comes from — it isn't your drawing, it's the spec's default.

Try this in the editor
Load the sample icon, select a path, and change its fill — the source panel updates as you go.

3. The four combinations

  • viewBox + no width/height — scales to fill its container. Best for inline icons sized in CSS.
  • viewBox + width/height — width and height set the default box, CSS can still override it. Best for files that also get opened standalone.
  • width/height only — fixed size, no scaling. Setting a larger width in CSS stretches the box while the drawing stays put.
  • Neither — 300×150, and the drawing is cropped or lost in it.

4. preserveAspectRatio decides what happens to the leftover space

The viewBox ratio and the element's ratio rarely match exactly, which leaves spare space to distribute. preserveAspectRatio controls that, and its default — xMidYMid meet — is why an icon in a wide container sits centred with gaps on either side rather than stretching.

It takes two parts: an alignment and a fit.

  • meet — scale until the whole viewBox fits inside the element. Nothing is cropped; space is left over. Equivalent to object-fit: contain.
  • slice — scale until the viewBox covers the element. Nothing is left over; the overflow is cropped. Equivalent to object-fit: cover, and what you want for a decorative banner that must fill its box.
  • none — ignore the ratio and stretch to fit exactly. Distorts the drawing, which is occasionally what you want for a sparkline or a gradient wash, and never what you want for an icon.

The alignment half — xMinYMin, xMidYMax and the rest — picks which edge or centre the drawing anchors to when there is space to distribute or overflow to crop. xMidYMid centres on both axes and is almost always right.

<!-- fill the box, crop the overflow, anchor to the top -->
<svg viewBox="0 0 1200 400" preserveAspectRatio="xMidYMin slice">

5. viewBox can pan and zoom, not just scale

The first two numbers are a starting corner, and they are not required to be zero. Changing them moves the window over the drawing without touching a single coordinate inside it.

viewBox="0 0 24 24" <!-- the whole icon -->
viewBox="12 0 12 12" <!-- top-right quadrant, at 2× -->

Shrinking the third and fourth numbers zooms in, because a smaller region is being mapped onto the same rendered box. This is the whole mechanism behind zoom and pan in an SVG editor — including the canvas in this one — and behind the older sprite technique of shipping one file and selecting icons by viewBox.

It also explains a common complaint: an icon that renders off-centre or clipped usually has a viewBox that no longer matches where its content actually sits, typically after shapes were moved or deleted. The fix is not to nudge the paths but to recalculate the box, which is the next section.

6. Reconstructing a missing viewBox

If the file has explicit dimensions, the viewBox is usually just 0 0 width height. If it has neither, you need the real bounding box of the content — which the browser can compute for you:

const b = svg.getBBox();
svg.setAttribute('viewBox', `${b.x} ${b.y} ${b.width} ${b.height}`);

The Resize & viewBox tool does this, including the padding you almost always want around a bounding box that hugs a stroke.

One caveat on getBBox(): it measures geometry, not paint. A shape with a 10-unit stroke extends five units past the box on every side, and filters, shadows and markers are excluded too. Padding the result is not cosmetic — without it, strokes get clipped at the edges.

7. How CSS sizes it once the viewBox is right

A correct viewBox makes the SVG scalable; CSS decides what it scales to. The combination that trips people up is setting one dimension and expecting the other to follow.

.icon { width: 100%; height: auto; } /* ratio preserved from viewBox */
.icon { width: 100%; } /* height falls back to 150px */

An <svg> is a replaced element, so with only a width set the height resolves against the default from section 2 rather than against the viewBox ratio. The fix is height: auto, and it only works when a viewBox is present — which is the whole reason the missing-viewBox case matters.

For containers whose ratio you control directly, the modern option is cleaner:

.banner svg { width: 100%; aspect-ratio: 3 / 1; height: auto; }

Combine that with preserveAspectRatio="xMidYMid slice" from section 4 and you get an SVG that behaves exactly like a well-behaved responsive image: fills the box, keeps its proportions, crops rather than distorts. Set the ratio in CSS and the fit in the SVG, and the two never fight.