How do you vertically center an element using Flexbox, and what are two other ways to achieve the same result?
Advertisement
🧩 Scenario
Architecture Walkthrough
The Flexbox Way
Two declarations on the parent centre a child on both axes:
.parent {
display: flex;
justify-content: center; /* main axis */
align-items: center; /* cross axis */
}
In the default flex-direction: row, justify-content handles horizontal and align-items handles vertical. Flip to column and the two swap, which is the standard trap: a layout that centres correctly as a row centres on the wrong axis after a media query changes the direction.
The crucial precondition is that the container must have a height greater than its content for vertical centring to be visible. align-items: center centres within the line's cross-axis extent, and if the container height is derived purely from the content, that extent is exactly the content height and centring has nothing to work with. The height can come from height, min-height, 100dvh, a grid track, or a stretched flex parent, but it must come from somewhere.
The Grid Way
Grid does both axes in one declaration:
.parent {
display: grid;
place-items: center; /* shorthand for align-items + justify-items */
}
place-items is the shorthand for align-items and justify-items, and center sets both. This is the shortest correct answer in modern CSS and is generally the better default for pure centring, because there is no axis to keep track of and no direction-swap trap. For a single child, place-content: center behaves equivalently.
Grid also handles multiple children more predictably when you want them centred as a group rather than individually distributed, since the implicit rows stack and the whole block can be centred with place-content.
The Absolute Positioning Way
.parent {
position: relative;
}
.child {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
top: 50% and left: 50% position the child's top-left corner at the container's centre, which leaves it visibly off-centre by half its own size. The translate(-50%, -50%) pulls it back by half its own width and height, and because percentage translate values resolve against the element's own dimensions, this works without knowing the child's size in advance.
What distinguishes this technique is that the child is out of flow. It does not contribute to the parent's height, does not push siblings, and cannot be pushed. That makes it wrong for ordinary content and exactly right for overlays: a loading spinner over a panel, a play button over a video thumbnail, a centred badge, or a modal within a backdrop. Any case where the centred element must be visually centred without affecting layout is this technique's territory.
A modern variant uses inset: 0 plus margin: auto on an element with a definite size, and newer engines support align-content and justify-content on absolutely positioned elements, but the translate approach remains the portable one.
Other Approaches and When They Are Wrong
line-height equal to the container height centres a single line of text and nothing else. It is still common in button and badge code, but it breaks the moment the text wraps to a second line, since every line gets the inflated line height. Reserve it for cases where wrapping is impossible.
margin: auto on a flex item centres it on both axes, and unlike justify-content, an auto margin on a single item lets you centre one child while distributing others. table-cell with vertical-align: middle still works but has no advantages over the modern options.
Choosing
The decision comes down to three questions. Should the child stay in flow? If no, use absolute plus transform. Do you need flex distribution behaviour among several children, such as space-between or flex-grow? If yes, use Flexbox. Otherwise, display: grid; place-items: center is the shortest thing that works and the least prone to axis confusion.
Key Code Explained
/* 1. FLEXBOX — two declarations, axis-dependent */
.flex-center {
display: flex;
justify-content: center; /* horizontal in a row */
align-items: center; /* vertical in a row */
min-height: 100dvh; /* REQUIRED: without height there is nothing to centre in */
}
/* The direction trap: these swap meaning in a column */
.flex-column-center {
display: flex;
flex-direction: column;
justify-content: center; /* now VERTICAL */
align-items: center; /* now HORIZONTAL */
min-height: 100dvh;
}
/* 2. GRID — one declaration, no axis to track */
.grid-center {
display: grid;
place-items: center; /* align-items + justify-items */
min-height: 100dvh;
}
/* 3. ABSOLUTE + TRANSFORM — centres WITHOUT affecting layout */
.overlay-host {
position: relative;
}
.spinner {
position: absolute;
top: 50%; /* puts the CORNER at the centre */
left: 50%;
transform: translate(-50%, -50%); /* pulls back by half its OWN size */
}
/* The percentages in translate resolve against the element itself,
so this works without knowing the spinner's dimensions. */
/* Auto margins: centre ONE flex item while others distribute */
.toolbar {
display: flex;
}
.toolbar .title {
margin: auto; /* centred on both axes within the free space */
}
/* line-height: single line ONLY — breaks on wrap */
.badge {
height: 24px;
line-height: 24px; /* a second line inherits 24px and overflows */
}
The .spinner block is the one to be able to explain rather than just recite. top: 50% positions the corner, not the centre, so the element sits half its own size too low and too far right. The negative translate corrects it, and the reason it works for unknown sizes is that transform percentages are relative to the transformed element itself rather than to its parent, unlike almost every other percentage in CSS.
The min-height: 100dvh in the first three blocks is the detail most "it does not centre" bug reports come down to. All three techniques centre within available space, and if the container is content-sized there is no available space to centre within.
Tradeoffs
| Technique | Declarations | Child in flow | Both axes | Best for |
|---|---|---|---|---|
display: grid + place-items: center | 2 | Yes | Yes, one property | Default choice for pure centring |
Flexbox justify-content + align-items | 3 | Yes | Yes, two properties | When flex distribution is also needed |
absolute + translate(-50%, -50%) | 4 | No | Yes | Overlays, spinners, badges |
margin: auto on a flex item | 1 | Yes | Yes | Centring one item among distributed siblings |
line-height | 1 | Yes | Vertical only | Single-line text only |
What Interviewers Actually Check
- Whether you can write the Flexbox pair correctly and name the axis each property targets
- Whether you mention that the container needs a height for vertical centring to be visible
- Whether you know the Grid one-liner
- Whether you can explain why the negative translate is required rather than just including it
- Whether you pick a technique based on whether the child should participate in layout
Follow-Up Questions
- Why do percentage values in
transform: translate()resolve against the element itself rather than its containing block? - What is the difference between
place-itemsandplace-content, and when does it matter for centring? - How does
100dvhdiffer from100vh, and why does that matter for full-screen centring on mobile? - Can you centre an absolutely positioned element with
inset: 0andmargin: auto? What must be true of the element for that to work? - How would you centre a child that is taller than its container, and what happens with each technique in that case?
Common Candidate Mistakes
- Writing the two Flexbox declarations and expecting vertical centring in a container whose height is derived entirely from its content, leaving no space to centre within
- Carrying
align-items: centeracross aflex-direction: columnchange and centring on the wrong axis - Omitting
transform: translate(-50%, -50%)from the absolute technique, which leaves the element offset by half its own size - Using absolute positioning for ordinary content that should contribute to the parent's height, then adding a fixed height to the parent to compensate
- Using
line-heightcentring on text that can wrap, where every subsequent line inherits the inflated line height and overflows the container
Interview Readiness Checklist
Before you leave this question, make sure you can answer:
- Can you write the Flexbox centring pair from memory and say which axis each targets?
- Can you explain why the container needs a defined or derived height?
- Can you write the Grid one-line equivalent?
- Can you explain what the negative translate compensates for and why it works without knowing the child's size?
- Can you choose between the techniques based on whether the child should stay in flow?
Summary
Flexbox centres a child with display: flex, justify-content: center, and align-items: center, where the first targets the main axis and the second the cross axis. Both depend on the container having space to centre within, so a container whose height comes only from its content will show no vertical centring regardless of the declarations. And because the properties bind to axes rather than screen directions, switching to flex-direction: column swaps which one centres vertically.
Grid does the same job in one declaration with display: grid; place-items: center, which makes it the shortest correct answer and the least error-prone, since there is no axis to track. Absolute positioning with top: 50%, left: 50%, and transform: translate(-50%, -50%) is the third technique, and its distinguishing property is that the child leaves normal flow entirely. The negative translate is required because the offsets place the child's corner at the centre, and it works for unknown sizes because transform percentages resolve against the element itself.
Choose by intent rather than habit. If the centred element must not affect layout, as with spinners, overlays, and badges, use absolute plus transform. If you also need flex distribution among multiple children, use Flexbox. Otherwise reach for place-items: center. Keep line-height centring for text that genuinely cannot wrap, and remember margin: auto on a flex item when one child needs centring while its siblings are distributed.
Which centring technique should be the default?
display: grid with place-items: center is the shortest and works for both axes at once. Flexbox is equally good and preferable when you also need flex distribution behaviour among multiple children.
Why is the absolute plus transform technique still useful?
Because it centres without affecting layout. It is the right choice for overlays, spinners, and badges that must not participate in the flow of their container.
Advertisement
Stay Updated
Get the latest frontend challenges, interview questions and tutorials delivered to your inbox.
Advertisement