Your table sticks out of the text block — the Overfull \hbox every LaTeX user meets sooner or later. Here it is, measured. A twelve-column booktabs table in article comes to 404.83pt against a \textwidth of 345pt, and the log says Overfull \hbox (74.83432pt too wide). Most people reach for \small at this point, and most people find it is not enough: even \scriptsize (7pt) leaves the table at 351.90pt, still over. The reason is that shrinking the font does not shrink the space between columns. This page lays out every way to get that table onto the paper: declaring the width with tabularx and tabulary, rebuilding it with tabularray (the tblr environment), scaling it with \resizebox and adjustbox, turning the page sideways with pdflscape and rotating, and letting it spill into the margins with \makebox.
Why shrinking the font does not stop the overflow
Because \tabcolsep, the space between columns, is an absolute length that a change of font size does not touch. A twelve-column table gets \tabcolsep in twenty-four places — 6pt on each side of every column, including the two outer edges. Reset the same table with \tabcolsep=0pt and 404.83pt becomes 260.83pt: a difference of exactly 144pt, meaning 35% of the table was padding. Those 144pt do not shrink by a single point when the type gets smaller — measured at \scriptsize, the padding still totalled 144pt. That is why the table bottoms out at 351.90pt.
| Remedy | Measured table width (textwidth = 345pt) | Result |
|---|---|---|
(none) | 404.83pt | Overfull \hbox (74.83432pt too wide) |
\small | 385.27pt | still over |
\footnotesize | 365.70pt | still over |
\scriptsize | 351.90pt | still over, even at 7pt type |
\tabcolsep=3pt | 332.83pt | fits, and the type stays at 10pt |
\resizebox | 345.01pt | fits, but the type shrinks to an effective 8.52pt |
The table tells you the working order: trim the padding first, then rethink the column design, and only then scale or rotate. Setting \tabcolsep to 3pt alone brings the table to 332.83pt while keeping the same readable 10pt type as the body, so that is where to start (\tabcolsep and @{} are covered in depth on “Column spec, alignment and width”). But there is only so much padding to cut. If the culprit is not numbers but a column holding long prose, no amount of padding removal helps: the column itself has to wrap. That is the next section.
tabularx: fix the width first and let the X column stretch
tabularx takes the table’s target width as its first argument and makes the X columns fill whatever is left: \begin{tabularx}{width}{column spec}. An X is a p column that automatically absorbs the leftover width, and several of them share it equally. If your overflow comes from long descriptive text, this is the shortest route out — declare the width as \textwidth and by construction the table cannot overflow. It is by David Carlisle, the author of longtable, and ships in the tools bundle, so there is nothing to install.
The mechanism is a little unusual: tabularx finds the widths by typesetting the table several times. It first sets every X at the full target width — so the table is too wide — divides the excess by the number of X columns, narrows each one by that much, and resets, iterating until it converges. In the end X becomes whatever \tabularxcolumn defines, and the default is \newcommand{\tabularxcolumn}[1]{p{#1}} — a top-aligned paragraph column. Redefine it with m{#1} to centre cells vertically. An X cell is justified by default; for flush left, prefix the column with >{\raggedright\arraybackslash}X. The crucial bit is \arraybackslash, which undoes the way \raggedright and its relatives redefine \\. If you write the same prefix repeatedly, \newcolumntype gives it a one-letter alias and the spec becomes readable again.
\usepackage{tabularx}
\newcolumntype{Y}{>{\raggedright\arraybackslash}X}
% ...
\begin{tabularx}{\textwidth}{l Y Y}
\hline
Package & Strength & Weakness \\
\hline
tabular & simple and fast & you compute the widths yourself \\
tabularx & the table hits a fixed width & slower: the body is set several times \\
\hline
\end{tabularx}Two caveats. First, because the body is expanded several times, commands with side effects — stepping counters, writing files, \footnote, \verb — can misbehave inside tabularx. The standard fix is to wrap such a cell in a box or a minipage. Second, plain tabularx has no weighting: X X is always an even split, and for 2:1 you need either tabularray’s X[2] below or a hand-computed p{} through \newcolumntype.
tabulary: apportion the width by how much content each column holds
Where tabularx splits its X columns equally, tabulary hands out width in proportion to the natural width of each column’s content. When one column holds a short label and the other a long description, an even split looks awkward. The syntax matches: \begin{tabulary}{width}{column spec}, with four column types — L left, C centred, R right, J justified. Ordinary l, c, r and p{width} may be mixed in, but they are left out of the measurement and keep their natural width. Each measured column is weighted by the natural width of its widest cell and scaled so the table totals the requested width — though a column already narrower than its share is simply left alone. To keep the apportioning from going to extremes, a measured column is bounded by \tymin (default 10pt) and \tymax (default 2\textwidth) — the safety valve against one freakishly long entry monopolising a column.
\usepackage{tabulary}
% ...
\begin{tabulary}{\textwidth}{L L}
\hline
Term & Description \\
\hline
X column & Shares the width left over after the fixed columns, equally with the other X columns. \\
\hline
\end{tabulary}Both columns here are L, yet “Term”, whose content is short, comes out narrow and “Description”, full of prose, gets the wider share. With tabularx and X X they would be identical, so for a label-plus-text table tabulary looks more natural. But tabulary also processes the body more than once, so the same care over footnotes and counters applies.
tabularray: the tblr environment puts width and rules in one syntax
tabularray folds into a single key–value interface what used to take array, booktabs, multirow, colortbl and diagbox working together. It is written by Jianrui Lyu (lvjr) and throws away the old \halign machinery, parsing and typesetting tables directly with LaTeX3 (expl3). The copy in TeX Live 2024 is dated 2024-02-16, internal version 2024A. The main environment is tblr; load it with \usepackage{tabularray}.
The structure of tblr differs slightly from tabular: before the body comes one argument of comma-separated key–value options, the inner specification, as in \begin{tblr}{ … inner spec … }. Column alignment is declared with the colspec key. The familiar one-letter form colspec={lcr} still works, but the real power is in Q[…] (a generic column taking alignment, width and other options in brackets) and X[…] (an expanding column, as in tabularx). X[c] is a centred expanding column; X[2,l] is a left-aligned one with coefficient 2, twice the width of a plain X. What matters here is that width= and colspec= live inside the same braces, and that those coefficients let you set the ratio. Measured, a tblr with width=\textwidth and colspec={X[2,l] X[1,r] X[1,r]} produced a box of exactly 345.0pt — dead on the target.
The width the X columns expand into is given by the width key (\linewidth if unset). Inter-row and inter-column spacing are the rowsep and colsep keys, defaulting in tblr to rowsep=2pt and colsep=6pt. Rules are declarative: hlines / vlines draw every horizontal / vertical rule, and hline{n} / vline{n} style a specific line, as in hline{2}={1pt,solid} (Z is the special index for the last row). You never write \hline in the body. Settings you reuse can be hoisted out with \SetTblrInner{rowsep=2pt}.
Cell spanning is done in the body with \SetCell. \SetCell[c=2]{c} is a centred cell that spans 2 columns (the \multicolumn equivalent), and \SetCell[r=2]{c} spans 2 rows (the \multirow equivalent); c= and r= give the column and row span, and the brace argument the cell’s alignment. To style whole rows or columns, use \SetRow and \SetColumn — \SetRow{cyan8} shades a row pale cyan, using the package’s built-in colour names. The same things can be declared from the inner spec instead, as row{1}={font=\bfseries}, column{2}={halign=r}, cell{2}{3}={c=2}, which keeps the body clean.
\usepackage{tabularray}
% ...
\begin{tblr}{
width = \textwidth,
colspec = {Q[l] X[c] X[2,l]},
rowsep = 3pt,
hline{1,Z} = {1pt,solid},
hline{2} = {0.5pt,solid},
row{1} = {font=\bfseries},
}
Item & Kind & Description \\
\SetCell[r=2]{l} Table environment & fixed width & The table width is fixed; X columns share the rest. \\
& content width & Width is apportioned by how much each column holds. \\
Note & --- & Rules, colour and spans are all set through keys. \\
\end{tblr}This tblr fixes the table width to the text width with width=\textwidth. The three columns are Q[l] (a left basic column), X[c] (a centred expanding column) and X[2,l] (a left expanding column with coefficient 2), so the two X columns split the leftover width 1 : 2 and column 3, “Description”, ends up widest. No \hline appears in the body: the rules come only from the hline{…} keys in the inner spec. The first row is bold via row{1}={font=\bfseries}, and the column-1 cell “Table environment” spans two rows with \SetCell[r=2]{l}. Getting this far without pulling in booktabs, multirow and array is exactly the appeal of tabularray.
When the table is long as well as wide, two derived environments take over. longtblr is a tblr that spans pages, with repeating header rows, captions and footnotes; talltblr is a single-page table that carries notes underneath, in the spirit of threeparttable. In both, the caption, label, note{…} and the like go into the optional […] argument before the body, the outer specification. Repeating a header in longtblr is just rowhead=1 — a plain count of rows — which is terser than longtable’s four markers.
resizebox lies about your font size
graphicx’s \resizebox{\textwidth}{!}{...} scales its contents bodily to the width you name. It is the fastest way to get a table onto the paper, and it shrinks your type by a factor that appears nowhere in the source. Measured: feeding the 404.83pt table to \resizebox{\textwidth}{!} gives a box 345.01pt wide, and the height drops from 26.07pt to 22.22pt. Both ratios come to 0.8522. So type you believe to be 10pt is 8.52pt on the page. The document says \documentclass[10pt] and the paper says 8.52pt — an accident waiting to happen wherever a referee or a submission rule checks font size.
Why does \resizebox fit where \scriptsize (7pt type) does not? For the same reason as at the top of this page: \resizebox shrinks the inter-column padding along with everything else. Changing the font size cannot take a single point off those 144pt, but scaling by 0.8522 takes them down to 122.7pt. That is how an apparent 8.52pt under \resizebox beats a real 7pt under \scriptsize. Put the other way, \resizebox produces a table whose padding has been squeezed too — doubly penalised on legibility.
And \resizebox has a much more visible trap: hand it a small table and it enlarges it. Measured, a little table 4.44pt wide fed to \resizebox{\textwidth}{!} came out at 345.46pt — a monster stretched about seventy-eight times. Apply it reflexively to every table, the way you might with \includegraphics, and the narrow ones balloon. This is where adjustbox and its max width=\textwidth earn their keep: the same 4.44pt table stayed at 4.44pt, while only the 404.83pt one came down to 345.01pt. Shrink when necessary, never enlarge — that is the safer default for real documents.
\usepackage{graphicx} % \resizebox
\usepackage{adjustbox} % \adjustbox and the adjustbox environment
% ...
% scales in BOTH directions -- a narrow table gets blown up
\resizebox{\textwidth}{!}{\begin{tabular}{...}...\end{tabular}}
% only ever shrinks; a narrow table is left alone
\begin{adjustbox}{max width=\textwidth}
\begin{tabular}{...}...\end{tabular}
\end{adjustbox}Turning the page sideways: lscape, pdflscape and rotating compared
Only pdflscape makes the page itself turn in a PDF viewer. All three rotate the content ninety degrees, through \begin{landscape} or sidewaystable, but only pdflscape writes the instruction into the PDF that says “display this page rotated” — the /Rotate 90 entry. Here is the same document set three ways, checked with pdfinfo.
| Package | Environment | What pdfinfo reports for rot and page size |
|---|---|---|
lscape | landscape | rot: 0 on every page, still 612 x 792. Content only |
pdflscape | landscape | rot: 90 on that page only, 612 x 792. The viewer turns it |
rotating | sidewaystable | rot: 0, 612 x 792. Rotates as a numbered float |
Note that in all three the page size stays 612 x 792: nothing has been made physically landscape, only the content is turned. That settles the choice. For a PDF that will be read on screen, use pdflscape and spare your reader a tilted neck; pdflscape loads lscape and adds the /Rotate, so the lscape syntax carries over unchanged. For something printed and bound, lscape is enough — you turn the paper instead. If the table should stay a numbered, captioned float, use rotating’s sidewaystable (or sidewaysfigure for a figure). rotating also offers the figuresright and figuresleft options to pick which way things spin.
\usepackage{pdflscape} % loads lscape and adds /Rotate 90 to the page
\usepackage{rotating} % sidewaystable / sidewaysfigure
% ...
% a whole page turned sideways in the viewer
\begin{landscape}
\begin{tabular}{...}...\end{tabular}
\end{landscape}
% a rotated float that still says "Table N"
\begin{sidewaystable}
\centering
\begin{tabular}{...}...\end{tabular}
\caption{A very wide table}
\end{sidewaystable}Letting the table spill into the margins with makebox
The text block is 345pt wide, but the paper has more room. With article’s defaults the left margin measures 134.27pt and the right 135.03pt — 269pt of white standing empty beside the text. The idiom for reaching into it is \noindent\makebox[\textwidth]{...}. \makebox puts its contents in a box of width \textwidth, and when the contents are wider they overflow equally on both sides. For the 404.83pt table that is 29.92pt into each margin: the table widens while the optical centre of the page stays put.
One thing to accept: since the contents exceed the box, TeX still reports Overfull \hbox. This is the one time you may ignore that warning — the overflow is deliberate. To spill on one side only, \makebox[\textwidth][l]{...} pins the left edge and lets the table run right. For anything more controlled, the adjustwidth environment from the changepage package widens the text block for a stretch of the document, which is better behaved and keeps footnotes and running heads in agreement. Remember that the actual margin figures change with the paper and any geometry settings, so check the real PDF whenever you design something to reach into the margin (how the margins are set in the first place belongs to “Margins and page area”).
Splitting the table: cut it down the middle, or transpose it
There are answers that need no package at all. Cut the columns into two tables and repeat the key column in both: for a twelve-column table, put the region names in the first column of each half and split the years into 2–6 and 7–12. The reader’s eye never has to travel across a fold, and the type stays the same size as the body text. Nothing is scaled and nothing is rotated, so this often reads best of all. The other move is to transpose it. A table of twelve years by five regions runs wide; five regions by twelve years runs tall — and a tall table is longtable’s department.
Which one to reach for
The cause decides. If a column of prose is what overflows, use tabularx — tabularray’s X[2] when the columns need unequal weights, tabulary when you want the split to follow the content; declaring the width outright is the cleanest fix. If it is a forest of numeric columns, start with \tabcolsep and then ask whether the table needs all those columns at once. If every column really must appear on one sheet, use adjustbox’s max width rather than \resizebox. If it is too wide even for that, turn the page with pdflscape. For a table you are writing from scratch, reach for tabularray: width, rules, colour and spans all in one syntax. If the table is long as well as wide, move on to longtblr or longtable. Whichever route you take, look at the finished PDF and ask whether an 8.5pt digit is still readable.
| Package | How the width is set | Best for |
|---|---|---|
tabularx | Fixed total; X columns share equally | Equal-width prose columns fitted to the text width |
tabulary | Fixed total; apportioned by content | Uneven label-plus-text tables |
tabularray | the width key plus X columns, with coefficients | Modern tables with rules, colour and spans unified |
adjustbox | shrinks only on overflow (max width) | Putting a safe ceiling on an existing table |
pdflscape | leaves the width alone and turns the page 90 degrees | Tables that will never fit upright |
The fine detail of column specs themselves (\multicolumn, the array package’s >{…}, and so on) belongs to “Column spec, alignment and width”, the basics of merging cells to “Merging cells”, and very long page-spanning tables to the “longtable” page.