How to center a div — 4 ways and when to use each

"How to center a div" is the oldest meme in frontend. The funny part is that today the problem isn't that there's no way — it's that there are five and it's unclear which one is yours.
Plus one update many people missed: you can now center vertically without flexbox at all — right inside a plain block. It landed in every browser in 2024, and half the tutorials out there still don't know.
We'll go in order of increasing difficulty. Take the first method that covers your case.
1. Horizontal only: margin
The simplest case — a block with a known width that should sit in the middle of the line.
.box {
width: 320px;
margin-inline: auto;
}
margin-inline: auto is the modern spelling of the familiar margin: 0 auto. The browser splits the free space evenly left and right.
Two conditions or it won't work: the block needs a width (otherwise it already fills everything) and it must be block-level. For a span, add display: block first.
What you get: the block centered horizontally, vertical untouched.
2. The general-purpose one: flex
When you need both axes and there's a single piece of content:
.parent {
display: flex;
justify-content: center; /* horizontal */
align-items: center; /* vertical */
min-height: 100vh; /* the parent needs a height */
}
The main trap is that last line. align-items: center centers inside the parent's height, and if the parent has no height, it's exactly as tall as its content — so there's nothing to center within. That's why "align-items doesn't work" almost always means "the parent has no height".
Memorize the pairing: justify-content runs along the main axis, align-items across it. With flex-direction: column they swap, which is the second source of confusion.
What you get: content dead center in the parent on both axes.
3. One line: grid
Same thing, shorter:
.parent {
display: grid;
place-items: center;
min-height: 100vh;
}
place-items is shorthand for align-items plus justify-items. If you have several children and want to center them as a group rather than each in its own cell, use place-content: center.
This is my default for "placeholder screen" cases: a 404 page, a loading spinner, a login form in the middle of an empty screen. The difference between the two layout systems is in Flexbox or Grid.
What you get: the same as method 2, two lines shorter.
4. No flex, no grid: align-content in a plain block
The fresh one. The only reason to write display: flex used to be, quite often, "I need to nudge text vertically". Now align-content works in an ordinary block container too:
.box {
height: 120px;
align-content: center; /* vertical */
text-align: center; /* horizontal */
}
No flex — the children stay ordinary blocks and behave as usual (they don't shrink, they wrap like text). Especially nice for buttons and fixed-height cells where flexbox was overkill.
Support: Chrome 123, Firefox 125, Safari 17.4 — spring 2024. It degrades gently in older browsers: the text just sits at the top, nothing breaks.
What you get: content vertically centered, with block layout intact inside.
5. On top of everything: absolute positioning
For when the block has to sit over something else — an overlay, a modal, a caption on an image.
.parent { position: relative; }
.overlay {
position: absolute;
inset: 0;
display: grid;
place-items: center;
}
inset: 0 is shorthand for top: 0; right: 0; bottom: 0; left: 0: the layer stretches across the whole parent and grid centers the content inside it.
The old top: 50%; left: 50%; transform: translate(-50%, -50%) trick is alive and useful when the block's size isn't known in advance. Just remember: transform creates a stacking context — so you won't have to wonder later why a tooltip slid under a neighbouring block.
What you get: the block exactly centered in the parent and above its content.
The common trap: you're centering the wrong thing
Half of all "it won't center" cases are a mix-up between two different jobs.
text-align: center centers the content inside a block: text, images, inline elements. The block itself stays exactly where it was and still spans the full width of its parent.
margin-inline: auto centers the block itself inside its parent, and doesn't touch what's inside it.
Check yourself with one question: do you need to move the box, or its filling? Often you want both lines. The easiest way to see what's happening is DevTools: hover the element and the browser highlights its real bounds, making it obvious whether it spans the full width. On the layers themselves, see the CSS box model.
Picking one in five seconds
- Horizontal only, width known — method 1.
- Both axes, several children in a row or column — method 2.
- Both axes, a single child — method 3.
- Vertical only inside a block of known height — method 4.
- Needs to sit on top — method 5.
Rule: ask "which axis, and does the parent have a height?" first. Half of all centering problems are the second half of that question.
Why doesn't align-items: center work?
Nine times out of ten the parent has no height: it's exactly as tall as its content, so there's nothing to center within. Set min-height (or height) and it works immediately. You can check it in a second in DevTools: look at the parent's computed height.
Why does text in my button sit slightly above center?
Usually the culprit isn't the centering method but uneven padding or a line-height larger than you think. Look at the box model in the Computed panel — you'll see padding-top and padding-bottom differ. More about those layers in the box model.
Can I center without giving the parent a height?
Yes, if the height comes from somewhere else: min-height: 100vh on the section, a height inherited from a parent grid, or inset: 0 with absolute positioning. The point is the same — the browser needs to know what space it's centering inside.
Short story-lessons, an agent simulator and daily practice — in our mobile app. Free.





