CSV Preview

A parsed CSV before you import it, showing the rows that will break the import. A preview that renders the first ten rows of a clean file has shown you nothing — mismatched column counts are what abort an import halfway.

src/components/ui/csv-preview.tsx

About

From the component's own source, where it was written.

A parsed CSV before you import it, with the rows that will break the import.

The point of a preview is not to show the data — it is to show the **bad rows**, and a preview that renders the first ten rows of a clean file has shown you nothing. Rows whose column count does not match the header are flagged and counted, because that is the failure that aborts an import halfway and leaves a table half-populated.

Type inference is per column and reported with a confidence, not asserted. A column that is 998 integers and 2 empty strings is an integer column with nulls; one that is 500 integers and 500 dates is a mess someone has to look at. Both would render as "mixed" if inference were a single guess.

It does not parse. Quoted fields containing commas and newlines are a real parser's job, and a component that ships a naive split(',') will silently mangle exactly the files that most need previewing.

Install

npx astralyx-ui add csv-preview

Usage

import { CsvPreview } from '@/components/ui/csv-preview'

<CsvPreview columns={columns} rows={rows} malformed={[4]} totalRows={18_402} />

Composer

Change the props and the source below follows.

1 row do not match the header and will fail the import.

#idstringemailstring1 emptyseatsinteger94%created_atmixed62%
1cus_8812ada@example.com122026-03-14
2cus_8813marc@example.com302/04/2026
3cus_8814∅82026-05-19
4cus_8815devon@example.commanyMay 2026
5cus_8816priya@example.com4∅3 cols

Showing 5 of 18,402 rows

Props
tsx
<CsvPreview
  columns={columns}
  rows={rows}
  totalRows={18_402}
  malformed={[4]}
/>

Examples

A file with a bad row

API

Generated from the source, so it cannot fall behind the code. One table per exported component, then hooks, functions and types.

CsvPreview

Extends Omit<ComponentProps<'div'>, 'children'>.

PropTypeDefaultDescription
columnsCsvColumn[]requiredAlready parsed. This renders; it does not parse — quoted fields with commas and newlines are a real parser’s job, and a naive split mangles exactly the files that need previewing.
rowsstring[][]requiredAlready parsed. This renders; it does not parse — quoted fields with commas and newlines are a real parser’s job, and a naive split mangles exactly the files that need previewing.
totalRowsnumber—Total rows in the file, when more than are shown here.
malformednumber[][]Row indices whose column count did not match the header. Surfaced above the table, since it is the reason to open a preview.
emptyLabelstring'This file has no rows.'—
malformedLabel(count: number) => ReactNode(count) => `${count} row${count === 1 ? '' : 's'} do not match the header and will fail the import.`—
showingLabel(shown: number, total: number) => ReactNode(shown, total) => `Showing ${shown} of ${total.toLocaleString()} rows`—
nullsLabelstring'empty'—

Types

CsvColumn

A parsed CSV before you import it, with the rows that will break the import.

FieldTypeDescription
namestring—
type?'string' | 'integer' | 'number' | 'boolean' | 'date' | 'mixed'What the caller's parser inferred.
confidence?numberInference reported rather than asserted: 998 integers and 2 blanks is an integer column with nulls; 500 integers and 500 dates is a mess someone has to look at.
nulls?numberCells that were empty.