A sidebar sits next to a block of text. The text contains a long, unbroken string: a filename, a URL, whatever. text-overflow: ellipsis is in place and should truncate it. Instead the text just keeps going, blowing out the container width and shoving the sidebar sideways. Nothing in the CSS looks wrong. There's no console error. It just doesn't work.
That bug shows up in real codebases constantly, and it has almost nothing to do with text-overflow itself; it comes from a default Flexbox behavior most explanations never mention. It's a good entry point, because it demonstrates something true about Flexbox in general: most of the API is simple, and most of the frustration comes from a small number of default behaviors that nobody points out until you've already lost an hour to one.
This post works through four layouts that show up repeatedly in real projects: a centered element, a card row that reflows without media queries, a sidebar that doesn't break under long content, and a footer that sticks to the bottom of its container. Every demo below is live CSS, rendered directly on this page, using the exact code shown above it.
What Flexbox Actually Does
Flexbox arranges items along a single line (a row or a column) and lets those items grow, shrink, or align based on rules set on the parent. That single-line constraint is the important part. It's why Flexbox resists being used for a full grid where rows and columns both need to line up together; that's a job for CSS Grid instead. Flexbox is built for one-directional arrangement: a navbar, a row of cards, a sidebar next to content, a label sitting beside an input.
A fast way to decide between the two:
laying things out along one line → Flexbox. Laying things out in a grid where rows and columns both need to align to each other → Grid. This decision gets overthought more than it deserves.
Centering: The First Wall Everyone Hits
Centering something in CSS (a button, a modal, a spinner) used to mean writing margin: 0 auto, getting horizontal centering, and then hitting a wall on vertical centering. Before Flexbox, vertical centering meant absolute positioning combined with manually calculated negative margins. It worked, but it was fragile and annoying to maintain.
Flexbox reduces that to three lines:
.container {
display: flex;
align-items: center;
justify-content: center;
}
justify-content controls the main axis, which is horizontal by default. align-items controls the cross axis, vertical by default. Here's that exact code rendered live:
Resize the browser window, view it on a phone, and the box stays centered regardless of viewport size, with no media queries and no manual math involved.
One detail that's easy to miss: this pattern changes slightly when centering multiple stacked items instead of one. align-items still behaves the same way, but stacking items vertically usually means adding flex-direction: column, which swaps which axis counts as "main" and which counts as "cross." That swap is covered in more detail further down, since it trips people up constantly.
A Card Row That Reflows Without Media Queries
A common layout: three pricing tiers, or three feature cards, sitting side by side on desktop and stacking or wrapping on smaller screens.
The approach to avoid is a fixed width on each card. It looks fine until the viewport lands at, say, 850px, where two and a half cards end up visible and the layout looks broken. A more durable approach uses the flex shorthand. It's a property that gets ignored or misused often enough that it's worth breaking down properly.
.card-row {
display: flex;
flex-wrap: wrap;
gap: 16px;
}
.card {
flex: 1 1 220px;
background: #fff3cd;
padding: 20px;
border-radius: 8px;
}
flex: 1 1 220px is shorthand for three separate values (flex-grow, flex-shrink, and flex-basis), and unpacking each one clears up most of the confusion around this property:
- flex-grow: 1: the card can grow to fill extra space if there's room
- flex-shrink: 1: the card can shrink if space runs short
- flex-basis: 220px: before any growing or shrinking happens, each card starts at 220px
Paired with flex-wrap: wrap on the parent, cards sit in a row whenever there's at least 220px of space per card, and drop to the next line automatically once there isn't. No breakpoints required. Here it is live:
Good for small teams testing things out.
For teams that outgrew the basics.
Built for heavier, ongoing usage.
Shrinking the browser window shows these reflow on their own: that's the full responsive strategy for this pattern. A media query can still be layered on top to fine-tune spacing at specific widths, but it's optional rather than required.
| Shorthand | What it expands to | Typical use |
|---|---|---|
flex: 1 |
grow: 1, shrink: 1, basis: 0% | Equal-width columns, ignoring content size |
flex: auto |
grow: 1, shrink: 1, basis: auto | Size based on content, but still flexible |
flex: none |
grow: 0, shrink: 0, basis: auto | Fixed size, never grows or shrinks |
flex: 1 1 220px |
grow: 1, shrink: 1, basis: 220px | Cards or columns with a sensible starting size |
flex: 1 1 220px). It covers the majority of real layout situations, more than the single-keyword versions like flex: auto that most references lead with.The Sidebar Bug From the Top of This Post
Back to the bug that opened this article: a sidebar next to a main content area, where the main content contains a long unbroken line of text.
The code below looks like it should work:
.layout {
display: flex;
gap: 16px;
}
.sidebar {
flex: 0 0 130px;
}
.main {
flex: 1 1 auto;
}
.main p {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
text-overflow: ellipsis truncates long text with "…" on a normal block element without issue. Inside a flex item, though, it silently fails. The text just extends past the intended boundary, pushing neighboring items around or forcing horizontal scroll. There's no warning and nothing looks wrong in the stylesheet itself.
The cause: flex items have a default min-width of auto, which effectively means "never shrink smaller than the content's natural size." A long unbroken line of text has a large natural width, so the flex item refuses to shrink below that width, and because the container has technically just grown to fit its content rather than overflowed it, the browser never applies the overflow rule in the first place.
The fix is a single added line:
.main {
flex: 1 1 auto;
min-width: 0;
}
min-width: 0 tells the flex item it's allowed to shrink smaller than its content, which is what lets the overflow and ellipsis rules actually take effect. To confirm this is the cause of a similar issue elsewhere:
- Open dev tools and select the flex item whose content is overflowing
- Check the computed styles panel for
min-width, if it readsauto, that's the source - Add
min-width: 0directly in the styles panel and check whether the overflow resolves immediately - If it does, move the change into the actual stylesheet
It's a quick check once it's on the radar. Here's the broken and fixed versions side by side, both live, both using genuinely long text:
Compare the sidebar width in both examples. In the first, the container stretches to accommodate the text. On a narrower viewport, that same behavior would squeeze or overflow the sidebar. In the second, the text respects its box entirely. Same content, same available width, one property different.
Related Posts
Why an Icon Turns Into an Oval
A related bug with a different symptom: a notification row with a circular avatar or icon next to some text. The text is long enough to wrap or get cut off, fine. But the circle stops being a circle, squeezed into an oval as the text pushes against it.
The cause is the same family of default behavior: every flex item has flex-shrink: 1 by default, meaning it can shrink if the row runs short on space. The icon has an explicit width and height, but the shrink behavior doesn't respect that. It compresses the box regardless, unless told otherwise.
.avatar {
width: 40px;
height: 40px;
border-radius: 50%;
flex-shrink: 0;
}
flex-shrink: 0 opts that single item out of shrinking entirely. It stays exactly 40px regardless of what the adjacent text is doing. It's a small fix, but it's the difference between a layout that holds up under long content and one that visibly distorts.
Flex Direction: The Property That Swaps the Axes
Everything so far has assumed a row layout, which is Flexbox's default direction. Adding flex-direction: column swaps the main and cross axes: justify-content now controls vertical positioning, and align-items controls horizontal. This is exactly where the centering pattern mentioned earlier tends to break: code copied from a row layout into a column layout stops behaving the way it did originally, because the axes it's targeting have flipped.
.column-layout {
display: flex;
flex-direction: column;
height: 220px;
}
.header { background: #a29bfe; }
.main-content { flex: 1 1 auto; }
.footer { background: #00b894; }
flex: 1 1 auto on the main content section does the actual work here: it tells that section to grow and absorb any leftover vertical space, which pushes the footer down to the bottom of the container naturally. This is the non-hacky way to build a sticky footer without absolute positioning. Here it is live:
Reducing the middle section's content doesn't change where the footer sits; it stays anchored to the bottom of the container regardless. That behavior makes this pattern useful well beyond footers: cards, modals, and full-page layouts all lean on the same mechanic.
flex: 1 1 auto clicks as a "fill the remaining space" tool, more use cases tend to show up: chat interfaces, dashboard panels, form layouts with a scrollable middle section. It's one of the more reusable patterns covered here.When Flexbox Is the Wrong Tool
Not every container benefits from display: flex. Wrapping a single paragraph in a flex container, for instance, adds a rule to the DOM without changing anything about how it renders. Flexbox solves alignment and distribution between sibling elements: with only one child, or with children that don't need special alignment, that problem doesn't exist yet.
A useful filter before reaching for Flexbox: is this actually arranging multiple items relative to each other? If yes, it's the right tool. If the real goal is just spacing or nudging one element to one side, plain margin sometimes does the job with less code and less nesting.
What gap Replaced
Worth knowing for anyone who learned Flexbox before gap was supported inside flex containers: spacing items apart used to mean adding margin-right to every child except the last, typically via a :not(:last-child) selector, or just accepting a stray bit of margin hanging off the end of the row.
/* old approach */
.item:not(:last-child) {
margin-right: 16px;
}
/* current approach */
.container {
display: flex;
gap: 16px;
}
gap handles spacing between items without touching the outer edges. It's supported across every modern browser at this point, which leaves little reason to keep the selector-based version around outside of legacy-browser support requirements.
A Realistic Navbar, Assembled
Closing with a layout that's directly reusable: a navbar with a logo on the left, nav links in the middle, and a button on the right, all vertically centered on a single line.
.navbar {
display: flex;
align-items: center;
justify-content: space-between;
gap: 20px;
padding: 14px 20px;
}
.nav-links {
display: flex;
gap: 18px;
list-style: none;
}
justify-content: space-between is doing the real work: it pushes the first and last items to opposite edges and distributes anything in between evenly. Nesting a second flex container specifically for the links produces a realistic navbar in a handful of lines:
That structure is genuinely close to production-ready. Real links, a hover state, and a media query to collapse the links into a hamburger menu below a given width would round it out: no positioning hacks or float clearing involved, none of the workarounds Flexbox replaced.
Handling narrow viewports
The navbar above starts feeling cramped once there isn't enough room for the logo, links, and button to sit comfortably on one line. Two realistic options exist, and the right one depends on how many nav links are actually present.
Letting it wrap
The simplest option: add flex-wrap: wrap to the .navbar rule. Links or the button drop to a second line automatically once space runs out, with no JavaScript involved. This holds up fine for two or three links but gets messy beyond that.
Hiding links behind a menu button
For anything with more than a handful of links, the standard pattern is a media query that hides .nav-links below a certain width and reveals a menu icon in its place, which then toggles a dropdown or slide-out panel on click. That toggle behavior needs a bit of JavaScript; Flexbox itself only handles layout once the menu is open or closed, not the show/hide logic driving it.
Questions That Come Up Around This Topic
Is Flexbox safe to use in production without fallbacks?
Yes. Every modern browser, desktop and mobile, supports it fully and has for years. The main historical gotcha was a partial, buggy implementation in very old Internet Explorer versions, not a concern unless there's a specific business requirement to support IE11 or earlier.
Flexbox vs. Grid: how is the choice actually made?
Flexbox arranges items along one axis at a time, a row or a column. Grid arranges items along two axes at once, with alignment across both rows and columns simultaneously. A single strip of items usually calls for Flexbox; a layout with a header, sidebar, main content, and footer all aligning to a shared structure usually calls for Grid. Many real layouts use both: Grid for the page skeleton, Flexbox for arranging items inside individual sections.
align-items isn't centering anything vertically: what's the usual cause?
Two common causes. First, check for flex-direction: column on the container: that swaps which axis align-items controls, and justify-content becomes the property responsible for vertical centering instead. Second, confirm the flex container has a defined height. A container that's shrink-wrapped to its content has no extra vertical space for centering to act on.
In plain terms, what is flex: 1 actually doing?
It tells an item to grow and shrink freely, splitting available space roughly evenly with sibling items that also carry flex: 1, while disregarding the item's natural content-based size as a starting point. To keep a sensible starting width instead of starting from zero, flex: 1 1 200px is the better fit: it starts each item at 200px before distributing leftover space.
Can an entire page, header through footer, be built with just Flexbox?
Yes, and plenty of sites do exactly this, especially simpler ones. Setting display: flex and flex-direction: column on a wrapper around the header, main, and footer, then giving the main section flex: 1 1 auto, fills leftover space and pushes the footer down. It's a solid approach for straightforward layouts. For anything with more complex two-dimensional structure (a dashboard with several independently-aligned regions, for instance), Grid tends to be worth the small extra learning curve.
Why does a flex item ignore the explicit width set on it?
This traces back to flex-shrink or flex-grow overriding the explicit width. By default, every flex item can both grow and shrink, and those behaviors take priority over a plain width property in most cases. To make an item respect a fixed width and resist resizing entirely, flex: 0 0 [width] (or flex-shrink: 0 paired with flex-grow: 0) locks it in place regardless of what sibling items are doing.
The Pattern Behind the Bugs
Every Flexbox bug covered here traces back to the same handful of defaults: min-width: auto, flex-shrink: 1, and axis behavior that depends entirely on flex-direction. None of these are edge cases; they're the standard, spec-defined behavior of every flex item unless something overrides it. The reason they feel unpredictable the first few times is that the override is rarely the obvious fix; it's usually one property away from where the visible symptom shows up.
That's the more useful way to hold onto this material: not as four unrelated layouts, but as one small set of default behaviors that resurface in different shapes depending on the layout. A squished icon and an overflowing sidebar look like unrelated bugs. They're the same shrink behavior, applied to two different kinds of content.
This covers the patterns that come up most often in day-to-day layout work, not the full specification. Properties like order and align-self exist for individual-item overrides and aren't covered here. For the complete reference, MDN's Flexbox documentation stays current and doesn't oversimplify the edge cases.