CSV Viewer Open CSV

How to view large CSV files

Excel stops at just over a million rows and slows badly long before that. Here is how to open, search and query a CSV that is far bigger than your spreadsheet can handle.

6 min read

Where each tool gives up

ToolHard limitWhere it gets painful
Excel1,048,576 rows~200k rows with several columns
Google Sheets10 million cells~150k rows × 20 columns, plus a slow import
Numbers (macOS)1,000,000 rows~100k rows
Notepad / TextEditNone stated~50 MB, and it is not a table anyway
Upload-based web toolsUsually 10k–100k rowsThe upload itself
Browser viewerNoneYour device's free memory
DuckDB / xsvNoneDisk speed — these stream the file

Opening it in a browser

A well-built browser viewer handles far more than people expect, because it never draws what you cannot see. This one keeps about forty rows in the document at a time and recycles them as you scroll, so the DOM stays the same size whether the file has a thousand rows or a million. Sorting and searching run over arrays already in memory, with no re-read and no network.

In practice a 100 MB CSV opens in a few seconds on an ordinary laptop, and files up to roughly 500 MB are workable. Beyond that you are fighting the browser's per-tab memory ceiling rather than the page.

Close other tabs before opening a very large file. A browser tab's memory budget is shared, and a dozen open tabs is often the difference between a file opening and the tab being killed.

Take a sample first

Most of the time you do not need the whole file — you need to know what the columns are, what the values look like, and whether the export is broken. A few thousand rows answers all three in under a second:

# first 5,000 rows, header included
head -5000 big.csv > sample.csv

# a random sample, header preserved
{ head -1 big.csv; tail -n +2 big.csv | shuf -n 5000; } > sample.csv

# the last rows, to check where an export stopped
{ head -1 big.csv; tail -5000 big.csv; } > sample.csv

Splitting a file into openable pieces

# 500,000 rows per piece
split -l 500000 big.csv part_

# with the header repeated on each piece
head -1 big.csv > header.csv
for f in part_*; do cat header.csv "$f" > "with_header_$f"; done
rm header.csv part_*

On Windows, with PowerShell:

$reader = [IO.File]::OpenText("big.csv")
$header = $reader.ReadLine(); $i = 0; $n = 0; $writer = $null
while (($line = $reader.ReadLine()) -ne $null) {
  if ($i % 500000 -eq 0) {
    if ($writer) { $writer.Close() }
    $writer = [IO.File]::CreateText("part_$n.csv")
    $writer.WriteLine($header); $n++
  }
  $writer.WriteLine($line); $i++
}
$writer.Close(); $reader.Close()

Querying instead of scrolling

Past a certain size, reading a file row by row stops being useful — you want an answer, not a view. DuckDB reads CSV directly with no import step and streams from disk, so file size stops mattering:

# what are the columns?
duckdb -c "DESCRIBE SELECT * FROM 'big.csv'"

# how many rows?
duckdb -c "SELECT count(*) FROM 'big.csv'"

# the rows you actually care about
duckdb -c "SELECT * FROM 'big.csv' WHERE status = 'failed' LIMIT 100"

# write a manageable slice back out, then open it in the viewer
duckdb -c "COPY (SELECT * FROM 'big.csv' WHERE country = 'DE') TO 'de.csv'"

That last pattern is the practical workflow for very large files: filter on the command line, view the result in a browser. You get SQL's reach and a real table to read.

Memory rules of thumb

  • A CSV loaded into memory as JavaScript strings typically uses 5–10× the file size. A 100 MB file needs roughly 0.5–1 GB free.
  • Wide files cost more than tall ones — per-value overhead dominates, so 50 columns × 100k rows is heavier than 5 columns × 1M rows at the same byte count.
  • A 64-bit browser tab can usually address about 2–4 GB before it is killed.
  • If a tab dies mid-load, the file was too big for the device, not for the viewer. Sample or split it.