---
title: Fix content wider than the viewport
description: >-
  Eliminate horizontal scrolling by ensuring all content fits within the device
  viewport width. Overflow hides content and makes a storefront hard to use on a
  phone.
source_url:
  html: >-
    https://shopify.dev/docs/storefronts/themes/best-practices/performance/fix-content-wider-than-mobile-screen
  md: >-
    https://shopify.dev/docs/storefronts/themes/best-practices/performance/fix-content-wider-than-mobile-screen.md
api_name: liquid
---

# Fix content wider than the viewport

Content that exceeds the viewport width forces horizontal scrolling and creates a poor mobile experience. Making sure all content fits within the viewport keeps every part of the page reachable on a phone.

***

## Why

Horizontal scrolling on mobile devices creates usability problems. Customers expect to scroll vertically, and horizontal content overflow breaks this expectation.

Content overflow causes specific issues:

* **Hidden content**: customers must scroll horizontally to see content that's off-screen.
* **Cramped layouts**: content that overflows in portrait orientation can reflow unpredictably when the device rotates.
* **Accessibility barriers**: screen readers struggle with overflowing content.
* **Broken interactions**: buttons and links pushed off-screen can be hard or impossible to reach.

Common causes of content overflow:

* Fixed-width elements, for example `width: 1200px`.
* Large images without `max-width` constraints.
* Tables with many columns.
* Pre-formatted text blocks.
* Absolute positioning with fixed coordinates.
* CSS transforms that extend beyond the viewport.

Overflow is easy to miss on a desktop browser at full width, so check every layout at mobile viewport sizes with Chrome DevTools device emulation.

***

## How

### Set the proper viewport meta tag

Essential viewport configuration:

```html
<!-- Prevents content from overflowing the viewport -->
<meta name="viewport" content="width=device-width, initial-scale=1.0">
```

Without this tag, mobile browsers assume desktop width, typically 980 px, which causes all content to shrink and potentially overflow.

### Use relative widths instead of fixed

Replace fixed widths with percentages or `max-width`:

```css
/* Anti-pattern: fixed width */
.container {
  width: 1200px;
}


/* Recommended: fluid width with max-width */
.container {
  box-sizing: border-box;
  width: 100%;
  max-width: 1200px;
  padding: 0 20px;
  margin: 0 auto;
}


/* Anti-pattern: fixed element */
.sidebar {
  width: 300px;
}


/* Recommended: flexible element */
.sidebar {
  width: 100%;
  max-width: 300px;
}


@media (min-width: 768px) {
  .sidebar {
    width: 30%;
  }
}
```

### Constrain images and media

Prevent images from overflowing:

```css
/* Global image constraint */
img {
  max-width: 100%;
  height: auto;
  display: block;
}


/* Video and iframe constraint */
video,
iframe {
  max-width: 100%;
  height: auto;
}


/* Responsive iframe wrapper */
.video-wrapper {
  position: relative;
  padding-bottom: 56.25%; /* 16:9 aspect ratio */
  height: 0;
  overflow: hidden;
}


.video-wrapper iframe {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
}
```

Shopify image sizing:

```liquid
{%- comment -%}
  Use Shopify image filters to serve appropriately sized images.
{%- endcomment -%}


<img
  src="{{ product.featured_image | image_url: width: 800 }}"
  srcset="
    {{ product.featured_image | image_url: width: 400 }} 400w,
    {{ product.featured_image | image_url: width: 800 }} 800w,
    {{ product.featured_image | image_url: width: 1200 }} 1200w
  "
  sizes="(max-width: 768px) 100vw, 50vw"
  width="{{ product.featured_image.width }}"
  height="{{ product.featured_image.height }}"
  alt="{{ product.title | escape }}"
  style="max-width: 100%; height: auto;"
>
```

### Handle tables responsively

Horizontal scroll for tables:

```css
/* Wrapper with horizontal scroll */
.table-wrapper {
  width: 100%;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  margin-bottom: 20px;
}


.table-wrapper table {
  min-width: 600px; /* Minimum table width */
  width: 100%;
}


/* Visual indicator for scrollable content */
.table-wrapper::after {
  content: '← Scroll →';
  display: block;
  text-align: center;
  font-size: 12px;
  color: #666;
  padding: 8px;
}


@media (min-width: 768px) {
  .table-wrapper::after {
    display: none;
  }
}
```

Responsive table pattern, with a card layout on mobile:

```liquid
{%- comment -%}
  Convert the table to a card layout on mobile.
{%- endcomment -%}


<div class="responsive-table">
  <table>
    <thead>
      <tr>
        <th>Product</th>
        <th>Price</th>
        <th>Quantity</th>
        <th>Total</th>
      </tr>
    </thead>
    <tbody>
      {% for item in cart.items %}
        <tr>
          <td data-label="Product">{{ item.title }}</td>
          <td data-label="Price">{{ item.price | money }}</td>
          <td data-label="Quantity">{{ item.quantity }}</td>
          <td data-label="Total">{{ item.line_price | money }}</td>
        </tr>
      {% endfor %}
    </tbody>
  </table>
</div>


<style>
  /* Mobile: card layout */
  @media (max-width: 767px) {
    .responsive-table table,
    .responsive-table thead,
    .responsive-table tbody,
    .responsive-table tr,
    .responsive-table th,
    .responsive-table td {
      display: block;
    }


    .responsive-table thead {
      display: none;
    }


    .responsive-table tr {
      margin-bottom: 20px;
      border: 1px solid #ddd;
      border-radius: 4px;
      padding: 12px;
    }


    .responsive-table td {
      text-align: right;
      padding: 8px;
      border-bottom: 1px solid #eee;
    }


    .responsive-table td:last-child {
      border-bottom: none;
    }


    .responsive-table td::before {
      content: attr(data-label);
      float: left;
      font-weight: 600;
    }
  }
</style>
```

### Fix pre-formatted text

Wrap code blocks and `pre` elements:

```css
/* Allow code to wrap */
pre,
code {
  max-width: 100%;
  overflow-x: auto;
  white-space: pre-wrap;
  word-wrap: break-word;
  font-family: monospace;
}


/* Scrollable code blocks */
pre {
  padding: 16px;
  background: #f5f5f5;
  border-radius: 4px;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}


pre code {
  white-space: pre; /* Preserve formatting in scrollable container */
}
```

### Handle long words and URLs

Break long strings:

```css
/* Break long words */
.content {
  word-wrap: break-word;
  overflow-wrap: break-word;
  hyphens: auto;
}


/* Break URLs and long strings */
.url,
.email,
.long-text {
  word-break: break-all;
  overflow-wrap: break-word;
}


/* Product titles and descriptions */
.product-title,
.product-description {
  overflow-wrap: break-word;
  word-wrap: break-word;
  hyphens: auto;
}
```

### Use CSS Grid and Flexbox safely

Prevent grid and flex items from overflowing:

```css
/* Grid with proper constraints */
.grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
  gap: 20px;
  width: 100%;
}


.grid-item {
  min-width: 0; /* Prevents overflow */
  overflow: hidden;
}


/* Flex with wrapping */
.flex-container {
  display: flex;
  flex-wrap: wrap;
  gap: 16px;
  width: 100%;
}


.flex-item {
  flex: 1 1 300px;
  min-width: 0; /* Prevents overflow */
  max-width: 100%;
}
```

***

## Examples

### Fixing an oversized product image

Before:

```liquid
<!-- Image overflows on mobile -->
<img src="{{ product.featured_image | image_url: width: 1200 }}" alt="{{ product.title | escape }}">
```

After:

```liquid
<!-- Image scales to fit viewport -->
<img
  src="{{ product.featured_image | image_url: width: 1200 }}"
  srcset="
    {{ product.featured_image | image_url: width: 400 }} 400w,
    {{ product.featured_image | image_url: width: 800 }} 800w,
    {{ product.featured_image | image_url: width: 1200 }} 1200w
  "
  sizes="(max-width: 768px) 100vw, 50vw"
  width="{{ product.featured_image.width }}"
  height="{{ product.featured_image.height }}"
  alt="{{ product.title | escape }}"
  style="max-width: 100%; height: auto; display: block;"
>
```

### Responsive hero section with text

```liquid
<section class="hero">
  <picture>
    <source
      media="(max-width: 767px)"
      srcset="{{ section.settings.mobile_image | image_url: width: 800 }}"
    >
    <source
      media="(min-width: 768px)"
      srcset="{{ section.settings.desktop_image | image_url: width: 2000 }}"
    >
    <img
      src="{{ section.settings.desktop_image | image_url: width: 2000 }}"
      width="{{ section.settings.desktop_image.width }}"
      height="{{ section.settings.desktop_image.height }}"
      alt="{{ section.settings.heading | escape }}"
      class="hero__image"
    >
  </picture>


  <div class="hero__content">
    <h1>{{ section.settings.heading }}</h1>
    <p>{{ section.settings.subheading }}</p>
    <a href="{{ section.settings.button_link }}" class="button">
      {{ section.settings.button_text }}
    </a>
  </div>
</section>


<style>
  .hero {
    position: relative;
    width: 100%;
    overflow: hidden; /* Prevents content overflow */
  }


  .hero__image {
    width: 100%;
    max-width: 100%;
    height: auto;
    display: block;
  }


  .hero__content {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    text-align: center;
    color: white;
    width: 90%; /* Ensures content fits viewport */
    max-width: 600px;
    padding: 20px;
  }


  .hero h1 {
    font-size: clamp(24px, 6vw, 48px);
    margin-bottom: 16px;
    word-wrap: break-word;
  }


  .hero p {
    font-size: clamp(16px, 3vw, 20px);
    margin-bottom: 24px;
    word-wrap: break-word;
  }


  .button {
    display: inline-block;
    padding: 12px 32px;
    font-size: 16px;
    white-space: nowrap;
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
  }
</style>
```

***

## Testing

1. **Mobile viewport testing**: use Chrome DevTools Device Mode. Test at 320 px, 375 px, and 414 px widths. Verify that there's no horizontal scrolling, and check that all content is visible.

2. **Overflow detection**: in the DevTools console, run `Array.from(document.querySelectorAll('*')).filter((element) => element.scrollWidth > document.documentElement.clientWidth)` to list the elements that overflow, or scroll the page horizontally and inspect what moves.

3. **Real device testing**: test on actual mobile devices. Try different screen sizes and orientations, and verify that there's no horizontal scroll on any page.

4. **Lighthouse audit**: run a mobile Lighthouse audit and verify that there are no viewport meta tag issues.

5. **Scroll testing**: scroll through the entire page vertically. Verify that no horizontal scroll appears, and check that all interactive elements are accessible.

***

## References

* [MDN: viewport meta tag](https://developer.mozilla.org/en-US/docs/Web/HTML/Viewport_meta_tag)
* [MDN: responsive images](https://developer.mozilla.org/en-US/docs/Web/HTML/Responsive_images)
* [Chrome DevTools: simulate mobile devices with device mode](https://developer.chrome.com/docs/devtools/device-mode)
* [Build responsive layouts that perform well](https://shopify.dev/docs/storefronts/themes/best-practices/performance/implement-responsive-design)

***
