CSS Flexbox Not Working — Diagnosis and Fix Guide

Quick answer

💡Flexbox only applies to direct children of the flex container. Put display: flex on the parent element, not the child. The most common failures are applying flex properties to the wrong element, missing a defined height for cross-axis alignment, and content overflowing because flex-wrap defaults to nowrap.

Error symptoms

  • Items stack vertically even though you set display: flex
  • justify-content or align-items have no visible effect
  • Flex items overflow the container instead of wrapping
  • Items do not shrink when the container gets narrow
  • A specific item ignores the flex layout and stays fixed
  • Layout looks correct in Chrome but breaks in Safari

Common causes

  • display: flex is on the child element, not the parent container
  • flex-wrap is left at its default nowrap — items never break to a new line
  • The container has no defined height — align-items: center has nowhere to center within
  • min-width: auto on flex children prevents them from shrinking below content width
  • flex-basis conflicts with width — flex-basis takes priority over width in a flex context
  • A deeply nested child expects flex behavior but is not a direct child of the flex container

When it happens

  • Building horizontal navigation bars that should wrap on small screens
  • Creating equal-height card grids where items should fill available space
  • Vertically centering text or icons inside a header or button
  • Converting floated or inline-block layouts to Flexbox
  • Debugging a layout that works in CodePen but breaks inside a React or Vue component

Examples and fixes

Flex properties applied to the child have no effect on layout — they must be on the parent.

display: flex on wrong element

❌ Wrong

<ul class="nav-menu">
  <li class="nav-item">Home</li>
  <li class="nav-item">About</li>
  <li class="nav-item">Contact</li>
</ul>

/* CSS */
.nav-item {
  display: flex;
  justify-content: space-between;
}

✅ Fixed

<ul class="nav-menu">
  <li class="nav-item">Home</li>
  <li class="nav-item">About</li>
  <li class="nav-item">Contact</li>
</ul>

/* CSS */
.nav-menu {
  display: flex;
  justify-content: space-between;
  list-style: none;
  padding: 0;
  margin: 0;
}

justify-content: space-between controls how the flex container distributes its children along the main axis. Applying it to .nav-item means each list item is its own flex container arranging its text content — the overall horizontal distribution is unaffected. Moving display: flex and justify-content: space-between to .nav-menu makes the three li elements flex children that are spaced across the container width.

flex-wrap defaults to nowrap — all items stay on one line and push past the container edge.

Items overflow instead of wrapping

❌ Wrong

<div class="product-grid">
  <div class="product-card">Card 1</div>
  <div class="product-card">Card 2</div>
  <div class="product-card">Card 3</div>
</div>

/* CSS */
.product-grid {
  display: flex;
  gap: 1rem;
}
.product-card {
  width: 300px;
}

✅ Fixed

<div class="product-grid">
  <div class="product-card">Card 1</div>
  <div class="product-card">Card 2</div>
  <div class="product-card">Card 3</div>
</div>

/* CSS */
.product-grid {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
}
.product-card {
  width: 300px;
}

Without flex-wrap: wrap, the three 300px cards on a 800px viewport total 900px plus gap — more than the container can hold. Instead of wrapping, flex items shrink if flex-shrink is non-zero, or overflow if they have a fixed width. Adding flex-wrap: wrap allows items to break into new rows when the total exceeds the container width, producing the intended multi-row grid without any media queries.

min-width: auto on flex children prevents shrinking below their natural content width.

Flex child not shrinking below content width

❌ Wrong

<div class="sidebar-layout">
  <nav class="sidebar">Navigation with long text content</nav>
  <main class="main-content">Main area</main>
</div>

/* CSS */
.sidebar-layout {
  display: flex;
}
.sidebar {
  flex: 0 0 250px;
}
.main-content {
  flex: 1;
}

✅ Fixed

<div class="sidebar-layout">
  <nav class="sidebar">Navigation with long text content</nav>
  <main class="main-content">Main area</main>
</div>

/* CSS */
.sidebar-layout {
  display: flex;
}
.sidebar {
  flex: 0 0 250px;
  min-width: 0;
  overflow: hidden;
}
.main-content {
  flex: 1;
  min-width: 0;
}

Flex items have min-width: auto by default. This means the browser will not shrink a flex child below the width of its content — even if you set flex-shrink: 1. Adding min-width: 0 explicitly overrides this default and allows the flex algorithm to shrink the item as needed. The main-content also needs min-width: 0 so long words inside it do not force the column wider than the available space.

Root causes of broken flexbox layouts

Flexbox failures share a single pattern: a property is applied to the wrong element, or a default value silently overrides your intent. The flex model is a parent-child contract. When you write display: flex, you are declaring that this element's direct children will be laid out as flex items. Properties like justify-content, align-items, flex-direction, and flex-wrap all live on the parent and control how the container arranges its children. Properties like flex-grow, flex-shrink, flex-basis, align-self, and order live on the children and describe how each item behaves within the container.

The most disruptive default is min-width: auto on flex items. In normal block layout, a div shrinks to its container's width freely. In a flex container, each item defaults to min-width: auto — meaning it will never shrink narrower than its content. This is the correct default for most text-heavy layouts, but it causes overflow when you expect columns to shrink to fit the container. Setting min-width: 0 on the flex child overrides this default and allows the flex algorithm to shrink the item below its content width.

flex-wrap: nowrap is the other significant default. All flex items stay on a single line by default, and if they cannot fit, they overflow the container. Most developers expect items to wrap when the container is narrow. Adding flex-wrap: wrap to the container gives items permission to move to the next line. combine it with a min-width or flex-basis on the children to control where wrapping occurs.

flex-basis overrides width in a flex context. If you set width: 50% on a flex item and also have flex-basis: auto in a shorthand like flex: 1, the flex-basis value wins. Always check the computed flex-basis in DevTools when a width appears to have no effect in a flex row.

Using DevTools to find the broken rule

Select the parent container in Chrome or Firefox DevTools Elements panel. A colored flex badge appears next to its tag. Click the badge to open the Flexbox overlay, which draws the main axis, cross axis, and free space directly on the page. This immediately shows whether items are overflowing, whether there is free space that alignment should be using, and whether items are on one or multiple lines.

In the Computed tab, find the element and look at the resolved values for display, flex-direction, flex-wrap, justify-content, and align-items. A value of nowrap in the computed display when you expected wrapping is the instant indicator of why content overflows. A computed height of 0 explains why align-items: center has no vertical effect.

For flex items, check the computed flex-basis, flex-grow, flex-shrink, and min-width. If min-width shows auto and a large resolved value, that is the item that is preventing the column from shrinking. Temporarily set min-width: 0 in the Styles panel and observe whether the layout shifts to confirm the diagnosis.

The Layout pane in Chrome DevTools has a Flexbox section that lists all flex containers on the page. Enable overlay badges to highlight them. This is especially useful in React or Vue applications where the container may be several DOM levels above the visible component, making it hard to find which element actually has display: flex.

Chrome's Flexbox inspector also shows a purple badge specifically on elements with display: flex or display: inline-flex, distinguishing flex containers from grid containers which use a green badge. When a flex item has align-self set to a value that overrides the container's align-items, the overlay draws a dotted line indicating the override is active for that specific item. This makes it straightforward to identify which child is breaking the cross-axis alignment pattern.

Targeted fixes for each failure type

If items are stacking vertically when they should be in a row, verify display: flex is on the parent. Check the computed flex-direction — it should be row for horizontal layout. If it is column (set explicitly or inherited from a theme), change it or reorganize your CSS. Browser stylesheets do not add flex-direction to common elements, so this is nearly always a CSS rule in your own code.

If alignment properties have no effect, the container has no height. For horizontal containers with align-items, add height: 300px or min-height: 100% to the container. For full-viewport centering, use min-height: 100vh. For a container inside a fixed-height parent, add height: 100% and ensure the parent chain has defined heights all the way up.

If items overflow instead of wrapping, add flex-wrap: wrap to the container. Then control each item's minimum width with flex-basis: 250px or min-width: 200px — this dictates when wrapping occurs. Using flex: 1 1 250px (grow, shrink, basis) on children is the cleanest way to create a responsive wrapping layout.

If a specific item refuses to shrink, add min-width: 0 to that item. Also consider adding overflow: hidden or overflow: auto if the item contains text that would be clipped. For text specifically, add overflow-wrap: break-word to prevent long unbreakable strings from forcing the item wide.

For individual items that need different cross-axis positioning from the rest of the group, use align-self on that specific child. align-self accepts the same values as align-items — flex-start, flex-end, center, stretch, baseline — and overrides the container's align-items rule for just that element. This is cleaner than splitting items into separate flex containers to achieve the exception. Similarly, the order property on a flex child changes its visual position within the row without altering the DOM order, which is useful for responsive reordering without JavaScript.

Browser quirks and mobile issues

Safari on iOS has historically had stricter flex behavior than Chrome and Firefox. One well-known issue is that Safari does not always respect flex: 1 on a direct child of a flex column container unless the container has an explicit height. The workaround is to set height: 0 and flex-grow: 1 separately, which forces Safari to distribute height correctly through the flex algorithm.

On mobile devices, the browser chrome (address bar and navigation bar) changes the effective viewport height as the user scrolls. A flex container with height: 100vh may be cut off or show unexpected empty space. Use height: 100dvh (dynamic viewport height) in browsers that support it — Chrome 108+, Safari 15.4+, Firefox 101+. For older mobile browsers, add a JavaScript resize listener or use the VisualViewport API to set the height dynamically.

Flex items with aspect-ratio set may not behave as expected when the flex container changes size. The aspect-ratio property interacts with flex-basis and the flex algorithm in ways that differ between browsers. If an image or video inside a flex item is unexpectedly tall or wide, check whether aspect-ratio combined with a percentage flex-basis is causing unexpected dimension calculations.

Internet Explorer 11 supports an early version of Flexbox but uses non-standard syntax and has numerous bugs. flex: 1 must be written as flex: 1 1 auto for IE 11 compatibility. min-height on flex containers inside a flex column works differently. align-items: stretch is the only reliably consistent behavior across IE 11 and modern browsers. Most production codebases no longer target IE 11, but if you must, use Autoprefixer and test every layout manually.

Mistakes that silently break flex layouts

Using display: inline-flex when you mean display: flex is a subtle mistake. inline-flex creates a flex container that behaves as an inline element in its parent context — useful for a flex button inside a paragraph, but confusing when the intent was a block-level flex row. The items inside are still flex items, but the container itself does not expand to the full width of its parent.

Setting align-items on the container and expecting it to control alignment inside flex children is another common error. align-items controls how children are positioned on the cross axis within the container. If you want to center content within a flex child, that child needs its own display: flex and alignment properties. Flex containers do not cascade alignment rules into their children.

Writing flex: 1 and expecting it to work identically in all browsers is risky. The shorthand expands to flex-grow: 1; flex-shrink: 1; flex-basis: 0. The flex-basis: 0 means the item starts with zero size and grows to fill space. This is often what you want, but it can cause text to disappear if the container is too narrow and shrinking is not constrained. Use flex: 1 1 auto or flex: 1 1 min-content when content must not shrink below a readable size.

Using percentage heights on flex children when the container has no explicit height is a reliable way to break a layout. Percentage heights resolve against the parent's computed height. If the parent has height: auto, percentage heights on children resolve to auto as well — they do not take a percentage of the content height. Add an explicit height to the flex container or use min-height on the children.

Flexbox patterns that work reliably

Build flex layouts from the container inward. Start by deciding what the container needs — flex-direction, flex-wrap, gap, alignment — before touching the children. This sequencing prevents the most common mistake of writing child properties before establishing the flex context.

Always include flex-wrap: wrap on containers that hold multiple items unless you explicitly want items to stay on one line and overflow is acceptable (such as a horizontally scrollable strip). Defaulting to wrap makes layouts resilient to varying content lengths and small screens.

Use the gap property instead of margin on flex children for spacing. gap: 1rem creates equal gutters between all items without the margin-collapse and negative-margin workarounds of the old approach. gap is supported in all modern browsers including Safari 14.1+.

When you need equal-height columns, the default align-items: stretch on the flex container already produces equal-height columns without any additional properties on the children. Only override align-items if you specifically do not want equal-height columns.

Document complex flex values. flex: 0 0 260px is opaque — a comment explaining that the sidebar is fixed at 260px and does not grow or shrink makes the intent clear for future maintainers. The flex shorthand's compactness is valuable in utility CSS but benefits from a comment in component-level stylesheets.

Apply flex-shrink: 0 to any flex child that must never shrink below its flex-basis, such as an icon, avatar, or fixed-width sidebar. Without it, the flex algorithm may compress the element when space is tight, even if you set an explicit width. Pairing flex-shrink: 0 with a defined flex-basis gives the element a guaranteed minimum footprint. For text children that should truncate with an ellipsis, add min-width: 0, overflow: hidden, text-overflow: ellipsis, and white-space: nowrap — min-width: 0 is the key that allows the text container to shrink enough for the ellipsis to engage.

Quick fix checklist

  • Confirm display: flex is on the parent container in DevTools computed styles
  • Open the Flexbox overlay in DevTools to see axis direction and free space
  • Add flex-wrap: wrap if items should break to a new line
  • Add min-width: 0 to children that refuse to shrink
  • Give the container a defined height if align-items is not working
  • Check for flex-basis values overriding width on flex children
  • Test layout at 375px viewport width for mobile breakage
  • Verify Safari-specific behavior if layout differs between browsers

Related guides

Frequently asked questions

Why does display: flex have no effect on my element?

Confirm the element actually has display: flex in computed styles via DevTools — it may be overridden by a more specific rule or by an inline style. Also verify the element is not position: absolute or float: left, which removes it from normal flow and the flex context.

Why do flex items not wrap to the next line?

flex-wrap defaults to nowrap. All flex items stay on a single line, shrinking if possible or overflowing if not. Add flex-wrap: wrap to the flex container. Set a flex-basis or min-width on items to control how wide each item must be before the next one wraps to a new row.

Why does align-items: center have no effect?

The container has no defined height. align-items distributes free vertical space — if the container's height is set by its content, there is no free space to distribute. Add height, min-height, or make the container stretch to fill a parent with a defined height using height: 100% or flex: 1.

Why does a flex item refuse to shrink below a certain width?

min-width defaults to auto for flex items, which resolves to the item's content width. The flex algorithm will not shrink an item below this minimum. Set min-width: 0 on the item to allow it to shrink freely. Pair this with overflow: hidden or overflow-wrap: break-word to handle the clipped content.

What is the difference between flex: 1 and flex: auto?

flex: 1 expands to flex-grow: 1; flex-shrink: 1; flex-basis: 0, meaning items start at zero width and grow to fill available space equally. flex: auto expands to flex-grow: 1; flex-shrink: 1; flex-basis: auto, meaning items start at their content width and then grow. Use flex: 1 for equal-width columns; use flex: auto when items should grow proportionally from their natural sizes.

Can grandchildren of a flex container be flex items?

No. Flexbox only applies to direct children of the flex container. A grandchild is a child of a flex item, not a flex item itself. To apply flex layout inside a flex item, set display: flex on that flex item, making it both a flex item in the outer context and a flex container for its own children.

Why does flex-basis: 50% not make two items per row?

flex-basis: 50% means 50% of the flex container width, but the default flex-wrap is nowrap so items stay on one line regardless. Add flex-wrap: wrap to the container. Also account for gap — two items at 50% each plus a 1rem gap will exceed 100% and still wrap to separate lines. Use calc(50% - 0.5rem) or reduce to 48%.

Why is my flexbox layout different in Safari?

Safari has several known deviations from the flexbox specification, particularly with flex: 1 inside column containers without explicit height, and percentage-based flex-basis inside a shrinking container. Use height: 0; flex-grow: 1 as a Safari-compatible alternative to flex: 1 in column layouts. Autoprefixer handles vendor prefixes but does not fix logical layout bugs.

Should I use flexbox or grid for a navigation bar?

Flexbox is the natural choice for a navigation bar because navbars are one-dimensional — items flow in a single row. Grid adds overhead you do not need for a simple row. Use Flexbox with justify-content for spacing and align-items for vertical alignment. Switch to Grid only if the navigation has two-dimensional structure like a megamenu.

All tools run in your browser. Your data never leaves your device. Last updated: 2026-05-06.