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.tsxAbout
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.
| # | idstring | emailstring1 empty | seatsinteger94% | created_atmixed62% | |
|---|---|---|---|---|---|
| 1 | cus_8812 | ada@example.com | 12 | 2026-03-14 | |
| 2 | cus_8813 | marc@example.com | 3 | 02/04/2026 | |
| 3 | cus_8814 | ∅ | 8 | 2026-05-19 | |
| 4 | cus_8815 | devon@example.com | many | May 2026 | |
| 5 | cus_8816 | priya@example.com | 4 | ∅ | 3 cols |
Showing 5 of 18,402 rows
<CsvPreview
columns={columns}
rows={rows}
totalRows={18_402}
malformed={[4]}
/>Examples
A file with a bad row
<CsvPreview columns={columns} rows={rows} malformed={[4]} />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'>.
| Prop | Type | Default | Description |
|---|---|---|---|
| columns | CsvColumn[] | required | Already 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. |
| rows | string[][] | required | Already 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. |
| totalRows | number | — | Total rows in the file, when more than are shown here. |
| malformed | number[] | [] | Row indices whose column count did not match the header. Surfaced above the table, since it is the reason to open a preview. |
| emptyLabel | string | '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` | — |
| nullsLabel | string | 'empty' | — |
Types
CsvColumn
A parsed CSV before you import it, with the rows that will break the import.
| Field | Type | Description |
|---|---|---|
| name | string | — |
| type? | 'string' | 'integer' | 'number' | 'boolean' | 'date' | 'mixed' | What the caller's parser inferred. |
| confidence? | number | Inference 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? | number | Cells that were empty. |