PapaParse Reference
CSV parsing and generation library. Use PapaParse to parse CSV strings into structured data and convert data back to CSV for export.
Import
import Papa from "papaparse";
Core Concepts
Papa.parse()parses CSV text into arrays or objectsPapa.unparse()converts arrays/objects back to CSV textheader: truetreats the first row as field names and returns objects instead of arraysdynamicTyping: trueauto-converts numbers and booleans from strings- Combine with
fetch()to load CSV app assets, or withuseFileUploadfor user-uploaded CSV files
Parsing CSV Strings
Basic Parse (Arrays)
const csv = "Name,Age,City\nAlice,30,NYC\nBob,25,LA";
const result = Papa.parse(csv);
// result.data = [
// ["Name", "Age", "City"],
// ["Alice", "30", "NYC"],
// ["Bob", "25", "LA"]
// ]
Parse with Headers (Objects)
const csv = "Name,Age,City\nAlice,30,NYC\nBob,25,LA";
const result = Papa.parse(csv, {
header: true,
skipEmptyLines: true,
});
// result.data = [
// { Name: "Alice", Age: "30", City: "NYC" },
// { Name: "Bob", Age: "25", City: "LA" }
// ]
// result.meta.fields = ["Name", "Age", "City"]
Parse with Dynamic Typing
const csv = "Name,Age,Active\nAlice,30,true\nBob,25,false";
const result = Papa.parse(csv, {
header: true,
dynamicTyping: true,
skipEmptyLines: true,
});
// result.data = [
// { Name: "Alice", Age: 30, Active: true },
// { Name: "Bob", Age: 25, Active: false }
// ]
Transform Values During Parse
const result = Papa.parse(csv, {
header: true,
dynamicTyping: true,
skipEmptyLines: true,
transformHeader: (header) => header.trim().toLowerCase().replace(/\s+/g, "_"),
transform: (value) => value.trim(),
});
Parse Config Options
| Option | Type | Default | Description |
|---|---|---|---|
header | boolean | false | First row is field names; rows become objects keyed by field name |
dynamicTyping | boolean | false | Auto-convert numeric and boolean strings to their types |
skipEmptyLines | boolean | "greedy" | false | Skip empty lines; "greedy" also skips whitespace-only lines |
delimiter | string | auto-detect | Delimiting character (leave blank to auto-detect) |
comments | string | false | String that indicates a comment line (e.g. "#") |
transformHeader | function | undefined | Transform each header name; receives (header, index) |
transform | function | undefined | Transform each cell value; receives (value, columnNameOrIndex) |
preview | number | 0 | If > 0, only parse that many rows |
skipFirstNLines | number | 0 | Skip first N lines before parsing |
Parse Result Object
const result = Papa.parse(csv, { header: true });
result.data // Array of parsed rows (arrays or objects)
result.errors // Array of parse errors
result.meta // { delimiter, linebreak, fields, aborted, truncated }
Error Structure
// Each error in result.errors:
{
type: "Quotes" | "Delimiter" | "FieldMismatch",
code: "MissingQuotes" | "InvalidQuotes" | "UndetectableDelimiter" | "TooFewFields" | "TooManyFields",
message: "Human-readable details",
row?: number, // Row index where error occurred (optional)
index?: number, // Character index within the row (optional)
}
Converting Data to CSV
Unparse Arrays
const csv = Papa.unparse([
["Name", "Age", "City"],
["Alice", "30", "NYC"],
["Bob", "25", "LA"],
]);
// "Name,Age,City\r\nAlice,30,NYC\r\nBob,25,LA"
Unparse Objects (Auto Header Row)
const csv = Papa.unparse([
{ Name: "Alice", Age: 30, City: "NYC" },
{ Name: "Bob", Age: 25, City: "LA" },
]);
// "Name,Age,City\r\nAlice,30,NYC\r\nBob,25,LA"
Unparse with Explicit Fields
const csv = Papa.unparse({
fields: ["Name", "City"],
data: [
["Alice", "NYC"],
["Bob", "LA"],
],
});
Unparse Config Options
| Option | Type | Default | Description |
|---|---|---|---|
header | boolean | true | Include header row (ignored for array of arrays) |
delimiter | string | "," | Delimiting character |
quotes | boolean | boolean[] | function | false | Force-quote all fields, per column array, or (value, colIndex) => boolean |
newline | string | "\r\n" | Newline sequence |
skipEmptyLines | boolean | "greedy" | false | Skip empty lines; "greedy" also skips whitespace-only lines |
columns | string[] | undefined | Manually specify keys to include (for array of objects) |
Example: Load and Display CSV App Asset
import * as React from "react";
import Papa from "papaparse";
const CSV_ASSET_URL = "https://...blob-url-from-assets...";
type Row = Record<string, string>;
export default function App() {
const [rows, setRows] = React.useState<Row[]>([]);
const [columns, setColumns] = React.useState<string[]>([]);
const [loading, setLoading] = React.useState(true);
React.useEffect(() => {
const loadData = async () => {
const response = await fetch(CSV_ASSET_URL);
const text = await response.text();
const result = Papa.parse<Row>(text, {
header: true,
dynamicTyping: true,
skipEmptyLines: true,
});
setColumns(result.meta.fields ?? []);
setRows(result.data);
setLoading(false);
};
loadData();
}, []);
if (loading) return <div className="flex justify-center p-8"><span className="inline-block h-4 w-4 animate-spin rounded-full border-2 border-current border-t-transparent" /></div>;
return (
<div>
<table>
<thead>
<tr>
{columns.map((col) => (
<th key={col}>{col}</th>
))}
</tr>
</thead>
<tbody>
{rows.map((row, i) => (
<tr key={i}>
{columns.map((col) => (
<td key={col}>{String(row[col] ?? "")}</td>
))}
</tr>
))}
</tbody>
</table>
</div>
);
}
Example: Upload CSV, Parse, and Persist
import * as React from "react";
import Papa from "papaparse";
import { Download } from "lucide-react";
type Expense = { date: string; category: string; amount: number; note: string };
export default function App() {
const [expenses] = usePersistentItem<Expense[]>("expenses", []);
const { download } = useDownload();
const handleExport = () => {
const csv = Papa.unparse(expenses);
download(csv, "expenses.csv");
};
return (
<div>
<button onClick={handleExport} disabled={expenses.length === 0}>
<Download className="w-4 h-4" />
Export {expenses.length} expenses
</button>
</div>
);
}
Best Practices
- Always use
header: truefor CSV with a header row — returns objects instead of index-based arrays - Use
dynamicTyping: trueto auto-convert numbers and booleans — avoids manualparseInt/parseFloat - Use
skipEmptyLines: trueto avoid empty trailing rows from spreadsheet exports - Check
result.errorsafter parsing — errors don't necessarily mean failure, but warn about data issues - Use
transformHeaderto normalize headers — trim whitespace, lowercase, replace spaces with underscores - Use
Papa.unparse()for CSV export instead of manual string concatenation — handles quoting, escaping, and special characters correctly - Type your parsed data — use
Papa.parse<MyType>(text, config)for TypeScript type safety