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

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

Tech Architect · Software Engineer · AI/DevOps

Tech architect and software engineer with 20+ years building software, Linux systems, and DevOps infrastructure, and lately working AI into the stack. Currently Chief Technology Officer at a healthcare tech startup, which is where most of these field notes come from.

Keep reading

Related posts

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.

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.

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.