Export to CSV button in react table

107,179

Solution 1

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.

Solution 2

Take a look at this npm library - https://www.npmjs.com/package/react-csv

For example -

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

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

Solution 3

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>

Solution 4

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.

Share:
107,179

Related videos on Youtube

Zinngg
Author by

Zinngg

A code blooded magician. Looking for opportunities to learn and grow.

Updated on March 09, 2022

Comments

  • Zinngg
    Zinngg over 2 years

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

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

    • raj
      raj almost 6 years
      How did you get the data that is present inthe table(like after applying filters). I want to export the data present after applying filters and not the whole original data supplied
    • raj
      raj almost 6 years
      Well i got it working finally via setting a ref in the react table insatance and retreiving the current data through this.reactTable.getResolvedState().sortedData
    • xargr
      xargr over 4 years
      you can use a simple function to trigger download gist.github.com/xargr/97f160e5ab1bbc513bc7a1acd4ed88e4
  • DrMeers
    DrMeers almost 6 years
    ...and use react-table documented advice on this.reactTable.getResolvedState() for getting the data prop
  • timdewolf
    timdewolf over 5 years
    Thank you for the example of how to make the link into a button.
  • Himanshu Tanwar
    Himanshu Tanwar over 5 years
    download method can be simplified by seperating headers and data. See this link. You can map column header to header ids and pass it to CSVLink component as header prop, and sortedData from table ref as data prop.
  • best wishes
    best wishes about 5 years
    it should, just try once.
  • Finesse
    Finesse almost 5 years
    You don't need react-csv, it only makes things more complicated. Use export-to-csv instead.
  • Andre DiCioccio
    Andre DiCioccio over 4 years
    Thank you for fleshing out the example like you have done.
  • Chizl
    Chizl about 3 years
    Doesn't seem to work that way anymore:: "error Arrow function should not return assignment no-return-assign" Happened on "ref={(r) => this.csvLink = r}"
  • best wishes
    best wishes about 3 years
  • Vivek S
    Vivek S over 2 years
    This package does not work on async calls.
  • emmaakachukwu
    emmaakachukwu about 2 years
    Nice. For anyone wondering what columns should look like, it looks like this: const columns = [ { Header: "first name", accessor: "first_name", }, { Header: "last name", accessor: "last_name", }, ]; The accessor is your value key from your data array.