A table wrapped in the table environment will never break across a page, however many rows it has — and when it overflows, nothing complains. On TeX Live 2024, wrapping a 119-row table in a float produced one page instead of three, with the remaining rows sliding quietly off the bottom of the sheet. LaTeX’s longtable package is what breaks that wall. But longtable’s real job is not page breaking. It is making the columns line up on every page — and to do that it writes its measurements into the .aux file and asks you for a second compile with the warning Table widths have changed. Rerun LaTeX. This page follows what that second run buys you, measured against supertabular, which skips it.
Why a table inside a float never breaks across pages
Because table, like figure, is a float, and LaTeX treats a float as one indivisible box that fits on a page. The box travels to the top or bottom of a page as a unit, and anything that travels as a unit cannot be cut in half. So however tall the inner tabular grows it is never split; if it does not fit, it simply runs off the paper. The nasty part is that this is not a warning. With the same 119 rows of data, the float-wrapped version came out as one page and the unwrapped version as three. Not a single error message. It is the kind of accident you only find by reading the PDF to the end.
A “table that spans pages”, then, is fundamentally incompatible with the float mechanism. What you need is a way to put the table in the same flow as the body text and hand it to TeX’s own page-breaking algorithm, the way a paragraph is handled. That is longtable. It was written by David Carlisle — the author of array, tabularx and dcolumn — and ships in the tools bundle that comes with every LaTeX installation, so there is nothing to install.
Writing a longtable: what changes from tabular
Almost everything is the same as tabular. Load \usepackage{longtable}, open with \begin{longtable}{l l r} — the column specification is a mandatory argument — separate columns with & and end rows with \\. Column types l, c, r, p{width} all work, as do \hline and \multicolumn. Only three things differ. First, never wrap it in a table environment: that turns it back into a float and brings back the one-page problem above. Second, an optional positioning argument [c], [r] or [l] may precede the column spec. Third, longtable carries its own caption (below). Page breaks occur only between rows or at an \hline, never partway through a single cell of a p column.
Position and surrounding space are set by lengths you can change any time after \begin{document}. On TeX Live 2024 the defaults measured as follows: \LTleft and \LTright are both 0pt plus 1fill — equal push from both sides, which is what centres the table. For a flush-left table, keep \setlength{\LTright}{0pt plus 1fill} and set \setlength{\LTleft}{0pt}. The space above and below is \LTpre and \LTpost, both defaulting to 12pt plus 4pt minus 4pt.
Repeating the head and foot on every page: endhead and endfoot
You declare four markers at the top of the table, each saying “repeat the rows up to here on such-and-such pages.” A marker goes at the end of its row in place of the \\. All four are optional; if the same header suits every page, \endhead alone will do.
| Marker | Where the preceding rows appear | Typical content |
|---|---|---|
\endfirsthead | head of the first page only | \caption{…} plus the column headings |
\endhead | head of every page except the first | “(continued)” plus the column headings |
\endfoot | foot of every page that continues (all but the last) | a “Continued on next page” row |
\endlastfoot | the very end of the table, last page only | the closing rule, or a note |
\kill | nowhere — the row is measured but discarded | a dummy row of the widest expected entries |
\documentclass{article}
\usepackage{longtable}
\begin{document}
\begin{longtable}{l l r}
% head of the first page only
\caption{Annual sales log}\\
\hline
Date & Item & Amount \\
\hline
\endfirsthead
% head of every later page
\multicolumn{3}{l}{\small (continued)}\\
\hline
Date & Item & Amount \\
\hline
\endhead
% foot of every page that continues
\hline
\multicolumn{3}{r}{\small Continued on next page}\\
\endfoot
% foot of the last page
\hline
\endlastfoot
% everything below the markers is real data
2026-01-05 & Apple & 380 \\
2026-01-06 & Orange & 120 \\
% ...rows continue; pages are broken automatically...
\end{longtable}
\end{document}Order matters. The four markers must appear in that order, at the very top of the table, and everything written below them is the real data that flows across the pages. Put the other way round: the table’s content begins with the first row after \endlastfoot. Break the order and a row you meant as a heading turns up as data row one. \kill is the odd one out: it makes a row that is measured but never printed. When the headings are short but one very long value turns up on page 100, a \kill row of maximum-width samples at the top makes the columns wide from the start.
Table widths have changed. Rerun LaTeX. — why two compiles
To make the columns line up on every page. longtable sets the table page by page, but the widest cell on a page is only known once that page has been set. Set each page independently and each page gets its own column widths. So longtable writes the widths it measured into the .aux file and reads them back on the next run as one common width for every page. Which is why the first compile prints this:
Package longtable Warning: Column widths have changed
(longtable) in table 1 on input line 24.
Package longtable Warning: Table widths have changed. Rerun LaTeX.Here is what ignoring the warning costs, measured. Reading the position of the right-hand column heading “Amount” out of the PDF of a three-page table: on the first run it sits at x = 320.41pt while the data below it sits at x = 488.69pt — heading and numbers standing in different places. After a second run the heading moves to x = 471.53pt and is identical on all three pages. So the first-run output is a table whose columns are too narrow and whose headings have drifted left. That is exactly what “I compiled once and the table looks wrong” means.
You can look inside that .aux file. Here is what a three-column longtable leaves behind. \LT@i means “the first longtable in this document”; the second one becomes \LT@ii. The second argument of each entry is the measured width of one column, and the next run simply uses those numbers.
\gdef \LT@i {\LT@entry
{2}{58.6668pt}\LT@entry
{2}{274.36153pt}\LT@entry
{1}{47.55566pt}}The measuring is not done in one gulp but in chunks, sized by the counter LTchunksize (200 rows by default). If a very long table exhausts TeX’s memory, make it smaller with \setcounter{LTchunksize}{10}; making it larger reduces the number of .aux round trips and can converge faster. In practice, using latexmk means you never have to think about the round trip — it reruns until the warning stops.
Captions and numbering: LTcapwidth defaults to 4 inches
longtable is not a float, yet it has a \caption{…} of its own. It steps the standard table counter, so the caption is numbered “Table N” automatically and appears in \listoftables. Because the caption is part of the table’s layout, the rule is to put it in the head block, before \endfirsthead, closing the row with \\, as \caption{…}\\. One surprise lurks in its width: the caption width \LTcapwidth defaults to 4 inches, i.e. 289.08pt. Since \textwidth in article is 345pt, out of the box the caption is 56pt narrower than the body text. To match the text block, write \setlength{\LTcapwidth}{\textwidth}.
The rest are small conventions. To drop the number, use \caption*{…} — no number and no entry in the list of tables. A subsidiary caption such as “(continued)” on later pages should be written with an empty optional argument, \caption[]{…}, so it is not listed twice. And a cross-reference \label{…} must never go inside the repeating \endhead: the same label would be defined once per page. Put it inside \endfirsthead, or on the first row of the table body.
What happens to booktabs rules at a page break
\toprule, \midrule and \bottomrule work inside a longtable as they are: booktabs added longtable compatibility in its 2000 release, and booktabs.sty still carries an \ifx\longtable\undefined branch for it. One asymmetry remains, though. longtable doubles an \hline automatically at a break, drawing one rule above and one below the seam, whereas booktabs rules are not doubled. So if a rule happens to land exactly on a page break, the booktabs manual’s own advice is that you fix it by hand — usually by deleting the offending rule. The practical answer is to design the break away: put the rules in \endfoot and \endhead so that no rule ever sits on the seam.
There is a bonus. The left/right trimming argument that normally belongs to \cmidrule alone also works on \toprule, \midrule and \bottomrule — but only inside a longtable. Write \toprule(r) and the right end pulls in; \midrule(lr) pulls in at both ends. It compiles cleanly on TeX Live 2024 with longtable and booktabs loaded together. The flip side is that you cannot write the same thing in a plain tabular; booktabs’ author remarks, half in jest, that if you must have trimmed rules you should simply make all your tables longtables.
supertabular and xtab: one run, but the columns jump
longtable is not the only way to break a table across pages. supertabular (Johannes Braams and Theo Jurriens) and its descendant xtab (Peter Wilson) take a completely different tack. Where longtable sets the whole table and hands it to TeX’s page breaker, these two measure how much room is left on the page and emit a fresh tabular that fits — laying down one independent tabular per page. So they need no .aux round trip and finish in a single compile.
The price shows up directly on the page. With the same 119 rows, and one extremely long entry planted in row 90, the two behave very differently. Under supertabular the column heading “Amount” sits at x = 249.16pt on pages 1 and 2, then at x = 471.53pt on page 3, the page carrying the long entry — turn the page and the table changes width. The same data under longtable, after its second compile, put the heading at x = 471.53pt on all three pages. Compiling twice is how you buy that consistency. The syntax differs too: supertabular declares head and foot outside the environment as \tablefirsthead, \tablehead, \tabletail and \tablelasttail, with \topcaption and \bottomcaption for captions. xtab builds on that, adding things like a minimum number of rows to keep a break from looking ragged.
Wrapping X columns that also break across pages: xltabular and ltxtable
longtable uses the same column spec as tabular, so long prose ends up in a fixed-width p{width} column. For a column that expands automatically into the remaining width you want tabularx’s X column — but the tabularx environment itself cannot break across pages. Two bridges close the gap. xltabular (Rolf Niepraschk and Herbert Voß) is a longtable that has X columns: write \begin{xltabular}{\linewidth}{l X r}, giving it a total width and a column spec, and the \endfirsthead family of markers still works. It loads ltablex internally but leaves the existing tabularx environment untouched.
\documentclass{article}
\usepackage{xltabular}
\begin{document}
\begin{xltabular}{\linewidth}{l X r}
\caption{Glossary}\\
\hline
Term & Description & Page \\
\hline
\endfirsthead
\hline
Term & Description & Page \\
\hline
\endhead
longtable & Breaks across pages; sits in the text flow, not in a float. & 12 \\
xltabular & A longtable whose X column takes the leftover width. & 34 \\
\end{xltabular}
\end{document}The other bridge is ltxtable, again by David Carlisle. Here you put the table body in a separate file and pull it in with \LTXtable{\textwidth}{filename}. The file holds a longtable with X columns; ltxtable runs the tabularx width computation once and then hands the result to longtable. Splitting the file is a nuisance, but it keeps tabularx’s repeated expansion from colliding with longtable’s .aux round trip, which makes it steadier on long tables. The choice is simple: xltabular for convenience, ltxtable when a big, complicated table starts behaving oddly. One small detail: xltabular does not consume a table number unless you actually write a \caption.