Features
Now the fastest React CSV parser for the browser
The world's first multi-threaded CSV parser for the browser
react-papaparse can handle files gigabytes in size without crashing
Use react-papaparse when performance, privacy, and correctness matter to you
react-papaparse alleviates privacy concerns related to uploading files
Malformed CSV is handled gracefully with a detailed error report
Frameworks
react-papaparse strongly support Next.js and other React frameworks. react-papaparse is the fastest React CSV parser for the browser (only works in the browser), so you need to set the component with no SSR (server-side render) in case you use readRemoteFile
function.
CSV Parsing
"Isn't parsing CSV just String.split(',')
?"
react-papaparse does it right. Just pass in the CSV string with an optional configuration.
import React from 'react';
import { usePapaParse } from 'react-papaparse';
export default function ReadString() {
const { readString } = usePapaParse();
const handleReadString = () => {
const csvString = 'Column 1,Column 2,Column 3,Column 4
1-1,1-2,1-3,1-4
2-1,2-2,2-3,2-4
3-1,3-2,3-3,3-4
4,5,6,7';
readString(csvString, {
worker: true,
complete: (results) => {
console.log('---------------------------');
console.log(results);
console.log('---------------------------');
},
});
};
return <button onClick={() => handleReadString()}>readString</button>;
}
/*
results = {
data: [ ... ], // parsed data
errors: [ ... ], // errors encountered
meta: { ... } // extra parse info
}
*/
Delimiter Detection
"But I don't know the delimiter..."
That's okay. react-papaparse will scan the first few rows to find the right delimiter.
const config = {
worker: true,
complete: (results) => {
console.log('---------------------------');
console.log(results.meta.delimiter);
console.log('---------------------------');
},
};
readString(csvString, config);
// "\t"
Local Files
"Great, but I have a file to parse."
Then use CSVReader component instead of readString method. Since file parsing is asynchronous, don't forget callback methods.
import React, { CSSProperties } from 'react';
import { useCSVReader } from 'react-papaparse';
const styles = {
csvReader: {
display: 'flex',
flexDirection: 'row',
marginBottom: 10,
} as CSSProperties,
browseFile: {
width: '20%',
} as CSSProperties,
acceptedFile: {
border: '1px solid #ccc',
height: 45,
lineHeight: 2.5,
paddingLeft: 10,
width: '80%',
} as CSSProperties,
remove: {
borderRadius: 0,
padding: '0 20px',
} as CSSProperties,
progressBarBackgroundColor: {
backgroundColor: 'red',
} as CSSProperties,
};
export default function CSVReader() {
const { CSVReader } = useCSVReader();
return (
<CSVReader
onUploadAccepted={(results: any) => {
console.log('---------------------------');
console.log(results);
console.log('---------------------------');
}}
>
{({
getRootProps,
acceptedFile,
ProgressBar,
getRemoveFileProps,
}: any) => (
<>
<div style={styles.csvReader}>
<button type='button' {...getRootProps()} style={styles.browseFile}>
Browse file
</button>
<div style={styles.acceptedFile}>
{acceptedFile && acceptedFile.name}
</div>
<button {...getRemoveFileProps()} style={styles.remove}>
Remove
</button>
</div>
<ProgressBar style={styles.progressBarBackgroundColor} />
</>
)}
</CSVReader>
);
}
import React, { useState, CSSProperties } from 'react';
import {
useCSVReader,
lightenDarkenColor,
formatFileSize,
} from 'react-papaparse';
const GREY = '#CCC';
const GREY_LIGHT = 'rgba(255, 255, 255, 0.4)';
const DEFAULT_REMOVE_HOVER_COLOR = '#A01919';
const REMOVE_HOVER_COLOR_LIGHT = lightenDarkenColor(
DEFAULT_REMOVE_HOVER_COLOR,
40
);
const GREY_DIM = '#686868';
const styles = {
zone: {
alignItems: 'center',
borderWidth: 2,
borderStyle: 'dashed',
borderColor: GREY,
borderRadius: 20,
display: 'flex',
flexDirection: 'column',
height: '100%',
justifyContent: 'center',
padding: 20,
} as CSSProperties,
file: {
background: 'linear-gradient(to bottom, #EEE, #DDD)',
borderRadius: 20,
display: 'flex',
height: 120,
width: 120,
position: 'relative',
zIndex: 10,
flexDirection: 'column',
justifyContent: 'center',
} as CSSProperties,
info: {
alignItems: 'center',
display: 'flex',
flexDirection: 'column',
paddingLeft: 10,
paddingRight: 10,
} as CSSProperties,
size: {
backgroundColor: GREY_LIGHT,
borderRadius: 3,
marginBottom: '0.5em',
justifyContent: 'center',
display: 'flex',
} as CSSProperties,
name: {
backgroundColor: GREY_LIGHT,
borderRadius: 3,
fontSize: 12,
marginBottom: '0.5em',
} as CSSProperties,
progressBar: {
bottom: 14,
position: 'absolute',
width: '100%',
paddingLeft: 10,
paddingRight: 10,
} as CSSProperties,
zoneHover: {
borderColor: GREY_DIM,
} as CSSProperties,
default: {
borderColor: GREY,
} as CSSProperties,
remove: {
height: 23,
position: 'absolute',
right: 6,
top: 6,
width: 23,
} as CSSProperties,
};
export default function CSVReader() {
const { CSVReader } = useCSVReader();
const [zoneHover, setZoneHover] = useState(false);
const [removeHoverColor, setRemoveHoverColor] = useState(
DEFAULT_REMOVE_HOVER_COLOR
);
return (
<CSVReader
onUploadAccepted={(results: any) => {
console.log('---------------------------');
console.log(results);
console.log('---------------------------');
setZoneHover(false);
}}
onDragOver={(event: DragEvent) => {
event.preventDefault();
setZoneHover(true);
}}
onDragLeave={(event: DragEvent) => {
event.preventDefault();
setZoneHover(false);
}}
>
{({
getRootProps,
acceptedFile,
ProgressBar,
getRemoveFileProps,
Remove,
}: any) => (
<>
<div
{...getRootProps()}
style={Object.assign(
{},
styles.zone,
zoneHover && styles.zoneHover
)}
>
{acceptedFile ? (
<>
<div style={styles.file}>
<div style={styles.info}>
<span style={styles.size}>
{formatFileSize(acceptedFile.size)}
</span>
<span style={styles.name}>{acceptedFile.name}</span>
</div>
<div style={styles.progressBar}>
<ProgressBar />
</div>
<div
{...getRemoveFileProps()}
style={styles.remove}
onMouseOver={(event: Event) => {
event.preventDefault();
setRemoveHoverColor(REMOVE_HOVER_COLOR_LIGHT);
}}
onMouseOut={(event: Event) => {
event.preventDefault();
setRemoveHoverColor(DEFAULT_REMOVE_HOVER_COLOR);
}}
>
<Remove color={removeHoverColor} />
</div>
</div>
</>
) : (
'Drop CSV file here or click to upload'
)}
</div>
</>
)}
</CSVReader>
);
}
import React, { useState, CSSProperties } from 'react';
import {
useCSVReader,
lightenDarkenColor,
formatFileSize,
} from 'react-papaparse';
const GREY = '#CCC';
const GREY_LIGHT = 'rgba(255, 255, 255, 0.4)';
const DEFAULT_REMOVE_HOVER_COLOR = '#A01919';
const REMOVE_HOVER_COLOR_LIGHT = lightenDarkenColor(
DEFAULT_REMOVE_HOVER_COLOR,
40
);
const GREY_DIM = '#686868';
const styles = {
zone: {
alignItems: 'center',
borderWidth: 2,
borderStyle: 'dashed',
borderColor: GREY,
borderRadius: 20,
display: 'flex',
flexDirection: 'column',
height: '100%',
justifyContent: 'center',
padding: 20,
} as CSSProperties,
file: {
background: 'linear-gradient(to bottom, #EEE, #DDD)',
borderRadius: 20,
display: 'flex',
height: 120,
width: 120,
position: 'relative',
zIndex: 10,
flexDirection: 'column',
justifyContent: 'center',
} as CSSProperties,
info: {
alignItems: 'center',
display: 'flex',
flexDirection: 'column',
paddingLeft: 10,
paddingRight: 10,
} as CSSProperties,
size: {
backgroundColor: GREY_LIGHT,
borderRadius: 3,
marginBottom: '0.5em',
justifyContent: 'center',
display: 'flex',
} as CSSProperties,
name: {
backgroundColor: GREY_LIGHT,
borderRadius: 3,
fontSize: 12,
marginBottom: '0.5em',
} as CSSProperties,
progressBar: {
bottom: 14,
position: 'absolute',
width: '100%',
paddingLeft: 10,
paddingRight: 10,
} as CSSProperties,
zoneHover: {
borderColor: GREY_DIM,
} as CSSProperties,
default: {
borderColor: GREY,
} as CSSProperties,
remove: {
height: 23,
position: 'absolute',
right: 6,
top: 6,
width: 23,
} as CSSProperties,
};
export default function CSVReader() {
const { CSVReader } = useCSVReader();
const [zoneHover, setZoneHover] = useState(false);
const [removeHoverColor, setRemoveHoverColor] = useState(
DEFAULT_REMOVE_HOVER_COLOR
);
return (
<CSVReader
onUploadAccepted={(results: any) => {
console.log('---------------------------');
console.log(results);
console.log('---------------------------');
setZoneHover(false);
}}
onDragOver={(event: DragEvent) => {
event.preventDefault();
setZoneHover(true);
}}
onDragLeave={(event: DragEvent) => {
event.preventDefault();
setZoneHover(false);
}}
noClick
>
{({
getRootProps,
acceptedFile,
ProgressBar,
getRemoveFileProps,
Remove,
}: any) => (
<>
<div
{...getRootProps()}
style={Object.assign(
{},
styles.zone,
zoneHover && styles.zoneHover
)}
>
{acceptedFile ? (
<>
<div style={styles.file}>
<div style={styles.info}>
<span style={styles.size}>
{formatFileSize(acceptedFile.size)}
</span>
<span style={styles.name}>{acceptedFile.name}</span>
</div>
<div style={styles.progressBar}>
<ProgressBar />
</div>
<div
{...getRemoveFileProps()}
style={styles.remove}
onMouseOver={(event: Event) => {
event.preventDefault();
setRemoveHoverColor(REMOVE_HOVER_COLOR_LIGHT);
}}
onMouseOut={(event: Event) => {
event.preventDefault();
setRemoveHoverColor(DEFAULT_REMOVE_HOVER_COLOR);
}}
>
<Remove color={removeHoverColor} />
</div>
</div>
</>
) : (
'Drop CSV file here to upload'
)}
</div>
</>
)}
</CSVReader>
);
}
import React, { useState, CSSProperties } from 'react';
import {
useCSVReader,
lightenDarkenColor,
formatFileSize,
} from 'react-papaparse';
const GREY = '#CCC';
const GREY_LIGHT = 'rgba(255, 255, 255, 0.4)';
const DEFAULT_REMOVE_HOVER_COLOR = '#A01919';
const REMOVE_HOVER_COLOR_LIGHT = lightenDarkenColor(
DEFAULT_REMOVE_HOVER_COLOR,
40
);
const GREY_DIM = '#686868';
const styles = {
zone: {
alignItems: 'center',
borderWidth: 2,
borderStyle: 'dashed',
borderColor: GREY,
borderRadius: 20,
display: 'flex',
flexDirection: 'column',
height: '100%',
justifyContent: 'center',
padding: 20,
} as CSSProperties,
file: {
background: 'linear-gradient(to bottom, #EEE, #DDD)',
borderRadius: 20,
display: 'flex',
height: 120,
width: 120,
position: 'relative',
zIndex: 10,
flexDirection: 'column',
justifyContent: 'center',
} as CSSProperties,
info: {
alignItems: 'center',
display: 'flex',
flexDirection: 'column',
paddingLeft: 10,
paddingRight: 10,
} as CSSProperties,
size: {
backgroundColor: GREY_LIGHT,
borderRadius: 3,
marginBottom: '0.5em',
justifyContent: 'center',
display: 'flex',
} as CSSProperties,
name: {
backgroundColor: GREY_LIGHT,
borderRadius: 3,
fontSize: 12,
marginBottom: '0.5em',
} as CSSProperties,
progressBar: {
bottom: 14,
position: 'absolute',
width: '100%',
paddingLeft: 10,
paddingRight: 10,
} as CSSProperties,
zoneHover: {
borderColor: GREY_DIM,
} as CSSProperties,
default: {
borderColor: GREY,
} as CSSProperties,
remove: {
height: 23,
position: 'absolute',
right: 6,
top: 6,
width: 23,
} as CSSProperties,
};
export default function CSVReader() {
const { CSVReader } = useCSVReader();
const [zoneHover, setZoneHover] = useState(false);
const [removeHoverColor, setRemoveHoverColor] = useState(
DEFAULT_REMOVE_HOVER_COLOR
);
return (
<CSVReader
onUploadAccepted={(results: any) => {
console.log('---------------------------');
console.log(results);
console.log('---------------------------');
setZoneHover(false);
}}
onDragOver={(event: DragEvent) => {
event.preventDefault();
setZoneHover(true);
}}
onDragLeave={(event: DragEvent) => {
event.preventDefault();
setZoneHover(false);
}}
noDrag
>
{({
getRootProps,
acceptedFile,
ProgressBar,
getRemoveFileProps,
Remove,
}: any) => (
<>
<div
{...getRootProps()}
style={Object.assign(
{},
styles.zone,
zoneHover && styles.zoneHover
)}
>
{acceptedFile ? (
<>
<div style={styles.file}>
<div style={styles.info}>
<span style={styles.size}>
{formatFileSize(acceptedFile.size)}
</span>
<span style={styles.name}>{acceptedFile.name}</span>
</div>
<div style={styles.progressBar}>
<ProgressBar />
</div>
<div
{...getRemoveFileProps()}
style={styles.remove}
onMouseOver={(event: Event) => {
event.preventDefault();
setRemoveHoverColor(REMOVE_HOVER_COLOR_LIGHT);
}}
onMouseOut={(event: Event) => {
event.preventDefault();
setRemoveHoverColor(DEFAULT_REMOVE_HOVER_COLOR);
}}
>
<Remove color={removeHoverColor} />
</div>
</div>
</>
) : (
'Click to upload'
)}
</div>
</>
)}
</CSVReader>
);
}
Remote Files
"No — I mean, the file isn't on my computer."
Oh, well then just pass in the URL and — of course — a callback.
import React from 'react';
import { usePapaParse } from 'react-papaparse';
export default function ReadRemoteFile() {
const { readRemoteFile } = usePapaParse();
const handleReadRemoteFile = () => {
readRemoteFile(url, {
complete: (results) => {
console.log('---------------------------');
console.log('Results:', results);
console.log('---------------------------');
},
});
};
return <button onClick={() => handleReadRemoteFile()}>readRemoteFile</button>;
}
Streaming
"Did I mention the file is huge?"
That's what streaming is for. Specify a step callback to receive the results row-by-row. This way, you won't load the whole file into memory and crash the browser.
readRemoteFile(url, {
step: (row) => {
console.log('Row:', row.data)
},
complete: () => {
console.log('All done!')
}
});
Multi-Threading
'Lovely. Now my web page locked up.'
That happens when a long-running script is executing in the same thread as the page. Use a Worker thread by specifying worker: true
. It may take slightly longer, but your page will stay reactive.
readRemoteFile(bigFileURL, {
worker: true,
step: (row) => {
console.log('Row:', row.data)
},
complete: () => {
console.log('All done!')
}
});
Header Row
"Great! Now I want data keyed by field name."
If you tell react-papaparse there is a header row, each row will be organized by field name instead of index.
// Key data by field name instead of index/position
readString(csvString, {
worker: true,
complete: (results) => {
console.log(results)
},
header: true
})
Type Conversion
"Hey, these numbers are parsed as strings."
Everything is parsed as strings. If you want numbers and booleans, you can enable dynamic typing to do the conversion for you.
// Converts numeric/boolean data
readString(csvString, {
worker: true,
complete: (results) => {
console.log(results)
},
dynamicTyping: true
})
Error Handling
"Aw, shoot. Errors."
react-papaparse handles errors pretty well. The CSV standard is somewhat loose ambiguous, so react-papaparse is designed for edge cases. For example, mismatched fields won't break parsing.
// Example error:
{
type: 'FieldMismatch',
code: 'TooManyFields',
message: 'Expected 3 fields, but parsed 4',
row: 1
}
JSON to CSV
"Last thing: what about converting JSON to CSV?"
Use jsonToCSV()
function, passing in your array of arrays or array of objects. react-papaparse will figure it out.
// Output is a properly-formatted CSV string.
import React from 'react';
import { usePapaParse } from 'react-papaparse';
export default function JsonToCSV() {
const { jsonToCSV } = usePapaParse();
const handleJsonToCSV = () => {
const jsonData = [
{
"Column 1": "1-1",
"Column 2": "1-2",
"Column 3": "1-3",
"Column 4": "1-4"
},
{
"Column 1": "2-1",
"Column 2": "2-2",
"Column 3": "2-3",
"Column 4": "2-4"
},
{
"Column 1": "3-1",
"Column 2": "3-2",
"Column 3": "3-3",
"Column 4": "3-4"
},
{
"Column 1": 4,
"Column 2": 5,
"Column 3": 6,
"Column 4": 7
}
];
const results = jsonToCSV(jsonData);
console.log('---------------------------');
console.log('Results:', results);
console.log('---------------------------');
};
return <button onClick={() => handleJsonToCSV()}>jsonToCSV</button>;
}
CSVDownloader
"Allow to download CSV file from js object."
Just pass in the js object with an optional configuration ( setting delimiter / separator ).
import React from 'react';
import { useCSVDownloader } from 'react-papaparse';
export default function CSVDownloader() {
const { CSVDownloader, Type } = useCSVDownloader();
return (
<CSVDownloader
type={Type.Button}
filename={'filename'}
bom={true}
config={{
delimiter: ';',
}}
data={[
{
'Column 1': '1-1',
'Column 2': '1-2',
'Column 3': '1-3',
'Column 4': '1-4',
},
{
'Column 1': '2-1',
'Column 2': '2-2',
'Column 3': '2-3',
'Column 4': '2-4',
},
{
'Column 1': '3-1',
'Column 2': '3-2',
'Column 3': '3-3',
'Column 4': '3-4',
},
{
'Column 1': 4,
'Column 2': 5,
'Column 3': 6,
'Column 4': 7,
},
]}
>
Download
</CSVDownloader>
);
}
data=
can be a function that returns a data object.
<CSVDownloader
filename={'filename'}
data={() => {
return [
{
"Column 1": "1-1",
"Column 2": "1-2",
"Column 3": "1-3",
"Column 4": "1-4",
}
]}
}
>
Download
</CSVDownloader>
Install
$ npm install react-papaparse --save
yarn
$ yarn add react-papaparse --save
Comments
"I forgot to mention: my CSV files have comments in them."
Okay, first off: that's really weird. But fortunately, you can skip those lines... just specify the comment string.