Export CSV like a Pro in React
If you’ve ever worked on an analytical dashboard or any system dealing with standardized data, you probably already know that exporting data from such systems is one of the fundamental features. In this post, I’ll explain how you can implement CSV export in React-based applications with minimal effort.
Comma-separated values (CSV) is a text file format that usually uses commas to separate values (although some applications and tools use different separators, such as a semicolon). Data is stored in plain text, where each row represents a single record. The first row typically serves as the header (a headless file is also acceptable). Each record should contain the same number of fields, which are separated by a delimiter. An example file in this format is shown below.
name,age,employmentStatus
Fred,33,employed
John,46,unemployed
Kate,27,employed
Bob,67,retired
Undeniable advantages of this format include simplicity, human readability, relatively small file size, as well as good portability to other applications (e.g. spreadsheets). Conversion to and from other formats is relatively simple. Data equivalent in JSON format looks like:
[
{ "name": "Fred", "age": 33, "employmentStatus": "employed" },
{ "name": "John", "age": 46, "employmentStatus": "unemployed" },
{ "name": "Kate", "age": 27, "employmentStatus": "employed" },
{ "name": "Bob", "age": 67, "employmentStatus": "retired" }
]
To get CSV out of JSON we can use following code snippet (considering newest
ECMA spec
Object.keys
will preserve order of creation - there is no integer like keys). Please
also note that we don’t use quotes for fields, which would be necessary in the case
of fields containing a character that corresponds to the separator.
const csv = [Object.keys(data[0])].concat(data.map(Object.values)).join('\r\n')
In HTML world we use anchor tag not only for opening a links, but also for downloading things. By defining download property we ensure such behaviour. Optionally you can pass filename as its value (without value browser will try to guess name and extension). Another mandatory property is href which is nothing more than URL of specifc resource (not only HTTP-based). We have 3 options here:
- use static file hosted on same origin
create data URL on the fly and use data scheme
create a blob and use blob scheme
In case of static files you just need to provide its location on a server. For example I store test.csv in data folder. In such
scenario file can be downloaded from this link. Next one from the
list - Data URL - prefixed with data:
scheme allow you to embeed small files inline in document. It can be a choice in case of CSV files. By transforming our test
file into data URL we obtain:
data:text/csv;charset=utf-8,name%2Cage%2CemploymentStatus%0D%0AFred%2C33%2Cemployed%0D%0AJohn%2C46%2Cunemployed%0D%0AKate%2C27%2Cemployed%0D%0ABob%2C67%2Cretired
It consists of 4 parts: data prefix, MIME type
, charset and encoded data (in global window object you can find encodeURIComponent
function which do a job for You). Link with data URL is available
here
. You can compare both files in any editor - no difference at all! Last but not least
- blob, which in general can take a form of immutable text (including CSV) or binary
data. Dealing with blob is rather simple. We have to call its constructor with our
CSV data and right after that create string (so called object URL) by using
URL.createObjectURL
. Such a string can be passed directly ashref
attribute.
const blob = new Blob([csv], { type: 'text/csv;charset=utf-8' })
const href = URL.createObjectURL(blob)
Finally let’s move blob based solution into React environment. A few years ago I’ve created React hook called
use-exportable-csv
, which encapsulates entire procedure. All you have to do is to provide data (in JSON-like format or just 2D array). Optionally you can specify options (delimiter, headers and BOM). Hook returns link which can be used directly in anchor tag. Example implementation you can find below (in this example we fetch data from jsonplaceholder endpoint, so that link is created dynamically, just like in real world). When unomunting component link in unbound from a document.
import React, { useEffect, useState } from 'react'
import { useExportableCSV } from 'use-exportable-csv'
export default function CSVLink() {
const [data, setData] = useState([])
useEffect(() => {
fetch('https://jsonplaceholder.typicode.com/todos')
.then((res) => res.json())
.then(setData)
}, [])
const options = { bom: true }
const csvLink = useExportableCSV(data, options)
return (
<a className="hover:text-white" href={csvLink} download="data.csv">
CSV download
</a>
)
}
Thanks to Astro framework which I use for building this blog I can embeed such a component inside mdx file. Adding a little bit of styling and we get interactive result, as you can see below.
After clicking on a button, you will download a blob containig CSV file. You can now import file in any spreadsheet, or just view a content in VS Code, or whatever editor you use.
That’s all for today. I hope that you are CSV ninja now 🥷🗒️. By knowing 3 different methods you can pick the one which suits you best in your circumstances. As always thank You for reading, and I hope see you soon!