skip to content

In a CSS Grid container, what is the difference between justify-items and justify-content?

level: middleimportance: must knowfreq 74%

answer

  1. two nested boxes, one axis
  2. free space between tracks and container
  3. fr leaves nothing for content alignment
  4. items get a -self version, content does not
  5. the split flexbox never had

basics

~20 s

justify-items aligns each grid item inside its own column track along the inline axis. justify-content distributes the column tracks themselves inside the grid container, and only does anything when the tracks are narrower than the container.

solid answer

~40 s

They act on different boxes. `justify-items` aligns each **item within its grid area** along the inline axis — start, end, center, or the default stretch. `justify-content` aligns the **whole set of column tracks within the grid container**, using the leftover space between the container's content box and the tracks, which is why it also accepts distribution values like `space-between` and `space-evenly`. The block-axis pair mirrors it exactly: `align-items` positions an item inside its row track, `align-content` distributes the row tracks in the container. The practical tell is free space: if your columns are `1fr 1fr` there is no leftover space, so `justify-content` looks broken while `justify-items` still works. That items-versus-tracks split does not exist in Flexbox, and it is the thing candidates coming from flex most often get wrong.

code

css · 12 lines
css
.grid {
  display: grid;
  grid-template-columns: repeat(3, 200px);
  width: 900px;
  height: 400px;

  /* moves the three 200px tracks inside the 900px container */
  justify-content: space-between;

  /* moves each item inside its own 200px track */
  justify-items: start;
}

go deeper

for a junior

Learn the four container properties by the box they move: -items moves an item inside its cell, -content moves the tracks inside the container. Say which one you would use before you write the CSS.

for a middle

Be ready to explain the free-space mechanic — why fr tracks make justify-content inert, and why only the content properties accept space-between and space-evenly.

for a senior

Diagnose it live: given a screenshot where the layout is off, name which of the four properties is the wrong one and why, including the tall-container case where align-content is the fix.

for a principal

Own the convention question — decide whether layout containers set alignment centrally or leave it to items, so teams do not fight the same items-versus-content confusion in every component.

## Two different boxes, one axis Grid alignment is easy once you accept that there are two nested things to align along each axis, not one: 1. The **tracks** (the rows and columns) sit inside the grid container's content box. 2. Each **item** sits inside the grid area it was placed into. `justify-content` and `align-content` handle step 1. `justify-items` and `align-items` handle step 2. Both pairs are split by axis the same way: `justify-*` is the inline axis (horizontal in a left-to-right, top-to-bottom writing mode), `align-*` is the block axis (vertical there). So the full container-level matrix is: | | inline axis | block axis | |---|---|---| | tracks inside container | `justify-content` | `align-content` | | item inside its area | `justify-items` | `align-items` | and each item can override its own row of that table with `justify-self` / `align-self`. ## justify-items: the item inside its area ```css .grid { display: grid; grid-template-columns: repeat(3, 200px); justify-items: start; } ``` Each item now sits at the inline-start edge of its 200px column, sized to its content instead of filling the track. The tracks have not moved — they are still three 200px columns in the same place. Legal values are the positional ones (`start`, `end`, `center`, `left`, `right`, `flex-start`/`flex-end` as legacy aliases), plus `stretch` and `normal`, plus baseline alignment. Notably it takes **no** distribution values: `space-between` on `justify-items` is invalid, because a single item inside a single area has nothing to distribute against. ## justify-content: the tracks inside the container ```css .grid { display: grid; grid-template-columns: repeat(3, 200px); /* 600px of track */ width: 900px; /* 300px of free space */ justify-content: space-between; } ``` Here the three tracks total 600px inside a 900px container, leaving 300px of free space. `justify-content` decides what happens to that 300px: `start` puts it all after the tracks, `end` before, `center` splits it evenly on both sides, `space-between` puts 150px between each adjacent pair with none at the edges, `space-around` gives each track equal space on both sides (so edge gaps are half the internal ones), `space-evenly` makes all gaps equal. Because it moves the tracks, it moves every item in them with the track. ## The free-space rule that explains the confusion `justify-content` needs leftover space to do anything. The overwhelmingly common reason it "does nothing" is that the track sizing already ate the space — `1fr` columns, `auto` columns that expand, or `repeat(auto-fit, minmax(...))`. `fr` is defined as a share of the *free space*, so once you use it there is, by construction, none left for content alignment. If you want gaps between tracks, size the tracks intrinsically (fixed widths, `max-content`, `min-content`) and then `justify-content` becomes meaningful. Conversely, `justify-items` does not care about container free space at all — it works inside whatever the track ended up being. ## The block-axis twin, and the tall-container case The same story runs vertically. A container with `height: 100vh` and two `auto` rows leaves a large block-axis free space. `align-items: center` will center each item **inside its own row**, which is not what you want if the rows themselves are stuck at the top — you want `align-content: center` to move the row group into the middle of the container. Getting this backwards is the classic "I set align-items: center and nothing moved" bug report. Note one asymmetry: the initial `normal` behaves as `stretch` for `align-content`/`justify-content` too, and stretching distributes leftover space into **auto-sized tracks** — so with `grid-template-rows: auto auto` and a tall container, the rows themselves grow. Tracks sized with a fixed length or `fr` do not participate. ## Shorthands and per-item overrides `place-content` is `align-content justify-content`; `place-items` is `align-items justify-items`; `place-self` is `align-self justify-self` — always block axis first. There is deliberately **no** self-version of the content properties: content alignment is a property of the container's track layout, so a single item cannot opt out of it. ## Why Flexbox intuition misfires In a flex container, `justify-content` distributes the items themselves along the main axis and `justify-items` is ignored entirely — there is only one box to align, so flex users learn one property where grid has two. Carrying that habit into grid produces exactly the two failure modes above: reaching for `justify-content` when you meant per-item alignment, and reaching for `align-items` when you meant to move the whole track group.

  • Why does justify-content often appear to do nothing on a grid?
    Because there is no free space to distribute. `justify-content` only positions the tracks inside the container's content box, and `fr`, `auto`, or `auto-fit` tracks are defined to absorb the leftover space themselves. With `grid-template-columns: 1fr 1fr` the tracks exactly fill the container, so every `justify-content` value looks identical. Give the tracks intrinsic or fixed sizes and it starts behaving.
  • Is there a justify-self equivalent for justify-content?
    No, and deliberately so. `justify-self` and `align-self` exist because item alignment is per-item, but content alignment describes how the container arranges its whole track group — a single item cannot opt out of where its track sits. If you need one item positioned differently, use `justify-self` (or an auto margin) inside its area instead.
  • A grid container is 100vh tall, its rows sit at the top, and align-items: center changed nothing. What is wrong?
    `align-items` centers each item inside its own row, and the rows are already exactly as tall as their content, so nothing moves. The free space is between the row group and the container, which is `align-content`'s job — `align-content: center` moves the whole set of rows into the middle. `place-content: center` does both axes at once.

saying these in an interview costs you the question

  • Says justify-content centers items inside their cells
  • Uses space-between as a justify-items value
  • Assumes grid alignment behaves exactly like flex
  • Claims justify-self is the self version of justify-content
  • Blames the browser when 1fr tracks ignore justify-content

context