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

AimCSSGoes on
Centre both waysdisplay: flex; justify-content: center; align-items: centerThe parent
Centre both ways, shorterdisplay: grid; place-items: centerThe parent
Centre across onlydisplay: block; margin: 0 autoThe SVG
Centre across onlytext-align: centerThe parent
Centre over other contentposition: absolute; inset: 0; margin: autoThe SVG, with position: relative on the parent
Line up with textvertical-align: middle, or flex on the parentThe SVG
Top, right, bottom or leftjustify-content and align-items with flex-start or flex-endThe 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.

ValueWhere the SVG goes
baseline (the default)Its bottom edge rests on the baseline of the text
middleIts middle is placed half the height of a lower-case x above the baseline
top, bottomIts top or bottom meets the top or bottom of the line
text-top, text-bottomIts top or bottom meets the top or bottom of the letters
A length, such as -0.125emIts 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 seeCauseFix
margin: 0 auto does nothingThe SVG is inlineAdd display: block, or use flex on the parent
Centred across but not up and downmargin: auto and text-align only work acrossFlex or grid on the parent, which needs a height
align-items: center has no effectThe parent is only as tall as the SVGGive the parent a height or min-height
The SVG overflows its boxNo width or height, so it takes the full widthSet a size or max-height: 100%
A few pixels of space under the SVGRoom for the tails of lettersdisplay: block
The icon sits a little high or low beside textBaseline alignmentFlex with align-items: center
The element is centred but the picture is notEmpty space in the viewBox, or a preserveAspectRatio valueCrop the viewBox; remove the attribute
vertical-align does nothingThe SVG is a block or a flex itemIt 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.