﻿---
title: HTML Table Overflow Issues
date: 2026-01-15
excerpt: A simple CSS technique to keep HTML tables within their containers by controlling table layout and text wrapping.
tags:
  - CSS
  - JavaScript
  - UIUX
updated: 2026-07-08 21:20:37
lang: en
i18n:
  cn: /html-tables-overflow
  translation: 2
---

**"Blog CSS Style Fixes"** Part Ⅱ, documenting some common blog styling problems and their solutions.

1. [[page_scroll|Fix Markdown Footnote Jump Overlap with CSS scroll-margin-top]]
2. [[html-tables-overflow|Fix HTML Tables from Becoming Too Wide]]

## Problem

On narrow screens such as mobile devices or sidebars, screen space is very limited.

By default, when a cell contains long unbreakable content (like a URL or a long snippet of code), the table ignores its parent container's width constraint and forces the container to expand, producing an unexpected horizontal scrollbar.

## CSS Tricks 🎪

In the examples below, the table sits inside a `.page` container that is only `150px` wide, and the table content is a long URL (about 170px wide), to show how different layouts behave. The applied CSS:

```css
.page {
  width: 150px;
  border: solid 2px var(--mauve);
  margin: 1em auto;
  table {
    font-size: 1em;
    width: 100%;
    th, td {
      padding: 1em;
    }
  }
}
```

<style>
.page {
  width: 150px;
  border: solid 2px var(--mauve);
  margin: 1em auto;
  color: var(--text);
  table {
    font-size: 1em;
    width: 100%;
    th, td {
        padding: 1em;
    }
  }
}
</style>

### Auto Layout & Word Break

First up is the default automatic layout, i.e. `{css} table { table-layout: auto }`. The browser adjusts column widths based on content[^1]. For this `2*1` table, the column width is determined by `https://gallery.vluv.space` in the second row, about `170px`, so the table simply overflows its parent container.

<div class="page">
    <table data-nowrap="true">
        <tr>
            <th>Gallery Link</th>
        </tr>
        <tr>
            <td>https://gallery.vluv.space</td>
        </tr>
    </table>
</div>

A simple workaround: set `word-break`[^2] to `break-all`. The URL, even though it is one long word, will then wrap across lines instead of stretching the table.

OK, change the table tag to `{html} <table style="word-break: break-all" data-nowrap="true">`, and it looks like this:

<div class="page">
    <table style="word-break: break-all" data-nowrap="true">
        <tr>
            <th>Gallery Link</th>
        </tr>
        <tr>
            <td>https://gallery.vluv.space</td>
        </tr>
    </table>
</div>

However, if we shrink the `.page` container further to `40px` and bump the font size up to `2em`, the table still overflows the container. This scenario is rare, though. One plausible example: viewing a table with many columns on a phone.

<div class="page" style="width: 40px; font-size: 2em">
    <table style="word-break: break-all" data-nowrap="true">
        <tr>
            <th>G</th>
        </tr>
    </table>
</div>

### Fixed Layout?

For tables overflowing their container, the classic StackOverflow answer is to set `{css} table-layout: fixed` on the table, together with `width: 100%` (the parent container's width).

With `table-layout: fixed`, the table strictly honors `width: 100%` (the parent container's width).

<div class="page" style="border-color: var(--mauve);">
    <table style="table-layout: fixed" data-nowrap="true">
        <tr>
            <th>Gallery Link</th>
        </tr>
        <tr>
            <td>https://gallery.vluv.space</td>
        </tr>
    </table>
</div>

But in my view this works about the same as `auto + word-break: all`. See the result below: a single character still cannot fit within `40px`, and the table still overflows the container.

<div class="page" style="width: 40px; font-size: 2em; border-color: var(--mauve);">
    <table style="table-layout: fixed; word-break: break-all" data-nowrap="true">
        <tr>
            <th>G</th>
        </tr>
    </table>
</div>

Also, if you don't specify column widths, `table-layout: fixed` makes all columns equally wide. To get a more balanced layout, you have to carefully assign widths to each column, which is inconvenient in practice.

> In the fixed table layout algorithm, the width of each column is determined in this priority:
>
> 1. A column element (`<col>`) with explicit width.
> 2. A cell in the **first row** with explicit width.
> 3. Otherwise, the shared remaining horizontal space.

<div style="width: 400px; max-width: 80%; border: 1px dashed var(--mauve); padding: 10px; margin: 1em auto;">
  <p style="margin: 0 0 5px; font-weight: bold;">1. table-layout: fixed</p>
  <table style="width: 100%; table-layout: fixed; border-collapse: collapse;">
    <tr>
      <td style="border: 1px solid var(--blue); padding: 4px;">1</td>
      <td style="border: 1px solid var(--blue); padding: 4px;">User</td>
      <td style="border: 1px solid var(--blue); padding: 4px;">This is a very long description text; in fixed mode it gets squeezed to the same width as the index column.</td>
    </tr>
  </table>

  <p style="margin: 15px 0 5px; font-weight: bold;">2. table-layout: auto</p>
  <table style="width: 100%; table-layout: auto; border-collapse: collapse;">
    <tr>
      <td style="border: 1px solid var(--green); padding: 4px;">1</td>
      <td style="border: 1px solid var(--green); padding: 4px;">User</td>
      <td style="border: 1px solid var(--green); padding: 4px;">This is a very long description text; in auto mode it automatically takes up most of the remaining space.</td>
    </tr>
  </table>
</div>

### JavaScript Solution

The default layout with `all-break` prevents overflow, but in some cases (showing code snippets, URLs) the forced line breaks look ugly.

As mentioned earlier, a table can stretch its container and trigger a horizontal scrollbar. While scrolling the table, everything else in the container (headings, buttons, text) scrolls left along with it, hurting readability.

The fix is fairly simple: wrap the table in its own container, and when the table exceeds that container's width, let the wrapper get the horizontal scrollbar instead.

```js
document.querySelectorAll(".content table").forEach((table) => {
  if (table.hasAttribute("data-nowrap") || table.parentElement.classList.contains('table-wrapper')) {
    return;
  }
  // if width exceeds container, wrap it
  const wrapper = document.createElement("div");
  Object.assign(wrapper.style, {
    width: "100%",
    overflowX: "auto",
  });
  table.parentNode.insertBefore(wrapper, table);
  wrapper.appendChild(table);
});
```

<div style="display: flex; justify-content: space-between; align-items: flex-start; width: 400px; max-width: 80%; margin: 1em auto;">
  <div style="flex: 1;">
    <div class="page" style="border-color: var(--yellow);">
        <table>
            <tr>
                <th>Gallery Link</th>
            </tr>
            <tr>
                <td>https://gallery.vluv.space</td>
            </tr>
        </table>
    </div>
  </div>
  <div style="flex: 1;">
    <div class="page" style="width: 40px; font-size: 2em; border-color: var(--yellow);">
    <table>
        <tr>
            <th>G</th>
        </tr>
    </table>
    </div>
  </div>
</div>

[^1]: The automatic table layout algorithm is used. The widths of the table and its cells are adjusted to fit the content. Most browsers use this algorithm by default.
[^2]: The word-break CSS property sets whether line breaks appear wherever the text would otherwise overflow its content box. [word-break - CSS | MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/word-break)
