Centering Techniques
"How do I center a div?" is the most-asked CSS question of all time, mostly because there is no single right answer — the correct technique depends on whether you are centering one axis or two, whether the content has a known size, and what else is in the container. This page walks through every reliable method, in the order you should actually reach for them.
1. Flexbox — both axes, the modern default
For centering a single child in both directions, flexbox is the shortest, most robust option. It works regardless of whether the child's size is known ahead of time.
.parent {
display: flex;
justify-content: center; /* horizontal */
align-items: center; /* vertical */
min-height: 100vh; /* something to center within */
}justify-content: center alone centers horizontally in a row, and align-items: center alone centers vertically.2. Grid — the shortest syntax
CSS Grid has a dedicated shorthand for exactly this problem: place-items. One property, two axes.
.parent {
display: grid;
place-items: center; /* shorthand for align-items + justify-items */
min-height: 100vh;
}
/* Centering just one grid item among several: */
.child {
place-self: center;
}place-items: center is the least code. If it is not, and centering is the only reason you would add a container property, flexbox and grid are functionally equivalent here — pick whichever your codebase already leans on.3. Margin auto — for a block with a known width
The oldest trick: a block-level element with an explicit width and margin: 0 auto centers itself horizontally within its parent. This only centers on the inline axis, and only works because the element has a defined width — auto margins need free space to distribute.
.centered-block {
width: 600px;
max-width: 100%;
margin: 0 auto; /* top/bottom: 0, left/right: auto */
}margin: 0 auto does nothing on an element without an explicit or constrained width — a default block element already stretches to fill its parent, so there is no free space left for the margins to consume.4. Absolute positioning + transform
Useful when the child must be taken out of normal flow entirely — for example, centering a modal over the whole viewport regardless of surrounding content.
.parent {
position: relative; /* establishes the positioning context */
}
.centered-child {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}top: 50%; left: 50% positions the element's top-left corner at the center point, which is not the same as centering the element itself. translate(-50%, -50%) then shifts the element back by half of its own width and height, landing its center exactly on that point — and it works without knowing the element's size in advance.5. Text-align + line-height — inline content only
For a single line of text (or an inline/inline-block element like an icon) inside a fixed-height container, you rarely need flexbox at all.
.badge {
height: 40px;
line-height: 40px; /* matches the height -> vertical centering */
text-align: center; /* horizontal centering */
white-space: nowrap;
}line-height to the container height only centers a single line. As soon as the text wraps to two lines, the second line pushes past the box and the vertical centering breaks. Use flexbox or grid for anything that might wrap.Quick decision table
Situation | Best technique |
|---|---|
Center anything, both axes, size unknown | flexbox ( |
Center a block with a known/max width, horizontal only |
|
Center an overlay/modal over its container regardless of flow |
|
Center a single line of text or an icon in a fixed-height box |
|
Center a grid item among many, without affecting siblings |
|
Common pitfalls
Forgetting the parent needs a defined height for vertical centering to have any space to work with (
min-height: 100vh, or a fixed height, oralign-items: stretchfrom an ancestor)Using
margin: 0 autoon an element without a set width — nothing happensCentering text with
line-heightand then adding more content that wraps to two linesForgetting
position: relativeon the parent when using absolute-positioned centering — the child centers relative to the nearest positioned ancestor, which may be the viewport insteadReaching for absolute positioning to center normal in-flow content — it removes the element from flow and can overlap siblings unexpectedly
/* The safe, all-purpose default for a new component */
.center-anything {
display: flex;
justify-content: center;
align-items: center;
}
/* If you need it to also handle overflow gracefully (long content) */
.center-anything-safe {
display: flex;
justify-content: center;
align-items: safe center; /* falls back to start alignment on overflow */
}center alignment can push overflowing content off-screen with no way to scroll to it. Adding the safe keyword tells the browser to fall back to start-alignment when the content would overflow, keeping it reachable.Grid with two children — auto-margin variant
Grid can center using the same margin-auto trick as flexbox, useful when you already have a grid container for other layout reasons and do not want place-items to also center unrelated siblings.
.grid-parent {
display: grid;
}
.centered-item {
margin: auto; /* centers this one item both ways within its grid cell */
}The ancient technique: table-cell centering
Before flexbox existed, vertical centering commonly relied on display: table-cell, which has always supported vertical-align properly (unlike normal block elements). It still works today and occasionally shows up in older codebases.
.table-cell-parent {
display: table;
width: 100%;
height: 300px;
}
.table-cell-child {
display: table-cell;
vertical-align: middle;
text-align: center;
}Flexbox vs Grid centering — the real difference
Scenario | Better fit |
|---|---|
Centering exactly one child, nothing else in the container | Either works identically — pick whichever the codebase already uses |
Centering one item while other siblings need their own independent positioning/sizing | Grid, using |
A row of several items that should distribute and stay centered as a group | Flexbox, since |
Centering and RTL layouts
Every technique on this page is direction-agnostic except the absolute positioning one, which is worth double-checking in a right-to-left context.
.centered-child {
position: absolute;
inset-inline-start: 50%; /* logical equivalent of left/right */
top: 50%;
transform: translate(-50%, -50%);
}left: 50% always means the physical left edge, regardless of text direction. inset-inline-start automatically flips to the correct physical side in an RTL context, which matters if the component needs to support both directions.