Export to Csv Button in React Table

Export to Csv Button in React Table

Looking for a way to add an "Export to CSV" button to a react-table which is an npmjs package ().

I need to add a custom button for exporting the table data to an excel sheet in the csv or xls format?

3

4 Answers

Take a look at this npm library -

For example -

import {CSVLink, CSVDownload} from 'react-csv';

const csvData =[
  ['firstname', 'lastname', 'email'] ,
  ['John', 'Doe' , ''] ,
  ['Jane', 'Doe' , '']
];
<CSVLink data={csvData} >Download me</CSVLink>
// or
<CSVDownload data={csvData} target="_blank" />
2

Here is how integration will look like

import React from "react";
import "react-dropdown/style.css";
import "react-table/react-table.css";
import ReactTable from "react-table";
import { CSVLink } from "react-csv";

const columns = [
  {
    Header: "name",
    accessor: "name", // String-based value accessors!
  },
  {
    Header: "age",
    accessor: "age",
  },
];

class AllPostPage extends React.Component {
  constructor(props) {
    super(props);
    this.download = this.download.bind(this);
    this.state = {
      tableproperties: {
        allData: [
          { name: "ramesh", age: "12" },
          { name: "bill", age: "13" },
          { name: "arun", age: "9" },
          { name: "kathy", age: "21" },
        ],
      },
      dataToDownload: [],
    };
  }

  download(event) {
    const currentRecords = this.reactTable.getResolvedState().sortedData;
    var data_to_download = [];
    for (var index = 0; index < currentRecords.length; index++) {
      let record_to_download = {};
      for (var colIndex = 0; colIndex < columns.length; colIndex++) {
        record_to_download[columns[colIndex].Header] =
          currentRecords[index][columns[colIndex].accessor];
      }
      data_to_download.push(record_to_download);
    }
    this.setState({ dataToDownload: data_to_download }, () => {
      // click the CSVLink component to trigger the CSV download
      this.csvLink.link.click();
    });
  }

  render() {
    return (
      <div>
        <div>
          <button onClick={this.download}>Download</button>
        </div>
        <div>
          <CSVLink
            data={this.state.dataToDownload}
            filename="data.csv"
            className="hidden"
            ref={(r) => (this.csvLink = r)}
            target="_blank"
          />
        </div>
        <div>
          <ReactTable
            ref={(r) => (this.reactTable = r)}
            data={this.state.tableproperties.allData}
            columns={columns}
            filterable
            defaultFilterMethod={(filter, row) =>
              String(row[filter.id])
                .toLowerCase()
                .includes(filter.value.toLowerCase())
            }
          />
        </div>
      </div>
    );
  }
}

export default AllPostPage;

This will work with filters as well.

7

I have it implemented like this in React + Typescript (no dependency):

  /**
   * @desc get table data as json
   * @param data
   * @param columns
   */
  const getTableDataForExport = (data: any[], columns: any[]) => data?.map((record: any) => columns
.reduce((recordToDownload, column) => (
  { ...recordToDownload, [column.Header]: record[column.accessor] }
), {}));

/**
 * @desc make csv from given data
 * @param rows
 * @param filename
 */
const makeCsv = async (rows: any[], filename: string) => {
  const separator: string = ';';
  const keys: string[] = Object.keys(rows[0]);

const csvContent = `${keys.join(separator)}\n${
  rows.map((row) => keys.map((k) => {
    let cell = row[k] === null || row[k] === undefined ? '' : row[k];

    cell = cell instanceof Date
      ? cell.toLocaleString()
      : cell.toString().replace(/"/g, '""');

    if (cell.search(/("|,|\n)/g) >= 0) {
      cell = `"${cell}"`;
    }
    return cell;
  }).join(separator)).join('\n')}`;

const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
  if (navigator.msSaveBlob) { // In case of IE 10+
    navigator.msSaveBlob(blob, filename);
  } else {
    const link = document.createElement('a');
    if (link.download !== undefined) {
      // Browsers that support HTML5 download attribute
      const url = URL.createObjectURL(blob);
      link.setAttribute('href', url);
      link.setAttribute('download', filename);
      link.style.visibility = 'hidden';
      document.body.appendChild(link);
      link.click();
      document.body.removeChild(link);
    }
  }
};

the table:

<Table data={data} columns={columns} />

and the button:

  <button
    type="button"
    onClick={() => makeCsv(getTableDataForExport(data, columns), `${filename}.csv`)}
  >
    Download table data CSV
  </button>
1

I thought I'd piggyback on best wishes' extremely valuable answer with a simplified download implementation.

  export = e => {
    const currentRecords = this.ReactTable.getResolvedState().sortedData;
    this.setState({ dataToDownload: this.dataToDownload(currentRecords, columns) }, () =>
      this.csvLink.link.click()
    );
  }

  dataToDownload = (data, columns) =>
    data.map(record =>
      columns.reduce((recordToDownload, column) => {
        recordToDownload[column.Header] = record[column.accessor];
        return recordToDownload;
      }, {})
    );

I used this to allow multiple table exports in one component by adding additional export functions.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Marcus Vance
Author

Marcus Vance

Marcus Vance is a cybersecurity auditor and technology writer dedicated to educating the public about online safety, data privacy regulations, enterprise security, and emerging cyber threats.