Why Your CSS Grid Layout Breaks on Mobile: Common Mistakes and Fixes

The layout looks tidy on your laptop. Then you drag the browser window down to a phone width and everything goes sideways: a horizontal scrollbar appears, one card has thrown itself onto its own row, and an image is hanging out past the edge of the container. Nothing in the CSS looks obviously wrong. That is the trouble with Grid — when it fails, it fails quietly, and the cause is usually two or three properties away from where the damage shows up.

The good news is that mobile Grid bugs fall into a handful of recognisable patterns. Once you know what to look for, most of them take a minute to fix.

Grid items refuse to shrink below their content

The most common cause of a broken mobile grid isn't the grid at all. It's the item sitting inside a track. Every grid item has an effective min-width of auto, which means it will not shrink below the smallest size its content can physically take. A long unbroken URL, a six-column table, a code block, or an image with a fixed width will all push a track wider than the screen.

So a rule like grid-template-columns: repeat(4, 1fr) does not mean "four equal columns, whatever happens". It means "four columns sharing the leftover space, each at least as wide as its content needs". On a wide desktop that distinction is invisible. On a 360px phone it is a scrollbar.

Three fixes, in order of preference

  1. Change the track definition to minmax(0, 1fr). This sets the minimum to zero and lets the column shrink as far as it needs to.
  2. If you can't touch the track, set min-width: 0 on the offending child item.
  3. Fix the content itself: overflow-wrap: anywhere on long strings, max-width: 100% on images, and a scroll container wrapped around wide tables or code samples.

The third option is the honest one, because it deals with the actual cause. The first two stop the symptom from spreading across the rest of the layout.

Implicit tracks don't follow your media queries

If you place an item at grid-column: 5 while your grid only defines four columns, Grid quietly creates a fifth. That's an implicit track, sized by grid-auto-columns, which defaults to auto. Implicit tracks are not covered by your media queries. They don't respond when the viewport narrows, and they often only show up at one breakpoint, which is why this bug feels random rather than systematic.

The same applies to rows. Usually that's harmless, since auto-sized rows simply grow taller as text wraps. But a dashboard using fixed row heights for a tidy desktop look will clip content on a narrow screen, and the overflow will be vertical instead of horizontal.

To find them, open DevTools, switch on the grid overlay and count the lines. If you see column lines numbered beyond the ones you defined, you have implicit tracks. The fix is to stop hard-coding line numbers and use spans instead — grid-column: span 2 survives a change in column count, whereas grid-column: 2 / 4 does not. Where you genuinely need repositioning, redefine it inside the relevant media query rather than fighting it.

minmax() and auto-fit: read the fine print

repeat(auto-fit, minmax(300px, 1fr)) is one of the most copied lines in CSS, and for good reason. It gives you a responsive card grid with no breakpoints at all. It also breaks the moment the viewport is narrower than 300px plus your gaps, because the track has a hard floor and simply refuses to go below it.

The fix is to make the minimum responsive: minmax(min(300px, 100%), 1fr). Now the track asks for 300px but accepts the full available width when that is smaller.

There's a second trap here. auto-fit collapses empty tracks and stretches the remaining items to fill the row; auto-fill keeps them, so the last row can look oddly short. If a card suddenly looks stretched at a mid-size viewport, that behaviour is usually the reason, not your item styles.

Gaps, padding and the maths you forgot

Gaps are real space, but the fr unit accounts for them automatically, so they are rarely the culprit. Fixed widths are. A card with width: 320px inside a 1fr track will overflow no matter how sensible the grid definition looks. The same goes for a nested grid with its own padding, or a translate transform that pushes an element visually past the edge without affecting layout.

Search your stylesheet for any px width on a grid child and ask whether it needs to be fixed at all. Most of the time max-width is what you actually wanted.

If you have to add overflow-x: hidden to make a layout behave, you have hidden the symptom rather than fixed it — and you may have broken sticky positioning in the process.

A practical debugging sequence

When a grid misbehaves on a small screen, work through this in order. It takes a couple of minutes and saves a lot of guessing.

  • Turn on the grid overlay in DevTools and look for line numbers you never defined.
  • Put a temporary outline on every grid child to see which one is widest.
  • Set min-width: 0 on that child. If the overflow disappears, you've found it.
  • Test at 320px, not 375px. If it survives 320px it will survive almost anything.
  • Paste a long unbroken string into the text, then bump the font size up. Grid problems that hide at 16px tend to show themselves at 24px.
  • Check nesting. A grid inside a flex item inside another grid can inherit constraints you forgot were there.

Build mobile first, then add columns

Start with a single column and add tracks only when the content genuinely needs them. Write the narrow layout first, then use a media query to introduce columns — not to remove them. Choose your breakpoint where the content starts to look cramped, rather than where a particular device happens to sit, and keep it in em units so it scales with the user's font size.

It's also worth being honest about when Grid isn't the right tool. If your layout is one-dimensional — a row of buttons that wraps onto two lines — flex-wrap will do the job with less ceremony. Grid earns its place when you need items aligned in two directions at once.

Before you ship, narrow the window to 320px and scroll down the page. If nothing escapes its container, you have done the important part. A handful of minmax(0, 1fr) declarations and one or two min-width: 0 rules will clear up most of what remains.

Photo: Pixabay / Pexels

Related News
New Year Codebase Health Check: A January Checklist for Development Teams

A practical January checklist for development teams: audit dependencies, target test coverage, prune...

A Developer's Checklist for GDPR-Compliant Logging

A practical checklist for keeping personal data out of application logs, setting sensible retention...

AWS vs Azure vs Google Cloud: Choosing a Cloud Provider for UK SMEs

A practical comparison of AWS, Azure and Google Cloud for UK SMEs, covering UK regions and data residency,...