How to fix horizontal scrolling on mobile (12 causes)
Page wobbles sideways on phones? The 12 usual causes of horizontal scrolling on mobile, a fix for each, and a console snippet that finds the culprit fast.
Horizontal scrolling on mobile means one element on the page is wider than the screen, so the whole page gets wider and wobbles sideways under your thumb. The fix is to find that element and make it fit, not to hide the overflow. Below are the 12 usual suspects, a fix for each, and two quick ways to catch the culprit.
Find the culprit first
Guessing wastes time. When a page is 40px too wide on a 360px phone, one or two elements stick out past the right edge and everything else is innocent.
The DevTools console snippet
Open the page in Chrome or Edge, open DevTools, switch on the device toolbar and set a real phone width: 360 for a Galaxy S25, 402 for an iPhone 17 Pro. Then paste this into the Console:
(() => {
const edge = document.documentElement.clientWidth;
const hits = [...document.querySelectorAll('body *')]
.map((el) => ({ el, over: Math.round(el.getBoundingClientRect().right + window.scrollX - edge) }))
.filter((hit) => hit.over > 0)
.sort((a, b) => b.over - a.over);
hits.forEach((hit) => { hit.el.style.outline = '2px solid red'; });
console.table(hits.map((hit) => ({
element: hit.el.tagName.toLowerCase() + (hit.el.id ? '#' + hit.el.id : '') +
[...hit.el.classList].map((c) => '.' + c).join(''),
'px past the edge': hit.over,
})));
return hits.map((hit) => hit.el);
})();
It compares every element's right edge with document.documentElement.clientWidth (the width the page actually has, without any scrollbar), outlines the offenders in red and prints a table, worst first. When you read it:
- Parents show up with their children. If a
sectionand theimginside it both stick out by 120px, the image is usually the real problem. - Clipped things can show up too. An element inside an
overflow: hiddenbox may be listed without widening the page. If fixing it changes nothing, move on. - Only the right edge counts. On left-to-right pages, anything hanging off the left is cut off, not scrollable.
DevTools device mode is a fine first look, but it's not a phone (here's why).
With doesitfit
Paste your address (or localhost) into doesitfit and every screen that scrolls sideways gets a SPILLING +Npx sticker: the page is N pixels too wide there. Click it and press Show me where to jump to the elements that stick out, each outlined with a "+Npx past the edge" label. Copy fix prompt turns that into a ready-made prompt for Claude, ChatGPT or Cursor.
The 12 common causes, with fixes
1. Fixed pixel widths and min-widths
A desktop layout says width: 600px or min-width: 1024px, and a 360px phone can't argue with it. AI tools produce the same thing with Tailwind's arbitrary values, like w-[600px].
/* Spills on phones */
.signup-card { width: 600px; }
.wrapper { min-width: 1024px; }
/* Fits */
.signup-card { width: 100%; max-width: 600px; }
.wrapper { min-width: 0; }
In Tailwind, swap w-[600px] for w-full max-w-[600px]. Rule of thumb: widths are maximums, not promises.
2. width: 100vw
100vw sounds like "the width of the screen", but it's the viewport width including a classic scrollbar. On Windows, and on Macs set to always show scroll bars, a 100vw section spills by exactly the scrollbar's width. It spills on phones too when the element doesn't start at the left edge, for example inside a padded container.
.hero { width: 100%; } /* not 100vw */
Block elements are full width by default, so you can often just delete it.
3. Images, videos and iframes without max-width
A 1600px photo, or an embed pasted with width="560", is wider than any phone unless CSS says otherwise. Put this near the top of your stylesheet:
img, video { max-width: 100%; height: auto; }
svg, canvas, iframe { max-width: 100%; }
/* YouTube or Vimeo embeds: full width, keep the shape */
.video-embed { width: 100%; height: auto; aspect-ratio: 16 / 9; }
height: auto keeps images in proportion as they shrink. Keep the width and height attributes in your HTML anyway, so the browser can reserve space while they load (see responsive images with srcset and sizes).
4. Long words, URLs and email addresses
Browsers mostly break lines at spaces and hyphens. A long URL or an address like bookings@averylongbakeryname.com has neither, so it pushes straight past the edge.
.prose, .card p, td { overflow-wrap: anywhere; }
overflow-wrap: anywhere lets the browser break a word that would otherwise overflow. Its older cousin break-word breaks words too, but it doesn't lower the box's minimum width, so inside flex items, grid items and table cells only anywhere actually helps (MDN: overflow-wrap). Put it on text areas rather than everything: on a squeezed button it can split a word letter by letter.
5. Wide tables
A comparison table with five columns has a minimum width, and it's usually more than 360px. Let it scroll inside its own box so the page doesn't have to:
<div class="table-scroll" role="region" aria-label="Plan comparison" tabindex="0">
<table>…</table>
</div>
.table-scroll { max-width: 100%; overflow-x: auto; }
tabindex="0" lets keyboard users scroll it too. For simple data, stacking rows as cards on phones is often nicer.
6. pre and code blocks
<pre> keeps every space and line break, so one long line of code sets the width of the whole block.
pre { max-width: 100%; overflow-x: auto; }
/* or wrap long lines instead of scrolling them */
pre { white-space: pre-wrap; overflow-wrap: anywhere; }
Still spilling inside a flex or grid layout? That's cause 9 or 10.
7. Negative margins and full-bleed hacks
Negative margins pull an element outwards: fine when a parent's padding absorbs them, a spill when it doesn't. A Bootstrap 5 .row outside a .container sticks out 12px on each side with the default gutter, and the full-bleed trick margin-inline: calc(50% - 50vw) inherits cause 2's scrollbar problem.
/* A negative margin must be matched by padding on the parent */
.container { padding-inline: 16px; }
.container > .row { margin-inline: -16px; }
Simpler still: end the container and give the full-width section its own inner padding, as in mobile padding that works.
8. Off-screen menus and animations
A menu that slides in from the right is often "hidden" with transform: translateX(100%), which parks it past the right edge, where it widens the page. Scroll animations that "fly in from the right" do the same before they play.
For menus, hide them properly when closed with display: none (or the hidden attribute). If you want the slide animation, put the drawer inside a full-screen layer that clips it:
.drawer-layer { position: fixed; inset: 0; overflow: hidden; pointer-events: none; }
.drawer { position: absolute; top: 0; right: 0; height: 100%; pointer-events: auto; }
/* keep your translateX(100%) closed state and open state as they are */
Also make a closed drawer inert so keyboard users can't tab into an invisible menu. For animations, clip the section they happen in with overflow-x: clip (see below for why that's fine and hidden isn't).
9. Flex items that won't shrink
Flex items have min-width: auto by default: "never narrower than my content". A long word, image, code block or input inside one stops it shrinking, and the row pushes past the edge.
.row { display: flex; flex-wrap: wrap; gap: 16px; }
.row > * { min-width: 0; }
min-width: 0 lets items shrink, and flex-wrap: wrap lets them drop to a new line when they genuinely don't fit. In Tailwind that's min-w-0 and flex-wrap.
10. Grid columns with plain 1fr
A 1fr column is really minmax(auto, 1fr): it can't get narrower than its widest content. Same trap as flexbox, different spelling.
.grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); }
/* Better: as many 240px+ columns as fit, one column on phones */
.grid {
display: grid;
gap: 16px;
grid-template-columns: repeat(auto-fit, minmax(min(100%, 240px), 1fr));
}
The min(100%, 240px) stops the 240px minimum from spilling when the container is narrower than that. Tailwind's grid-cols-3 already uses minmax(0, 1fr), so there the usual bug is simply three columns on a phone: use grid-cols-1 md:grid-cols-3.
11. Big fixed font sizes and long headings
A 72px hero heading looks great on a laptop. On a phone, one long word at that size can be wider than the screen. Let the size scale:
h1 {
font-size: clamp(2rem, 1.2rem + 4vw, 4rem);
overflow-wrap: break-word;
text-wrap: balance;
}
That's about 34px on a 360px phone and 64px on a laptop (MDN: clamp()). Keep a rem part in the middle value: text sized only in vw doesn't get bigger when someone zooms in.
12. Third-party embeds and widgets
Social embeds, map iframes pasted with width="600", booking widgets and chat bubbles bring their own widths, sometimes as inline styles.
.embed-wrap { max-width: 100%; overflow-x: auto; }
/* An inline style="width: 600px" beats your CSS unless you use !important */
.embed-wrap iframe { width: 100% !important; }
Check the widget's own settings first; many have a responsive option. If one still spills and you can't change it, a wrapper that scrolls beats a page that does.
Why overflow-x: hidden on the body is a band-aid
It's the fix AI tools reach for first:
html, body { overflow-x: hidden; } /* please don't */
The wobble goes away and the problem stays:
- It hides content. The button that stuck out 30px is now cut off 30px early, text and all. You swapped SPILLING for broken.
- It can break
position: sticky.overflow-x: hiddenturns an element into a scroll container (and quietly makesoverflow-ycompute toauto). A sticky element sticks within its nearest scroll container, so when the rule lands on a wrapperdiv, or on bothhtmlandbodyas above, sticky headers and sidebars stop sticking (MDN: position). - It hides the evidence. The next too-wide element gets silently chopped too.
When overflow-x: clip is fine
Sometimes the overflow is decoration: blurred blobs behind a hero, a rotated shape, a marquee, a slide-in animation. Nobody needs the bit past the edge, so clip that wrapper:
.hero { overflow-x: clip; }
clip cuts off the overflow like hidden does, but it doesn't create a scroll container, so sticky elements keep working (MDN: overflow). The rule: clip decoration, fix content.
Does your site fit?
Paste your address (or localhost:3000). doesitfit shows it on real screen sizes at once and tells you, bluntly, where it spills.
Free, no sign-up. Or ask Claude, ChatGPT or Codex to check it.
A prompt for your AI tool
If you build with Claude, ChatGPT, Cursor, Lovable, v0 or Bolt, give it numbers instead of "it's broken on mobile". Fill in the brackets:
My site scrolls sideways on phones. At [360]px wide the page is [48]px wider than the screen, and the element sticking out is [div.pricing-grid]. Find what makes it too wide and fix the cause: fixed widths, 100vw, images or iframes without max-width, long words or URLs, tables or code blocks, negative margins, off-screen menus or animations, flex items missing min-width: 0, grid columns needing minmax(0, 1fr), oversized headings. Don't hide it with overflow-x: hidden on html, body or a page wrapper; overflow-x: clip is only OK on purely decorative wrappers. Keep the desktop layout exactly as it is and tell me what you changed.
For a full check-fix-recheck workflow with more prompts, see make your AI website mobile friendly. For the mistakes AI tools make most often, see 10 responsive mistakes in AI-built websites.
The short version
- Sideways scrolling means one element is wider than the screen. Find it before you change anything.
- Test the narrow end: 360px (Galaxy S25 and other Galaxy phones), 375px (iPhone SE) and 402px (iPhone 17 Pro). More in /devices.
- Treat widths as maximums:
max-width: 100%andwidth: 100%, not100vw. - Flex and grid children need
min-width: 0orminmax(0, 1fr)before they'll shrink. overflow-x: hiddenon the page hides the problem;overflow-x: clipon a decorative wrapper is fine.- Page looks tiny rather than wobbly? Different bug: check the meta viewport tag.
Questions people ask
Why does my website scroll sideways on mobile?
Something on the page is wider than the screen, so the whole page becomes wider too. The usual suspects are fixed pixel widths, 100vw, images or embeds without max-width, long URLs and wide tables.
Should I put overflow-x: hidden on the body?
Only as a last resort. It hides the symptom by cutting off whatever sticks out, and when it ends up on a wrapper or on both html and body it can stop position: sticky from working. Fix the element that is too wide instead.
Why does width: 100vw cause a horizontal scrollbar?
100vw is the full viewport width including a classic scrollbar, so on desktops with visible scrollbars it is wider than the space the page actually has. Use width: 100% instead.
How do I find the element that causes horizontal scroll?
Set DevTools to a phone width and run a console snippet that lists every element whose right edge is past document.documentElement.clientWidth. Or open the page in doesitfit and press Show me where on a SPILLING screen.
What is the difference between overflow-x: hidden and overflow-x: clip?
Both cut off content that sticks out. hidden turns the element into a scroll container, which can break sticky children, while clip does not, so clip is the safer choice for decorative wrappers.