All formats

HTML to JSONHTML Tables

Scrape tables from HTML.

Convert any HTML <table> element (from a file, a fetched page, or a string) into structured worksheet JSON, one worksheet per table. Supports data-formula attributes, merged cells (colspan/rowspan), and inline styles.

Tables on web pages, in HTML e-mails and in .html exports from reporting tools hold real data, but getting it out usually means writing a DOM scraper for each page. TabularJS turns every <table> into a worksheet with a 2D data array, the same structure it returns for an Excel file.

It accepts an HTML string (any string that starts with <), a DOM element in the browser, or an .html or .htm file as a path, File, Blob or bytes. The markup is parsed by a small JavaScript HTML parser, so it works in Node.js without jsdom or a headless browser.

Feature support

Data values
Formulas
Styles
Merged cells
Comments
Column metadata

What TabularJS extracts from HTML files

Convert HTML to JSON in Node.js

js
import tabularjs from 'tabularjs';

// From a file
const result = await tabularjs('page.html');
const scores = result.worksheets[0];

console.log(scores.columns.map((c) => c.title)); // [ 'Name', 'Score' ]
console.log(scores.data);
// [ [ 'Alice', '90' ], [ 'Bob', '85' ], [ 'Total', '=SUM(B1:B2)' ], [ 'Final', '' ] ]

// From a fetched page: pass the HTML text, and pick the second table only
const response = await fetch('https://example.com/report.html');
const second = await tabularjs(await response.text(), { tableIndex: 1 });

Read HTML files in the browser

js
import tabularjs from 'tabularjs';

// Convert a table that is already on the page
const table = document.querySelector('#scores');
const result = await tabularjs(table);

const sheet = result.worksheets[0];
const keys = sheet.columns.map((c) => c.title);
const records = sheet.data.map((row) => Object.fromEntries(keys.map((k, i) => [k, row[i]])));
console.log(records); // [ { Name: 'Alice', Score: '90' }, ... ]

JSON output

json
{
  "worksheets": [
    {
      "worksheetName": "Table1",
      "data": [
        ["Alice", "90"],
        ["Bob", "85"],
        ["Total", "=SUM(B1:B2)"],
        ["Final", ""]
      ],
      "columns": [{ "title": "Name", "width": 100 }, { "title": "Score", "width": 100 }],
      "mergeCells": { "A4": [2, 1] },
      "style": { "A1": "color: red" },
      "comments": { "A1": "Top score" }
    },
    {
      "worksheetName": "Table2",
      "data": [],
      "columns": [{ "title": "x", "width": 100 }, { "title": "y", "width": 100 }]
    }
  ]
}

Complete output for a page with two tables. The second table has a single row, which became the column titles, so its data is empty.

Options

OptionEffect for HTML
tableIndexReturn only this table (0-based). The worksheet then has no worksheetName. An index past the last table throws an error.
firstRowAsHeaderDefault true: the first row becomes the column titles and is removed from data. Set false to keep it as data, with titles A, B, C...

Limitations

Frequently asked questions

How do I convert an HTML table to JSON in the browser?

Pass the element: await tabularjs(document.querySelector('table')). You get one worksheet with the header row in columns and the other rows in data.

Can I scrape a table from a URL in Node.js?

Fetch the page yourself and pass the HTML text: await tabularjs(await (await fetch(url)).text()). TabularJS does not download URLs, and pages that build their tables with JavaScript need a headless browser first.

How do I keep the first row as data?

Pass { firstRowAsHeader: false }. The first row stays in data and the columns get the titles A, B, C...

What happens to colspan and rowspan?

The value goes into the top-left cell, the covered cells are empty strings, and the span is recorded in mergeCells, for example { A4: [2, 1] } for a cell that spans two columns.

Why TabularJS for HTML Tables?