TechEarl

Pushing Flex Items Apart: margin-auto, gap, and space-between

Push a logo left and links right in a flexbox nav without an empty spacer div. The three correct tools: justify-content: space-between, margin-left: auto, and gap.

Ishan Karunaratne⏱️ 6 min readUpdated
Share thisCopied
Push flex items apart in CSS without an empty spacer div using justify-content space-between, margin-left auto, and gap.

To push some flex items apart, you do not need a spacer element. Flexbox has three native tools, and which one you reach for depends on the layout:

  • One item to the far end (logo left, account menu right): put margin-left: auto on the item you want pushed.
  • Items spread to both edges with the gap in the middle: justify-content: space-between on the container.
  • Even, fixed spacing between every item: gap on the container.

That covers almost every real case. The empty <div class="spacer"> you may have seen in older tutorials is not one of them, and I will get to why.

Push one item to the far end with margin-auto

This is the cleanest answer to "send this one item to the right." A flex item with margin-left: auto absorbs all the free space on its left, so it and everything after it slide to the end of the row. The classic example is a navbar: brand on the left, the rest of the links flowing after it, and an account menu pinned to the right edge.

css
.nav {
  display: flex;
  align-items: center;
  gap: 1rem;
}

/* Everything before this stays left; this and anything after it go right. */
.nav .account {
  margin-left: auto;
}
html
<nav class="nav">
  <a class="brand" href="/">TechEarl</a>
  <a href="/blog">Blog</a>
  <a href="/about">About</a>
  <a class="account" href="/account">Account</a>
</nav>

An auto margin is greedy: it eats every pixel of free space in its direction. That is exactly the behavior you want here, and it is the idiomatic toolbar pattern. One important interaction to know: auto margins are resolved before justify-content, so once any item in the row has an auto margin, justify-content has nothing left to distribute and stops mattering. Pick one mechanism per axis, not both.

You can also split a row into two groups (left cluster, right cluster) by putting margin-left: auto on the first item of the right-hand group, no wrapper needed.

Spread items to the edges with space-between

When you want the first item flush left, the last item flush right, and the leftover space dropped evenly between the items, set it on the container:

css
.bar {
  display: flex;
  justify-content: space-between;
  align-items: center;
}

The two siblings of space-between are worth knowing:

css
/* space-around: equal space around each item, so half-size gaps at the two ends. */
.a { justify-content: space-around; }

/* space-evenly: truly equal space everywhere, including the two ends. */
.b { justify-content: space-evenly; }

space-between is the one you want for a two-item header (logo left, button right) or a row of tabs that should fill the width. Reach for margin-auto instead when only a single item needs to break ranks while the others stay grouped, since space-between distributes the gap across all the items, not just one seam.

Even spacing between items with gap

If the goal is just consistent breathing room between items (a row of buttons, nav links that should sit a fixed distance apart), gap is the right tool. It is the flexbox property people forget exists, and it has been safe to use across all browsers since 2021.

css
.toolbar {
  display: flex;
  gap: 0.75rem;        /* space between items, none on the outer edges */
}

/* Different row and column gaps if the items wrap: */
.grid-ish {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem 1.5rem;    /* row-gap column-gap */
}

gap only adds space between items, never before the first or after the last, which is usually what you want and what manual margins on every child get wrong (you end up fighting a trailing margin with :last-child). It also composes cleanly with margin-left: auto: use gap for the uniform spacing and one auto margin for the item that breaks to the far edge.

The thing not to do: an empty spacer div

The pattern to retire is the empty filler element:

html
<!-- Don't do this. -->
<nav class="nav">
  <a href="/">TechEarl</a>
  <div class="spacer"></div>
  <a href="/account">Account</a>
</nav>
css
/* Don't do this either. */
.spacer { flex-grow: 1; }

It works, visually. But it adds a non-semantic, empty element to the DOM purely to occupy space, which is exactly the job CSS is supposed to do. A screen reader has nothing to read there, it clutters the markup, and the moment your links change you are maintaining a phantom div. The single line margin-left: auto on the .account link does the same thing with no extra element, so use that. If you genuinely need a spacer with no markup at all, a ::before/::after pseudo-element with flex-grow: 1 is the honest version, but in practice margin-auto and space-between cover it.

For the rest of flexbox (axes, wrapping, alignment, flex-grow/flex-shrink/flex-basis), see my complete flexbox guide. If your goal is dead-centering rather than pushing apart, centering anything in CSS walks through the flexbox, grid, and margin-auto routes. And once you are styling based on what a container holds (a nav that has an active link, say), the CSS :has() selector is the modern companion to these layout tools.

FAQ

Put margin-left: auto on that item. The auto margin absorbs all the free space to its left, pushing the item (and anything after it) to the end of the row. This is cleaner than a spacer element and is the standard navbar pattern.

justify-content: space-between goes on the container and spreads the gap evenly between every item. margin-left: auto goes on one item and pushes just that item to the end while the others stay grouped. Note that auto margins are resolved first, so once an item has one, justify-content no longer has space to distribute.

No. An empty div with flex-grow: 1 works but adds a meaningless element to the DOM. Use margin-left: auto on the item you want pushed, or justify-content: space-between on the container. If you truly need a markup-free spacer, a pseudo-element with flex-grow: 1 is the honest alternative.

Use gap whenever you want even spacing between items. It only adds space between items, never on the outer edges, so you avoid the trailing-margin cleanup that per-child margins require. It has been supported in flexbox across all browsers since 2021.

space-between puts the first and last items flush against the edges with the leftover space split between them. space-around gives each item equal space around it, so the end gaps are half the size of the inner gaps. space-evenly makes every gap, including the two ends, exactly equal.

See also

Sources

Authoritative references this article was fact-checked against.

TagsflexboxCSSmargin autojustify-contentspace-betweengapnavbar layout

Found this useful? Pass it on.

Copied

Ishan Karunaratne

Software Systems Architect · Senior Software Engineer · Engineering Leadership

Software systems architect and senior software engineer with more than two decades designing, building, and running production software, Linux systems, and DevOps infrastructure, and lately working AI into the stack. Now a CTO, though what I write here is drawn from the full arc of that work, across architecture, engineering, and operations, not any single job.

Keep reading

Related posts

Use Claude CLI to run WordPress and ACF work end-to-end. Field group generation, WP-CLI orchestration, log triage, debugging. Real prompts, real output, real limits.

Using Claude CLI to Manage WordPress Sites

How I use Claude CLI to run WordPress and ACF work end-to-end: ACF field group generation, WP-CLI orchestration, log triage, plugin debugging, bulk content ops. Concrete prompts, what it gets wrong, and where it fits in an agency workflow.

ACF Options Pages for site-wide settings: header CTAs, footer links, social profiles. Registration, reading patterns, parent/child structure, cache strategy.

Using ACF Options Pages for Global Site Settings

ACF Options Pages are the right home for site-wide settings: header CTAs, footer links, social profiles, contact info, global announcements. Registration, reading patterns, parent/child structure, and the cache-busting trick for high-traffic sites.

AI + WP-CLI + ACF for bulk content updates. Schema-aware update_field, content rewrites, image alt backfills, the safety patterns that prevent disasters.

Using AI to Update ACF Fields and WordPress Content

AI plus WP-CLI plus ACF is the canonical pattern for bulk content updates that used to take a careful afternoon. Schema-aware update_field calls, content rewrites at scale, image alt backfills, and the safety patterns that prevent disasters.