├── .npmignore ├── .babelrc ├── .gitignore ├── src ├── interfaces │ ├── context.ts │ ├── use-excel.ts │ └── react-export-table-excel.ts ├── index.tsx ├── hooks │ └── useExcel.ts ├── utils │ ├── index.ts │ └── create-table.tsx └── lib.ts ├── tsconfig.json ├── package.json └── README.md /.npmignore: -------------------------------------------------------------------------------- 1 | src/ 2 | node_modules/ 3 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/preset-typescript"] 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .vscode/ 3 | .DS_Store 4 | lib/ 5 | 6 | .npmignore -------------------------------------------------------------------------------- /src/interfaces/context.ts: -------------------------------------------------------------------------------- 1 | export interface Context { 2 | worksheet: string; 3 | table: string; 4 | } 5 | 6 | export type ContextTypes = "worksheet" | "table"; 7 | -------------------------------------------------------------------------------- /src/interfaces/use-excel.ts: -------------------------------------------------------------------------------- 1 | export interface UseExcel { 2 | filename: string; 3 | sheet?: string; 4 | currentTableRef: any; 5 | } 6 | 7 | export interface ExcelReturn { 8 | onDownload: () => boolean; 9 | } 10 | -------------------------------------------------------------------------------- /src/interfaces/react-export-table-excel.ts: -------------------------------------------------------------------------------- 1 | import { ReactElement } from "react"; 2 | import { UseExcel } from "./use-excel"; 3 | 4 | export interface IProps extends UseExcel { 5 | children: ReactElement | ReactElement[]; 6 | } 7 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES6", 4 | "lib": ["DOM", "ES6", "DOM.Iterable", "ScriptHost", "ES2016.Array.Include"], 5 | "module": "commonjs", 6 | "declaration": true, 7 | "outDir": "./lib", 8 | "strict": true, 9 | "jsx": "react", 10 | "esModuleInterop": true 11 | }, 12 | "include": ["src"], 13 | "exclude": ["node_modules", "**/__tests__/*"] 14 | } 15 | -------------------------------------------------------------------------------- /src/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from "react"; 2 | import { IProps } from "./interfaces/react-export-table-excel"; 3 | import { useDownloadExcel } from "./hooks/useExcel"; 4 | import { downloadExcel } from "./lib"; 5 | 6 | const DownloadTableExcel: FC = ({ 7 | currentTableRef, 8 | filename, 9 | sheet, 10 | children, 11 | }) => { 12 | const { onDownload } = useDownloadExcel({ currentTableRef, filename, sheet }); 13 | return {children}; 14 | }; 15 | 16 | export { DownloadTableExcel, useDownloadExcel, downloadExcel }; 17 | -------------------------------------------------------------------------------- /src/hooks/useExcel.ts: -------------------------------------------------------------------------------- 1 | import { UseExcel } from "../interfaces/use-excel"; 2 | import { useEffect, useMemo, useState } from "react"; 3 | import { excel } from "../lib"; 4 | 5 | export function useDownloadExcel({ 6 | currentTableRef, 7 | filename, 8 | sheet, 9 | }: UseExcel) { 10 | const [payload, setPayload] = useState({} as UseExcel); 11 | 12 | useEffect(() => { 13 | setPayload({ 14 | currentTableRef, 15 | filename, 16 | sheet, 17 | }); 18 | }, [currentTableRef, filename, sheet]); 19 | 20 | return useMemo(() => excel(payload), [payload]); 21 | } 22 | -------------------------------------------------------------------------------- /src/utils/index.ts: -------------------------------------------------------------------------------- 1 | import { Context, ContextTypes } from "../interfaces/context"; 2 | 3 | export function base64(uriComponent: string | number | boolean) { 4 | return window.btoa(unescape(encodeURIComponent(uriComponent))); 5 | } 6 | 7 | export function format(s: string, c: Context) { 8 | return s.replace(/{(\w+)}/g, (_: string, p: ContextTypes) => c[p]); 9 | } 10 | 11 | export const uri = "data:application/vnd.ms-excel;base64,"; 12 | export const template = 13 | '{table}"; 19 | 20 | export { default as createTable } from "./create-table"; 21 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-export-table-to-excel", 3 | "version": "1.0.6", 4 | "description": "It allows you to export an HTML table just by sending the table reference and the name with which you want the file to be saved", 5 | "main": "lib/index.js", 6 | "types": "lib/index.d.ts", 7 | "private": false, 8 | "repository": { 9 | "type": "git", 10 | "url": "https://github.com/EdisonJpp/react-export-table-to-excel" 11 | }, 12 | "keywords": [ 13 | "react", 14 | "excel", 15 | "table", 16 | "html", 17 | "xls" 18 | ], 19 | "homepage": "https://github.com/EdisonJpp/react-export-table-to-excel", 20 | "scripts": { 21 | "test": "echo \"Error: no test specified\" && exit 1", 22 | "build": "tsc", 23 | "start:dev": "react-scripts start", 24 | "publish:npm": "rm -rf lib && npm run build && npm publish --access public" 25 | }, 26 | "author": "Edison J. Padilla && Enmanuel Santana", 27 | "license": "ISC", 28 | "devDependencies": { 29 | "@babel/preset-typescript": "^7.18.6", 30 | "@types/node": "^18.6.4", 31 | "@types/react": "^18.0.15", 32 | "@types/react-dom": "^18.0.6", 33 | "react": "^18.2.0", 34 | "react-dom": "^18.2.0", 35 | "react-scripts": "^5.0.1", 36 | "typescript": "^4.7.4" 37 | }, 38 | "browserslist": { 39 | "production": [ 40 | ">0.2%", 41 | "not dead", 42 | "not op_mini all" 43 | ], 44 | "development": [ 45 | "last 1 chrome version", 46 | "last 1 firefox version", 47 | "last 1 safari version" 48 | ] 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/utils/create-table.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { renderToString } from "react-dom/server"; 3 | 4 | const acceptTypes = ["string", "number", "boolean"]; 5 | 6 | export interface ITablePayload { 7 | header: string[]; 8 | body: 9 | | Array<{ [key: string]: string | number | boolean }> 10 | | Array<(string | number | boolean)[]>; 11 | } 12 | 13 | export default function createTable({ header, body }: ITablePayload) { 14 | const headers = ( 15 | 16 | {header.map((head) => ( 17 | {head} 18 | ))} 19 | 20 | ); 21 | 22 | const bodies = body.map((val, i) => { 23 | if (Array.isArray(val)) { 24 | return ( 25 | 26 | {val.map((value, i) => ( 27 | {value} 28 | ))} 29 | 30 | ); 31 | } 32 | 33 | if (val !== null && typeof val === "object") { 34 | return ( 35 | 36 | {Object.entries(val).map(([key, value], i) => { 37 | if (typeof value === "object") { 38 | console.error( 39 | `typeof ${key} is incorrect, only accept ${acceptTypes.join( 40 | ", " 41 | )} ` 42 | ); 43 | 44 | return ; 45 | } 46 | 47 | return ( 48 | 49 | <>{value} 50 | 51 | ); 52 | })} 53 | 54 | ); 55 | } 56 | 57 | console.error( 58 | ` 59 | data structure is incorrect, 60 | data structure type -> 61 | " type data = Array<{ [key: string]: string | number | boolean }> 62 | or 63 | type data = Array<(string | number | boolean)[]>" 64 | ` 65 | ); 66 | 67 | return null; 68 | }); 69 | 70 | return renderToString( 71 | 72 | 73 | {headers} 74 | {bodies} 75 | 76 |
77 | ); 78 | } 79 | -------------------------------------------------------------------------------- /src/lib.ts: -------------------------------------------------------------------------------- 1 | import * as utils from "./utils"; 2 | 3 | import { UseExcel, ExcelReturn } from "./interfaces/use-excel"; 4 | import { Context } from "./interfaces/context"; 5 | import { ITablePayload } from "./utils/create-table"; 6 | 7 | function valEnv(): boolean { 8 | if (!document) { 9 | if (process?.env.NODE_ENV !== "production") { 10 | console.error("Failed to access document object"); 11 | } 12 | return false; 13 | } 14 | return true; 15 | } 16 | 17 | function download(fileName: string, context: Context): boolean { 18 | const element = window.document.createElement("a"); 19 | element.href = 20 | utils.uri + utils.base64(utils.format(utils.template, context)); 21 | element.download = fileName; 22 | document.body.appendChild(element); 23 | element.click(); 24 | document.body.removeChild(element); 25 | return true; 26 | } 27 | 28 | function getTable(currentTableRef?: any, tablePayload?: ITablePayload) { 29 | if (currentTableRef) { 30 | const cloneTable = currentTableRef.cloneNode(true); 31 | return cloneTable.outerHTML; 32 | } 33 | if (tablePayload) return utils.createTable(tablePayload); 34 | 35 | console.error("currentTableRef or tablePayload does not exist"); 36 | } 37 | 38 | function handleDownload( 39 | { 40 | fileName, 41 | sheet, 42 | tablePayload, 43 | }: { fileName: string; sheet: string; tablePayload?: ITablePayload }, 44 | currentTableRef?: HTMLElement 45 | ) { 46 | if (!valEnv()) return false; 47 | 48 | const table = getTable(currentTableRef, tablePayload); 49 | const context: Context = { 50 | worksheet: sheet || "Worksheet", 51 | table, 52 | }; 53 | 54 | return download(fileName, context); 55 | } 56 | 57 | function excel({ currentTableRef, filename, sheet }: UseExcel): ExcelReturn { 58 | function onDownload(): boolean { 59 | if (!valEnv()) return false; 60 | 61 | const table = getTable(currentTableRef); 62 | const fileName = `${filename}.xls`; 63 | 64 | const context: Context = { 65 | worksheet: sheet || "Worksheet", 66 | table, 67 | }; 68 | 69 | return download(fileName, context); 70 | } 71 | 72 | return { onDownload }; 73 | } 74 | 75 | export { excel, handleDownload as downloadExcel }; 76 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ReactExportTableToExcel 2 | 3 | Provides a client side generation of Excel (.xls) file from HTML table element. 4 | 5 | [![NPM](https://nodei.co/npm/react-export-table-to-excel.png)](https://npmjs.org/package/react-export-table-to-excel) 6 | 7 | --- 8 | 9 | #### No additional dependencies 10 | 11 | --- 12 | 13 | #### [ReactExportTableToExcel's samples in React.js ( CodeSandbox )](https://codesandbox.io/s/react-export-table-to-excel-sample-dvzkms) 14 | 15 | #### [ReactExportTableToExcel's samples in Next.js ( CodeSandbox )](https://codesandbox.io/s/react-export-table-to-excel-next-js-yzl74q) 16 | 17 | --- 18 | 19 | ## Installation 20 | 21 | ``` 22 | npm install react-export-table-to-excel 23 | yarn add react-export-table-to-excel 24 | ``` 25 | 26 | ## Features 27 | 28 | - Download HTML table as Excel file in .xls format 29 | - No server side code 30 | - Set desired .xls filename and sheet 31 | - Hook to export to excel 32 | - Component to export to excel 33 | - Method to export to excel 34 | 35 | ## Options 36 | 37 | - #### Component 38 | 39 | A list of available properties can be found below. These must be passed to the containing `DownloadTableExcel` component. 40 | 41 | | Property | Type | Description | 42 | | ------------------- | -------------- | ---------------------------------------------------------------------------------------------- | 43 | | **filename** | _string_ | Name of Excel file. | 44 | | **sheet** | _string_ | Name of Excel sheet. | 45 | | **children** | _ReactElement_ | component that will obtain the onClick event to export to excel (the most common is a button). | 46 | | **currentTableRef** | _HTMLElement_ | the current of the table reference. | 47 | 48 | #### Example 49 | 50 | ```javascript 51 | import React, {useRef} from 'react'; 52 | import { DownloadTableExcel } from 'react-export-table-to-excel'; 53 | 54 | const Test = () => { 55 | const tableRef = useRef(null); 56 | 57 | return ( 58 |
59 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 |
FirstnameLastnameAge
EdisonPadilla20
AlbertoLopez94
88 | 89 |
90 | ); 91 | } 92 | } 93 | 94 | export default Test 95 | ``` 96 | 97 | - #### Hook 98 | 99 | A list of available properties can be found below. These must be passed to the containing `useDownloadExcel` hook. 100 | 101 | | Property | Type | Description | 102 | | ------------------- | ------------- | ----------------------------------- | 103 | | **filename** | _string_ | Name of Excel file. | 104 | | **sheet** | _string_ | Name of Excel sheet. | 105 | | **currentTableRef** | _HTMLElement_ | the current of the table reference. | 106 | 107 | #### Example 108 | 109 | ```javascript 110 | import React, {useRef} from 'react'; 111 | import { useDownloadExcel } from 'react-export-table-to-excel'; 112 | 113 | const Test = () => { 114 | const tableRef = useRef(null); 115 | 116 | const { onDownload } = useDownloadExcel({ 117 | currentTableRef: tableRef.current, 118 | filename: 'Users table', 119 | sheet: 'Users' 120 | }) 121 | 122 | return ( 123 |
124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 |
FirstnameLastnameAge
EdisonPadilla20
AlbertoLopez94
145 | 146 |
147 | ); 148 | } 149 | } 150 | 151 | export default Test 152 | 153 | ``` 154 | 155 | - #### Method 156 | 157 | A list of available properties can be found below. These must be passed to the downloadExcel method. 158 | 159 | | Property | Type | Description | 160 | | ---------------- | -------- | -------------------------- | 161 | | **filename** | _string_ | Name of Excel file. | 162 | | **sheet** | _string_ | Name of Excel sheet. | 163 | | **tablePayload** | _object_ | payload to download excel. | 164 | 165 | #### Example 166 | 167 | ```javascript 168 | import React from "react"; 169 | import { downloadExcel } from "react-export-table-to-excel"; 170 | 171 | const Test = () => { 172 | const header = ["Firstname", "Lastname", "Age"]; 173 | const body = [ 174 | ["Edison", "Padilla", 14], 175 | ["Cheila", "Rodrigez", 56], 176 | ]; 177 | 178 | /** 179 | * @description: 180 | * also accepts an array of objects; the method (downloadExcel) will take 181 | * as order of each column, the order that each property of the object brings with it. 182 | * the method(downloadExcel) will only take the value of each property. 183 | */ 184 | const body2 = [ 185 | { firstname: "Edison", lastname: "Padilla", age: 14 }, 186 | { firstname: "Cheila", lastname: "Rodrigez", age: 56 }, 187 | ]; 188 | 189 | function handleDownloadExcel() { 190 | downloadExcel({ 191 | fileName: "react-export-table-to-excel -> downloadExcel method", 192 | sheet: "react-export-table-to-excel", 193 | tablePayload: { 194 | header, 195 | // accept two different data structures 196 | body: body || body2, 197 | }, 198 | }); 199 | } 200 | 201 | return ( 202 |
203 | 204 | 205 | 206 | 207 | 208 | {header.map((head) => ( 209 | 210 | ))} 211 | 212 | 213 | {body.map((item, i) => ( 214 | 215 | {item.map((it) => ( 216 | 217 | ))} 218 | 219 | ))} 220 | 221 |
{head}
{it}
222 |
223 | ); 224 | }; 225 | 226 | export default Test; 227 | ``` 228 | --------------------------------------------------------------------------------