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 as href 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!