How to centre an SVG in a div, a button or the page
Centre an SVG with flexbox or grid on its parent. Why margin auto fails on an inline svg, how to align it with text, and why a drawing sits off-centre.
Published
To centre an SVG inside a div, make the div a flex container and centre its contents both ways:
<style>
.box {
display: flex;
justify-content: center;
align-items: center;
width: 400px;
height: 300px;
}
</style>
<div class="box">
<svg viewBox="0 0 100 100" width="100" height="100">
<circle cx="50" cy="50" r="50" fill="teal"/>
</svg>
</div>
The circle sits in the middle of the box, 150 pixels from the left and 100 from the top. The same CSS centres an <img> that shows an SVG file: the SVG is only a box as far as page layout goes.
The methods
| Aim | CSS | Goes on |
|---|---|---|
| Centre both ways | display: flex; justify-content: center; align-items: center | The parent |
| Centre both ways, shorter | display: grid; place-items: center | The parent |
| Centre across only | display: block; margin: 0 auto | The SVG |
| Centre across only | text-align: center | The parent |
| Centre over other content | position: absolute; inset: 0; margin: auto | The SVG, with position: relative on the parent |
| Line up with text | vertical-align: middle, or flex on the parent | The SVG |
| Top, right, bottom or left | justify-content and align-items with flex-start or flex-end | The parent |
Each row was checked in Chrome with a 100 pixel SVG in a 400 by 300 box.
Why margin auto does nothing
An inline <svg> has display: inline by default, like an <img>. It sits in a line of text, and margin: 0 auto has no effect on anything inline. Two things follow from that.
To centre it across with a margin, make it a block first:
svg {
display: block;
margin: 0 auto;
}
This needs the SVG to have a width, from its width attribute or from CSS. Or leave it inline and centre the line it sits in, with text-align: center on the parent. Neither centres it up and down.
The other consequence is a gap. An inline SVG rests on the text baseline, and the line keeps room below the baseline for the tails of letters such as g and y. A div holding nothing but a 100 pixel SVG came out 107 pixels tall in Chrome, at a 16 pixel font size with a line height of 1.5. display: block on the SVG removes the gap, and so does vertical-align: middle or vertical-align: top.
Centre it in the page
To put an SVG in the middle of the screen, the body has to be as tall as the window. By default it is only as tall as its contents:
<style>
body {
margin: 0;
min-height: 100vh;
display: grid;
place-items: center;
}
</style>
<svg viewBox="0 0 100 100" width="100" height="100">
<circle cx="50" cy="50" r="50" fill="teal"/>
</svg>
In an 800 by 600 window this placed the SVG at 350 across and 250 down: dead centre.
Centre it in a button
A button centres its contents across but lines an inline SVG up with the text baseline, which leaves it slightly high. In a 48 pixel button a 24 pixel icon sat 1.5 pixels above centre in Chrome. Flex fixes it:
<button type="button" aria-label="Stop"
style="width: 48px; height: 48px; padding: 0; display: inline-flex; align-items: center; justify-content: center">
<svg viewBox="0 0 24 24" width="24" height="24" aria-hidden="true">
<rect x="5" y="5" width="14" height="14" fill="currentColor"/>
</svg>
</button>
display: block; margin: auto on the SVG gave the same result. The aria-label names the button for screen readers: see accessible SVG.
Line an icon up with text
vertical-align moves an inline SVG up and down within its line.
| Value | Where the SVG goes |
|---|---|
baseline (the default) | Its bottom edge rests on the baseline of the text |
middle | Its middle is placed half the height of a lower-case x above the baseline |
top, bottom | Its top or bottom meets the top or bottom of the line |
text-top, text-bottom | Its top or bottom meets the top or bottom of the letters |
A length, such as -0.125em | Its bottom edge is moved that far above the baseline, or below it for a negative length |
middle is close to centred on the text but not exact, because it is worked out from the lower-case letters. With a 24 pixel icon beside 20 pixel Arial, the icon’s centre came out 1.8 pixels below the centre of the text. For an exact result, make the parent a flex container:
<p style="display: flex; align-items: center; gap: 8px">
<svg viewBox="0 0 24 24" width="24" height="24" aria-hidden="true">
<circle cx="12" cy="12" r="10" fill="teal"/>
</svg>
<span>Saved</span>
</p>
Here the two centres matched to the pixel.
Align to the top, the right or a corner
For svg align top inside a taller box, the simplest answer is flex with align-items: flex-start. For the right-hand side, justify-content: flex-end. An SVG that is inline in a line of text goes to the top of the line with vertical-align: top.
To pin an SVG to a corner over other content, position it:
<div style="position: relative; width: 300px; height: 200px">
<svg viewBox="0 0 10 10" width="50" height="50" style="position: absolute; top: 0; right: 0">
<rect width="10" height="10" fill="teal"/>
</svg>
</div>
The square sits in the top-right corner. position: absolute works on an <svg> exactly as on a div, and it is measured from the nearest ancestor that is itself positioned, which is usually one given position: relative.
To centre an absolutely positioned SVG, use inset: 0; margin: auto when it has a width and height. The older top: 50%; left: 50%; transform: translate(-50%, -50%) gives the same place.
An SVG with no width or height
An inline SVG that has a viewBox and no size grows to the full width of its parent, and takes its height from its shape. In Chrome, in the 400 by 300 box, a square SVG became 400 by 400: taller than the box. Centring then pushes it out at the top and bottom, and it looks anything but centred.
Give it a size. Either a width and height, or a rule that makes it fit the box:
.box svg {
max-width: 100%;
max-height: 100%;
}
With that rule the square SVG became 300 by 300 and a two-to-one SVG became 400 by 200, each centred in the box. Responsive SVG covers sizing in full.
The SVG is centred but the drawing is not
There are two boxes: the <svg> element on the page, and the drawing inside it. CSS centres the first. If the picture still looks off to one side, the cause is inside the file.
The element is not the same shape as the viewBox. When the element is wider or taller than the viewBox, the drawing is scaled to fit and centred in the spare room. That is the default, preserveAspectRatio="xMidYMid". A file that sets another value moves it: xMinYMid pushes the drawing to the left edge, and xMidYMin to the top. In a 400 by 200 element with a square viewBox, the drawing began 100 pixels in by default and at 0 with xMinYMid. Remove the attribute to centre the drawing, or set it on purpose to align the drawing to an edge. preserveAspectRatio lists every value.
The shapes are not in the middle of the viewBox. A file may have empty space on one side. Find the real extent of the shapes with getBBox() and set the viewBox to it:
const svg = document.querySelector('svg');
const box = svg.getBBox();
svg.setAttribute('viewBox', `${box.x} ${box.y} ${box.width} ${box.height}`);
For a circle of radius 20 at 30,30 this returned 10, 10, 40, 40, and the circle then filled the element. getBBox() leaves strokes out, so add half the stroke width on every side if the drawing has outlines. The crop SVG tool does the same job on a file without code, and SVG viewBox explained covers the four numbers.
Positioning inside an SVG
CSS positioning stops at the <svg> element. Shapes inside it are not boxes: position, top, left, margin and text-align do nothing to a <rect> or a <path>. Tested in Chrome, a rect with position: absolute; left: 50px stayed where its x and y put it.
Inside the drawing, place things with coordinates: x and y, cx and cy, or a transform. To centre a shape in the drawing, work from the viewBox: the middle of viewBox="0 0 200 100" is 100,50. SVG transform covers moving shapes, and SVG text covers centring text with text-anchor.
Common problems
| What you see | Cause | Fix |
|---|---|---|
margin: 0 auto does nothing | The SVG is inline | Add display: block, or use flex on the parent |
| Centred across but not up and down | margin: auto and text-align only work across | Flex or grid on the parent, which needs a height |
align-items: center has no effect | The parent is only as tall as the SVG | Give the parent a height or min-height |
| The SVG overflows its box | No width or height, so it takes the full width | Set a size or max-height: 100% |
| A few pixels of space under the SVG | Room for the tails of letters | display: block |
| The icon sits a little high or low beside text | Baseline alignment | Flex with align-items: center |
| The element is centred but the picture is not | Empty space in the viewBox, or a preserveAspectRatio value | Crop the viewBox; remove the attribute |
vertical-align does nothing | The SVG is a block or a flex item | It only applies to inline elements: use align-items |
Questions
Does this work for an SVG in an img tag? Yes. Every method here treats the element as a box and does not care what is in it. Only the getBBox() fix needs the SVG to be inline.
How do I centre a background SVG? With background-position: center and background-repeat: no-repeat. See SVG as a background image.
Which method should I use? Flex or grid on the parent. Both centre in two directions with no sums, and neither needs to know the size of the SVG.