Skip to main content

CSVtoTable

CSVtoTable converts CSV, TSV, and Excel files into interactive HTML tables.

  • Single native binary with embedded frontend assets
  • Standalone HTML output that works offline — data, styles, and scripts all inlined by default
  • CSV, TSV, and Excel (.xlsx) input, gzip archives included
  • Local files, URLs, standard input, or several files combined into one table
  • BOM and UTF-16 detected automatically; --encoding, --delimiter, --quotechar for the rest
  • Search, per-column filters with clearable chips, sorting, pagination, and virtual scrolling
  • Copy, CSV, JSON, and print exports, plus column show/hide
  • Markdown or raw HTML page title and description, inline or read from a file
  • Five colour themes, switchable in the page or fixed with --theme
  • Themes are just CSS variables — define your own with --css and it joins the picker
  • --css and --js inline your own stylesheet and script, with the live table API exposed
  • Self-unpacking output: the frontend ships gzipped, roughly halving every file
  • --split into separately cacheable assets instead, or --serve a preview over HTTP
  • Mobile-responsive layout

CSVtoTable demo

Try the live demo — a page built by CSVtoTable from the sample data, served as one static file.

Usage

# Write a standalone page
csvtotable input.csv page.html

# Combine files with the same columns
csvtotable input1.csv input2.csv page.html

# Gzip-compressed CSV files
csvtotable data.csv.gz data.html

# Read an Excel workbook
csvtotable sales.xlsx sales.html

# Fetch CSV directly from a URL
csvtotable https://raw.githubusercontent.com/vividvilla/csvtotable/master/demo/meteorite-landings-1.csv meteorites.html

# Build and open the page on a local HTTP server
csvtotable data.csv --serve

# Serve it on a port you choose
csvtotable data.csv --serve :8080

# Write a directory of separate files instead of one page
csvtotable data.csv site/ --split

# Add a title and generate headers for headerless data
csvtotable data.csv data.html --title "Sales" --no-header

# Title and description accept Markdown
csvtotable data.csv data.html --title "Sales for **Q3**" --description "Pulled from \`warehouse\`. See the [runbook](https://example.com)."

# or pass a markdown file as decription
csvtotable data.csv data.html --title "Sales for **Q3**" --description @sales.md

# or use raw HTML instead of Markdown
csvtotable data.csv data.html --title-html '<span>Sales <b>Q3</b></span>'
csvtotable data.csv data.html --title-html '<span>Sales <b>Q3</b></span>' --description-html @description.html

# Paginate instead of showing every row
csvtotable data.csv data.html --page-size 50

# Open in a particular theme
csvtotable data.csv data.html --theme solarized

# Add your own CSS and JavaScript
csvtotable data.csv data.html --css brand.css --js setup.js

# Read stdin and write stdout
curl -L https://example.com/data.csv | csvtotable - - > data.html

Run csvtotable --help for all options or csvtotable --version for the version. For compatibility with version 2, --caption, --display-length, --pagination, and --export still work; the last two disable those features.

Install

uvx, pipx, or pip

Run without installing:

uvx csvtotable data.csv data.html

Or install it:

pipx install csvtotable
uv tool install csvtotable
pip install csvtotable  # inside a virtual environment

npx or npm

npx @vividvilla/csvtotable data.csv data.html   # run without installing
npm install --global @vividvilla/csvtotable     # or install globally

To run a particular published version:

uvx csvtotable@X.Y.Z data.csv data.html
npx @vividvilla/csvtotable@X.Y.Z data.csv data.html

Homebrew

brew install vividvilla/tap/csvtotable

Standalone binary

Download an archive from GitHub Releases, verify it with SHA256SUMS, and place csvtotable (csvtotable.exe on Windows) on your PATH.

Prebuilt binaries support Linux x86-64/ARM64, macOS 12+ x86-64/Apple Silicon, and Windows 10+ x86-64.

Output

Size

The frontend script is gzipped and base64'd into the page, cutting an otherwise empty file from about 260KB to 145KB. Unpacking needs DecompressionStream (Chrome 103+, Firefox 113+, Safari 16.4+); older browsers get a message saying so, and --no-compress inlines readable source instead. The stylesheet stays uncompressed either way, so the page is styled at first paint.

Separate files

--split writes the output path as a directory instead of a single page:

site/
  index.html                    the page, a couple of KB
  csvtotable.9bf9d6348cd4.css   the stylesheet
  csvtotable.47304c323fd7.js    the frontend
  data.50248c1cfaed.js          the rows

References are relative, so the directory serves from any path, and the browser caches the frontend like any other asset — behind a gzipping server the demo data costs roughly 105KB the first time and 31KB on a revisit.

Filenames carry a content hash: an unchanged rerun keeps the URL and the cache hit, while changed rows get a new one that cannot be served stale. Superseded files are left in place for you to clear out. index.html keeps its name, so its freshness is up to whatever serves it.

Nothing needs fetch, so index.html still opens from disk. --css and --js stay inline — a --css theme has to be, for the theme picker to find it over file://. Compression does not apply here; caching does that job.

Serving

--serve builds the page into a temporary directory, serves it over HTTP, and opens a browser there. It takes an optional [HOST]:PORT:

csvtotable data.csv --serve                  # a random loopback port
csvtotable data.csv --serve :8080            # port 8080 on loopback
csvtotable data.csv --serve 0.0.0.0:8080     # every interface
csvtotable data.csv --serve --split          # each asset served separately

An empty host binds loopback; exposing the data on the network takes writing the host out, and prints a warning. The address is printed either way, so the page is reachable if no browser opens.

Responses carry Cache-Control: no-store, since the directory is rebuilt each run onto a port the kernel reuses and caching it would mix runs together. The directory is removed on exit. A --split directory you deploy yourself caches normally.

Styling

The page is plain semantic HTML, and every element CSVtoTable owns carries a csvtotable- class. These are the stable hooks:

Hook Element
.csvtotable <main>, the page root
.csvtotable-header <header> wrapping the title and description
.csvtotable-title <h1> page heading
.csvtotable-description description block
.csvtotable-table the <table>
.csvtotable-theme theme picker
.csvtotable-filters active-filter chip row
.csvtotable-chip one active filter, with -key, -value, and -remove parts
.csvtotable-clear the "clear all" control
.csvtotable-error shown only when the page cannot unpack itself

A theme is nothing but a block of colour variables. Every rule in the stylesheet reads them, so a new theme is a copy of one block with different values — no rules to write and nothing else to touch:

[data-theme="my-theme"] {
  color-scheme: dark;      /* so scrollbars and dropdowns match */
  --ct-paper: #1a1b26;     /* page background */
  --ct-ink: #c0caf5;       /* body text */
  --ct-muted: #7f88b0;     /* labels, secondary text */
  --ct-rule: #232433;      /* row separators */
  --ct-rule-strong: #343b58;
  --ct-accent: #7aa2f7;    /* focus, active sort, filter chips */
  --ct-wash: #1f2335;      /* accent tint behind chips */
  --ct-hover: #1e2030;     /* row and control hover */
}

Switching themes is just swapping data-theme on <html>. Type and metrics sit outside the palettes in the --ct-text-* scale and --ct-sans/--ct-mono, since they do not vary by theme. Headings are styled by level (h1 to h6), so a Markdown heading in a description matches the same level elsewhere; start description headings at ## to keep one <h1> per page.

Nothing is styled through an ID, so a stylesheet loaded after the page's own <style> overrides any of the above with a single class selector. Classes beginning dt- come from DataTables and may change when it is upgraded.

Custom CSS and JavaScript

--css and --js inline a stylesheet and a script into the page, keeping the output self-contained. Both take a file path; a leading @ is accepted but means nothing here:

csvtotable data.csv data.html --css brand.css --js setup.js

Placement is the point. The stylesheet goes last in <head>, so a single class selector overrides the built-in one without !important. The script goes last in <body>, after the table is built, with CsvToTable.table holding the live DataTables API instance:

CsvToTable.table.order([2, "desc"]).draw();   // sort by the third column
CsvToTable.table.column(0).visible(false);    // hide the first column

The table's height is fitted on the next animation frame, so wrap layout reads in requestAnimationFrame; column widths and scroll height are inline styles a stylesheet cannot override — use --height. In a compressed page the script is parked in an inert <script type="text/plain"> and run by the unpacker, so it still runs after the table is built.

A new palette is a stylesheet plus a matching --theme, whose name need not be built-in; the picker lists it alongside the others:

csvtotable data.csv data.html --css custom.css --theme tokyonight

"Auto" in the picker unpins data-theme and falls back to the built-in palettes, so keep [data-theme] blocks for the palette itself and put anything that should survive in :root.

Both flags are trusted input: whatever the files contain runs for anyone who opens the page, so do not generate them from untrusted data. Content is escaped only so it cannot break out of its <style> or <script> element; it is not sanitised.

Development

Building requires Go 1.25+ and Bun. Distribution packaging also requires Python and npm. table/dist is generated before the Go build and embedded in the final binary.

nix develop  # optional development environment
make build   # frontend and build/csvtotable
make test    # frontend, formatting, vet, and tests
make demo    # the published demo page under site/
make dist    # binary and host-platform packages under dist/
./build/csvtotable demo/meteorite-landings-1.csv demo/meteorite-landings-2.csv /tmp/meteorites.html

License

MIT

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

csvtotable-3.2.0-py3-none-win_amd64.whl (6.4 MB view details)

Uploaded Python 3Windows x86-64

csvtotable-3.2.0-py3-none-musllinux_1_2_x86_64.whl (6.3 MB view details)

Uploaded Python 3musllinux: musl 1.2+ x86-64

csvtotable-3.2.0-py3-none-musllinux_1_2_aarch64.whl (5.7 MB view details)

Uploaded Python 3musllinux: musl 1.2+ ARM64

csvtotable-3.2.0-py3-none-manylinux_2_17_x86_64.whl (6.3 MB view details)

Uploaded Python 3manylinux: glibc 2.17+ x86-64

csvtotable-3.2.0-py3-none-manylinux_2_17_aarch64.whl (5.7 MB view details)

Uploaded Python 3manylinux: glibc 2.17+ ARM64

csvtotable-3.2.0-py3-none-macosx_12_0_x86_64.whl (6.4 MB view details)

Uploaded Python 3macOS 12.0+ x86-64

csvtotable-3.2.0-py3-none-macosx_12_0_arm64.whl (5.9 MB view details)

Uploaded Python 3macOS 12.0+ ARM64

File details

Details for the file csvtotable-3.2.0-py3-none-win_amd64.whl.

File metadata

  • Download URL: csvtotable-3.2.0-py3-none-win_amd64.whl
  • Upload date:
  • Size: 6.4 MB
  • Tags: Python 3, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for csvtotable-3.2.0-py3-none-win_amd64.whl
Algorithm Hash digest
SHA256 88125c8b979634198af4a3136c8cee052b00fc8cc7ce75ae7d462a1a0caca564
MD5 6dea55982cf21471866c0e44bed4ee11
BLAKE2b-256 27eceb4e4d4803d41a1e6c88e500b19925f7fb9866e5d8bec70fcea2e36df978

See more details on using hashes here.

Provenance

The following attestation bundles were made for csvtotable-3.2.0-py3-none-win_amd64.whl:

Publisher: release.yml on vividvilla/csvtotable

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file csvtotable-3.2.0-py3-none-musllinux_1_2_x86_64.whl.

File metadata

File hashes

Hashes for csvtotable-3.2.0-py3-none-musllinux_1_2_x86_64.whl
Algorithm Hash digest
SHA256 9cbb91c41009b986c7205de29acfda85e40a2b86fdf304e2f20eab9778007ec3
MD5 6fc8bb32c2bfbed62b90c35df2105a18
BLAKE2b-256 ee3450c8b866dec54f1758113e29c066fd10c808afd7eb31f0890f8ed6fab5e8

See more details on using hashes here.

Provenance

The following attestation bundles were made for csvtotable-3.2.0-py3-none-musllinux_1_2_x86_64.whl:

Publisher: release.yml on vividvilla/csvtotable

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file csvtotable-3.2.0-py3-none-musllinux_1_2_aarch64.whl.

File metadata

File hashes

Hashes for csvtotable-3.2.0-py3-none-musllinux_1_2_aarch64.whl
Algorithm Hash digest
SHA256 111d5b209e8c9b8076f8427a5c3e012ab180dcb74c52e5f2dba8df8cdd37ca39
MD5 ab224df1dac7bb91b768ced7c4441557
BLAKE2b-256 2a93d1b6f698b61f91b628fdadbfa5e13a2b597dd0491b15bba00831c933e69e

See more details on using hashes here.

Provenance

The following attestation bundles were made for csvtotable-3.2.0-py3-none-musllinux_1_2_aarch64.whl:

Publisher: release.yml on vividvilla/csvtotable

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file csvtotable-3.2.0-py3-none-manylinux_2_17_x86_64.whl.

File metadata

File hashes

Hashes for csvtotable-3.2.0-py3-none-manylinux_2_17_x86_64.whl
Algorithm Hash digest
SHA256 a0dc5224cd55c6f682ed868c01b2029d1eae170ee5b004a1925fe331a0ce699e
MD5 d6aafeb9ea4d56720bd08c0106826245
BLAKE2b-256 98a71d24b2c74ef85b763b1ab49576f60ea68d0368d77a27ba151fd2175c6668

See more details on using hashes here.

Provenance

The following attestation bundles were made for csvtotable-3.2.0-py3-none-manylinux_2_17_x86_64.whl:

Publisher: release.yml on vividvilla/csvtotable

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file csvtotable-3.2.0-py3-none-manylinux_2_17_aarch64.whl.

File metadata

File hashes

Hashes for csvtotable-3.2.0-py3-none-manylinux_2_17_aarch64.whl
Algorithm Hash digest
SHA256 28c8ce08710bc7cb5e219d8b1e7e8a79c4245649159d3f017a5d1438f4b4a024
MD5 2571d78d0bcfbafb638cc48c1667a996
BLAKE2b-256 2f62c5421083e7409080633e6c4d88af6612d5f74cbdb166e22dd672bc327d04

See more details on using hashes here.

Provenance

The following attestation bundles were made for csvtotable-3.2.0-py3-none-manylinux_2_17_aarch64.whl:

Publisher: release.yml on vividvilla/csvtotable

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file csvtotable-3.2.0-py3-none-macosx_12_0_x86_64.whl.

File metadata

File hashes

Hashes for csvtotable-3.2.0-py3-none-macosx_12_0_x86_64.whl
Algorithm Hash digest
SHA256 61a7972d47ca28e19b43089f5237237349c7962a7baacd583063f71192518e23
MD5 6ffc5cb7816e6dffdf77a40dbd6bdaa4
BLAKE2b-256 72491ce016b4fe9481994c23a18301db363f742f7aec12b95e7f803a030e0b70

See more details on using hashes here.

Provenance

The following attestation bundles were made for csvtotable-3.2.0-py3-none-macosx_12_0_x86_64.whl:

Publisher: release.yml on vividvilla/csvtotable

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file csvtotable-3.2.0-py3-none-macosx_12_0_arm64.whl.

File metadata

File hashes

Hashes for csvtotable-3.2.0-py3-none-macosx_12_0_arm64.whl
Algorithm Hash digest
SHA256 485081ceb115d160c90c6c8e8442d2c6419f6c294dd960c6ab25c681d041587d
MD5 fc5e3e72d99c78ab91bf22c414e97ac9
BLAKE2b-256 ef1f4d584f1e12701a542bd21ed47e8b585ca1372073a70b0fa37aa3b0f51cf3

See more details on using hashes here.

Provenance

The following attestation bundles were made for csvtotable-3.2.0-py3-none-macosx_12_0_arm64.whl:

Publisher: release.yml on vividvilla/csvtotable

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

3.2.0 This release

7 files

3.1.0

7 files

3.0.0

7 files

2.1.1

2 files

2.1.0

2 files

2.0.0

2 files

1.1.4

1 file

1.1.3

2 files

1.1.2

2 files

1.1.1

2 files

1.1.0

2 files

1.0.2

2 files

1.0.1

2 files

1.0.0

2 files

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page