Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Most jsPDF table errors come from treating four separate controls as one: the table’s total width, each column’s width, text overflow, and pagination. Fix them in that order. Measure the page width left after margins, set tableWidth and columnStyles deliberately, choose an overflow policy, then configure vertical or horizontal page breaks. Finally, confirm that your installed jspdf-autotable version supports the option names and hook API in your code.

Start with a version-safe invocation

Record the installed versions before changing layout. Major releases have changed option names and invocation styles, so an example copied from an old README may be silently ignored.

npm ls jspdf jspdf-autotable

Use the current function-style API when your installed release documents it:

import { jsPDF } from 'jspdf';
import { autoTable } from 'jspdf-autotable';

const doc = new jsPDF({ unit: 'mm', format: 'a4' });
autoTable(doc, {
  head: [['Product', 'Quantity', 'Notes']],
  body: [
    ['Keyboard', '2', 'Wireless, low profile'],
    ['Monitor', '1', '27-inch display']
  ]
});
doc.save('table.pdf');

If your package uses a plugin-installation pattern instead, follow that release’s documented installation method rather than mixing APIs. A table that renders with default settings is not proof that every option is supported.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
#1 Best Overall
Sale
Adobe Acrobat 6 PDF For Dummies
  • Used Book in Good Condition

Measure the usable page width

Width calculations start with the page’s physical width minus the left and right margins. Set margins explicitly when the default is not part of your design:

const margins = { top: 18, right: 18, bottom: 18, left: 18 };

autoTable(doc, {
  margin: margins,
  tableWidth: 'auto',
  head: [['ID', 'Description', 'Status']],
  body: rows
});

tableWidth: 'auto' targets the available page width. Use 'wrap' when the table should be only as wide as its content, or provide a numeric width when deterministic geometry is required. Remember that tableWidth controls the whole table; it does not force individual columns to fit.

Control individual columns with columnStyles

Set a policy for every column that is likely to cause drift. A column can use cellWidth: 'auto', 'wrap', or a numeric width. Numeric widths are useful when a report must line up with another page; 'wrap' gives long text room by increasing the column and can make a table exceed the page.

autoTable(doc, {
  margin: { left: 15, right: 15, top: 20, bottom: 20 },
  tableWidth: 'auto',
  columnStyles: {
    0: { cellWidth: 18 },       // identifier
    1: { cellWidth: 'wrap' },   // descriptive text
    2: { cellWidth: 28 },       // status
    3: { cellWidth: 'auto' }
  },
  head: [['ID', 'Description', 'Status', 'Owner']],
  body: rows
});

If fixed widths add up to more than the usable page width, text cannot be made accurate without changing the geometry. Reduce widths, margins, font size, or padding, or use horizontal pagination. Do not expect overflow to solve a table that is physically wider than the page.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Choose an explicit text-overflow policy

Width and overflow are separate decisions. overflow describes what happens after a cell’s text does not fit:

  • linebreak preserves the text by wrapping it and increasing row height.
  • ellipsize truncates the displayed value with an ellipsis.
  • visible allows text to spill outside the cell.
  • hidden clips text at the cell boundary.
autoTable(doc, {
  styles: {
    overflow: 'linebreak',
    fontSize: 9,
    cellPadding: 2
  },
  columnStyles: {
    0: { cellWidth: 20, overflow: 'hidden' },
    1: { cellWidth: 70, overflow: 'linebreak' },
    2: { cellWidth: 35, overflow: 'ellipsize' }
  },
  head: [['Code', 'Description', 'Comment']],
  body: rows
});

Inspect long headers as well as body cells. A header that does not wrap can make a column appear wider even when ordinary rows fit.

Keep vertical pagination predictable

Position the table after preceding content with startY, and make the page-break policy explicit. The pageBreak option defines what happens when a table spans more than one page.

doc.text('Monthly inventory', 15, 18);

autoTable(doc, {
  startY: 25,
  pageBreak: 'auto',       // normal flow
  rowPageBreak: 'avoid',   // keep a row together when possible
  showHead: 'everyPage',   // repeat the header
  margin: { top: 15, right: 15, bottom: 15, left: 15 },
  head: [['SKU', 'Item', 'Units']],
  body: rows
});
  • pageBreak: 'auto' lets the table flow normally.
  • pageBreak: 'avoid' moves the table when the whole table can fit on a later page.
  • pageBreak: 'always' starts the table on a new page.
  • rowPageBreak: 'avoid' prevents rows from splitting unless a row is taller than a page.
  • showHead: 'everyPage' repeats the header on each page. Older documentation may call this option showHeader; use the name supported by your installed release.

Set startY from the actual end position of the preceding content, not from a hard-coded value that becomes invalid when a title wraps.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Use horizontal pagination for genuinely wide tables

When the required column widths exceed the page, shrinking text can destroy readability. Enable horizontal splitting instead:

autoTable(doc, {
  horizontalPageBreak: true,
  horizontalPageBreakRepeat: [0],
  horizontalPageBreakBehaviour: 'afterAllRows',
  margin: { left: 14, right: 14, top: 16, bottom: 16 },
  columnStyles: {
    0: { cellWidth: 18 },
    1: { cellWidth: 55 },
    2: { cellWidth: 45 },
    3: { cellWidth: 45 },
    4: { cellWidth: 45 }
  },
  head: [['ID', 'Name', 'Region', 'Owner', 'Notes']],
  body: rows
});

horizontalPageBreakRepeat repeats identifier columns, such as an ID, on each horizontal segment. Choose the documented behavior, 'immediately' or 'afterAllRows', according to whether you want each horizontal segment completed before moving through the rows. Verify the exact spelling supported by your installed version.

Apply hooks at the correct rendering stage

AutoTable can overwrite styles after a hook runs. Put each change at the stage where it belongs:

  • didParseCell: normalize content or styles while cell data is being parsed.
  • willDrawCell: call native jsPDF drawing methods immediately before a cell is drawn.
  • didDrawCell: add images or shapes after the cell has been drawn.
autoTable(doc, {
  head: [['Status', 'Value']],
  body: rows,
  didParseCell(data) {
    if (data.section === 'body' && data.column.index === 0) {
      data.cell.text = String(data.cell.text).trim();
    }
  },
  willDrawCell(data) {
    if (data.section === 'body' && data.column.index === 0 && data.cell.text[0] === 'Error') {
      doc.setTextColor(180, 0, 0);
    }
  },
  didDrawCell(data) {
    if (data.section === 'body' && data.column.index === 1) {
      // Draw an image or extra shape using data.cell coordinates here.
    }
  }
});

Make HTML input deterministic

With html input, verify the selector, hidden rows and columns, and the CSS assumptions that determine the resulting cell text. Browser table appearance is not a reliable prediction of PDF geometry. For difficult layouts, pass explicit head, body, and columns data so widths and ordering are under your control.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
autoTable(doc, {
  html: '#orders',
  includeHiddenHtml: false,
  styles: { overflow: 'linebreak' },
  tableWidth: 'auto'
});

If a selector returns no table, or a hidden cell contributes unexpected text, switch temporarily to explicit arrays and compare the generated PDF.

A complete correction pattern

import { jsPDF } from 'jspdf';
import { autoTable } from 'jspdf-autotable';

const doc = new jsPDF({ unit: 'mm', format: 'a4' });
const rows = [
  ['A-100', 'A long description that must remain readable across lines', 'Open'],
  ['B-200', 'Short description', 'Closed']
];

autoTable(doc, {
  startY: 28,
  margin: { top: 18, right: 16, bottom: 18, left: 16 },
  tableWidth: 'auto',
  styles: { fontSize: 9, cellPadding: 2, overflow: 'linebreak' },
  headStyles: { fontSize: 9 },
  columnStyles: {
    0: { cellWidth: 25 },
    1: { cellWidth: 95, overflow: 'linebreak' },
    2: { cellWidth: 35, overflow: 'ellipsize' }
  },
  pageBreak: 'auto',
  rowPageBreak: 'avoid',
  showHead: 'everyPage',
  head: [['Code', 'Description', 'Status']],
  body: rows
});

doc.save('corrected-table.pdf');

Render and inspect the first page, a page containing a split, the last page, the widest column, and rows containing long unbroken strings. Compare the PDF itself, not just the source HTML.

Common symptoms and fixes

Symptom Likely cause Fix
Columns run off the page Fixed widths exceed usable width, or wrapping expanded a content-width table. Measure margins; use tableWidth: 'auto', numeric widths that fit, smaller padding/font, or horizontal pagination.
Text is cut off hidden or a narrow fixed cell. Use linebreak for preservation, widen the cell, or choose ellipsize when truncation is acceptable.
Text spills into another column visible overflow. Switch to linebreak, ellipsize, or hidden deliberately.
Rows split awkwardly Default row pagination or an incorrect startY. Set rowPageBreak: 'avoid' and calculate startY after preceding content.
Header appears only once Header repetition is disabled or the legacy option name was copied. Use showHead: 'everyPage' in current releases; verify whether an older release expects showHeader.
A hook change disappears The hook runs before AutoTable applies its drawing styles. Move normalization to didParseCell, native styling to willDrawCell, and additions to didDrawCell.
HTML table differs from the PDF Selector, hidden content, or CSS parsing changed the cell data. Check the selector and parsed text, then use explicit head, body, and columns.
Option has no effect Version/API mismatch. Run npm ls jspdf jspdf-autotable and consult the documentation for that installed release.

Performance, reliability, and cost considerations

  • Wrapping long text increases row height and can create many pages; ellipsizing reduces output size but changes the displayed content.
  • Explicit arrays avoid the selector and CSS variability of HTML parsing and make repeated renders easier to compare.
  • Horizontal pagination preserves readable column widths but requires readers to move across segments; repeat a stable identifier column so segments remain understandable.
  • Always test a normal row, a very tall row, a long unbroken token, and the widest set of columns. A layout that works with short sample data can fail when production text expands.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Or skip the browser setup

If your goal is to capture a rendered page or PDF rather than generate a jsPDF table in the browser, ScreenshotNeo provides a single API request. It accepts cookie and consent banners like a visitor, then removes more than 60 known consent platforms, newsletter popups and chat widgets before capture; each step can be disabled. Bot checks, CAPTCHAs, blank pages, timeouts, failed loads and cache hits are not billed, and response headers identify the page verdict and billing status. Its MCP server exposes take_screenshot, get_page_info, and capture_pdf to Claude, Cursor, and other MCP clients.

Install the setup described in the ScreenshotNeo documentation, then make a call:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

ScreenshotNeo also supports full-page lazy-image capture, CSS-selector element capture, device presets or custom viewports, retina scale, PDF paper and page-range controls, custom CSS and JavaScript, clicks, selector or network-idle waits, request blocking, headers, cookies, user agents, authorization, timezone, geolocation, transparent backgrounds, resizing, chosen cache TTLs, signed links, asynchronous webhooks, bulk capture of 100 URLs per call, usage data, an OpenAPI specification, and familiar parameter names for easier migration. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000. Create a free ScreenshotNeo account.

Final verification checklist

  • Installed jspdf and jspdf-autotable versions are recorded.
  • Left and right margins are included in the width calculation.
  • tableWidth and every important cellWidth fit the intended geometry.
  • Overflow behavior is intentional for headers, body cells, and long tokens.
  • startY, pageBreak, rowPageBreak, and header repetition match the report flow.
  • Horizontal splitting and repeated identifier columns are enabled for wide tables.
  • Hooks are placed according to parse, pre-draw, and post-draw timing.
  • The actual PDF has been inspected at page boundaries and with production-length data.

Frequently Asked Questions

Can I make every column a fixed width?

Yes, but the widths must fit the page after margins. If their sum is larger, reduce widths or use horizontal pagination; overflow settings cannot remove excess physical width.

Why does a wrapped row make the next page look almost empty?

linebreak increases row height. With rowPageBreak: 'avoid', AutoTable moves a row that cannot fit, so a large row can leave unused space on the preceding page.

Should I use HTML parsing for a complex report?

Use explicit head, body, and columns when geometry, hidden content, or CSS interpretation must be deterministic. HTML input is convenient but requires selector and parsed-text checks.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.