### What’s really happening under the hood
1. **The browser’s “auto” table-layout algorithm**
By default (`table-layout: auto`) every column’s width is negotiated between:
* **min-content width** – the narrowest the column can get if the text wraps at every possible break.
* **max-content width** – the width of the longest unbreakable piece (a long URL, a `superLongVariableName`, etc.).
The browser tries to fit the table inside the available block, but if a scroll container (`overflow-x:auto`) says *“don’t worry, the table can spill out horizontally”*, the algorithm happily lets the table grow until it hits something closer to the max-content widths. That’s why you don’t end up with 2-word columns but you also don’t get a single **100-word-wide** column: the browser lands somewhere between the two extremes.
2. **OpenAI’s tweak**
ChatGPT wraps each `<table>` in a div that scrolls **and** sets a *reasonable cap* on every cell:
```css
td, th {
max-width: 40ch; /* ~40 characters before wrapping */
white-space: normal; /* allow wrapping */
overflow-wrap: anywhere; /* break long URLs/words */
}
```
*Short cells stay short, long paragraphs wrap after \~40 characters, and horizontal scroll still kicks in once the total table width exceeds the viewport.*
---
### Bring that behaviour to your Next.js / Docusaurus site
#### 1. Global CSS you can drop in today
```css
/* 1️⃣ Make tables scroll when needed */
table {
display: block;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
}
/* 2️⃣ Let rows grow naturally, but stop cells from getting ridiculous */
table td,
table th {
max-width: 40ch; /* tweak to taste: 35–50ch feels good */
white-space: normal; /* wrap as needed */
overflow-wrap: anywhere;
/* optional polish */
padding: 0.4rem 0.8rem;
vertical-align: top;
}
```
*Result:* long prose wraps after \~40 characters, URLs break when they have to, and the whole table scrolls horizontally once its total width tops the viewport.
#### 2. A reusable MDX component (the nice-to-maintain way)
```tsx
// components/ScrollableTable.tsx
export default function ScrollableTable(
props: React.TableHTMLAttributes<HTMLTableElement>
) {
return (
<div className="overflow-x-auto my-4 -mx-4">
<table {...props} className="min-w-full border-collapse prose-table" />
</div>
);
}
```
Then shadow/override the `<table>` element just once:
* **Next.js (with `@mdx-js/react` or `next-mdx-remote`)**
```tsx
import { MDXProvider } from '@mdx-js/react';
import ScrollableTable from '@/components/ScrollableTable';
const components = { table: ScrollableTable };
export default function MyApp({ Component, pageProps }) {
return (
<MDXProvider components={components}>
<Component {...pageProps} />
</MDXProvider>
);
}
```
* **Docusaurus v2**
```jsx
// src/theme/MDXComponents.js
import React from 'react';
import MDXComponents from '@theme-original/MDXComponents';
import ScrollableTable from '@site/src/components/ScrollableTable';
export default {
...MDXComponents,
table: ScrollableTable,
};
```
Add the same CSS from the previous step (or Tailwind equivalents like `max-w-[40ch]` and `break-words`) and every markdown table now behaves like ChatGPT’s—no matter who writes the docs.
---
### Fine-tuning knobs you can play with
| What you want | Tweak |
| --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| **Narrower / wider wrap** | Change `max-width: 35ch` … `50ch` or use `em` units. |
| **Keep header sticky** | In the scroll wrapper add `position: relative`; then:<br>`thead th { position: sticky; top: 0; background: var(--ifm-background-color); }` |
| **Don’t wrap code blocks inside cells** | `td code { white-space: nowrap; }` |
| **A11y for keyboard users** | Give the wrapper `role="region"` and an `aria-labelledby` linking to a `<caption>` or heading so screen-reader users know they can scroll. |
---
**TL;DR**
Put your table inside a horizontal-scroll wrapper **and** cap each cell’s width (`max-width: 40ch; overflow-wrap:anywhere;`). That yields the same “sensible middle ground” ChatGPT uses—easy to scan, impossible to break your layout.