Export custom excel files, using exceljs and file-saver npm packages

Hi everyone,
as for many users i needed to export some data from my Wix site into excel spreadsheets. Some tutorials are already available for this purpose:
Export data collection to XLS excel file using code
Export any Data Collections to MS Excel using Wix Code (Video)

However, the main limitation of these solutions is the difficulty of customization, with the data being processed within an html element.

The goal of this tutorial is to show an alternative method of creating an excel spreadsheet in javascript within a Wix site, and later downloading it.


Elements and packages used in this tutorial

  • Button
  • Html element
  • Install the npm exceljs package (tested with v4.3.0)
  • create/add a Back-end web module (in my case called aModule.jsw)

The first step is to create a function in the web module that generates the custom excel file.
The exceljs library is used for this purpose, which allows to read, manipulate and write spreadsheet data and styles to XLSX and JSON.
In the package’s git repository you will find many examples on how to customize your excel sheet.

The following example shows how to add some rows and columns:

aModule.jsw

import * as ExcelJS from "exceljs/dist/exceljs.min.js";

export async function generateExcel(){
  const workbook = new ExcelJS.Workbook();
  workbook.creator = 'Me';
  workbook.lastModifiedBy = 'Her';
  workbook.created = new Date(1985, 8, 30);
  workbook.modified = new Date();
  workbook.properties.date1904 = true;

  var worksheet = workbook.addWorksheet('My Sheet');

  worksheet.columns = [
  { header: 'Id', key: 'id', width: 10 },
  { header: 'Name', key: 'name', width: 32 },
  { header: 'D.O.B.', key: 'dob', width: 32 }
  ];

  worksheet.addRow({id: 1, name: 'John Doe', dob: new Date(1970,3,4)});
  worksheet.addRow({id: 2, name: 'Jane Doe', dob: new Date(1980,7,12)});

  let buffer = await workbook.xlsx.writeBuffer();
  return buffer
}

This function returns a buffer containing all the data in the excel sheet.


The next step is to insert the code into the html element that allows you to download the file. The file-saver package is used for this purpose, which is imported directly into the html element.

html element code:

<html>
<head>
<script src="https://unpkg.com/file-saver@2.0.5/dist/FileSaver.js"></script>
<script>
    window.onmessage = function(event){
      if(event.data){
        var buffer = event.data.buffer;
        const fileExtension = event.data.extension;
        const fileType = event.data.type;
        const fileName = event.data.filename;
		
        const blob = new Blob([buffer], {type: fileType});
        saveAs(blob, fileName + fileExtension);
      }
  };
</script>
</head>
<body>
</body>
</html>

Finally we need to import the web form into the code of the wix page, and send the data to be downloaded to the html element.

wix page code:

import {generateExcel} from 'backend/aModule.jsw'

$w.onReady(function () {
    // Write your JavaScript here
    // To select an element by ID use: $w('#elementID')
    $w('#button1').onClick(async()=>{
        let buffer = await generateExcel()
        let filename = "export";
        let extension = ".xlsx"
        let type = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
        $w('#html1').postMessage({"buffer": Uint8Array.from(buffer.data), "filename": filename, "extension": extension,"type": type})
    })
});

Bonus
The file-saver package allows you to save different types of files by specifying their extension and type.
An example for exporting a text file is:

$w('#button1').onClick(async()=>{
        let buffer = "Hello World!"
        let filename = "exportText";
        let extension = ".txt"
        let type = "text/plain;charset=utf-8";
        $w('#html1').postMessage({"buffer": buffer, "filename": filename, "extension": extension,"type": type})
    })

Please feel free to comment for any improvements or suggestions.
#excel #exceljs #filesaver #npm

3 Likes

Thanks for the great solution!
Here is a way to implement it universally for any database.

wix page code:


import { generateExcel } from 'backend/aModule.jsw'
import wixData from 'wix-data';

export async function button122_click(event) {
    let tt = await wixData.query("mydata").find()
    let data = tt.items
    let buffer = await generateExcel(data)
    let filename = "export";
    let extension = ".xlsx"
    let type = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
    $w('#html1').postMessage({ "buffer": Uint8Array.from(buffer.data), "filename": filename, "extension": extension, "type": type })
}


aModule.jsw


import ExcelJS from 'exceljs'

export async function generateExcel(data){
  const workbook = new ExcelJS.Workbook();
  workbook.creator = 'Me';
  workbook.lastModifiedBy = 'Her';
  workbook.created = new Date(1985, 8, 30);
  workbook.modified = new Date();
  workbook.properties.date1904 = true;

  var worksheet = workbook.addWorksheet('My Sheet');
  let cals = []
let item = data[0]
for (const key in item) {
     cals.push({header: key, key:key, width: 15})  
    }
  worksheet.columns =cals
for (const iterator of data) {
     worksheet.addRow(iterator)
}
  let buffer = await workbook.xlsx.writeBuffer();
  return buffer
}


I love this solution and have been putting it to great use.

However, in the last ~24 hours or so, I can no longer download my data to a spreadsheet. I get these error messages when I click the Download button.

Error: Error loading web module backend/excelJS.jsw: Cannot find module 'node:events'
Error loading web module backend/excelJS.jsw: Cannot find module 'node:events'
Require stack:
- /dynamic-modules/third-party-npm/07569248/WC_BEGIN/minipass/7.0.1/WC_END/minipass/dist/cjs/index.js
- /dynamic-modules/third-party-npm/07569248/WC_BEGIN/path-scurry/1.10.1/WC_END/path-scurry/dist/cjs/index.js
- /dynamic-modules/third-party-npm/07569248/WC_BEGIN/glob/10.1.0/WC_END/glob/dist/cjs/glob.js
- /dynamic-modules/third-party-npm/07569248/WC_BEGIN/glob/10.1.0/WC_END/glob/dist/cjs/index.js
- /dynamic-modules/third-party-npm/07569248/WC_BEGIN/archiver-utils/2.1.0/WC_END/archiver-utils/file.js
- /dynamic-modules/third-party-npm/07569248/WC_BEGIN/archiver-utils/2.1.0/WC_END/archiver-utils/index.js
- /dynamic-modules/third-party-npm/07569248/WC_BEGIN/archiver/5.3.1/WC_END/archiver/lib/core.js
- /dynamic-modules/third-party-npm/07569248/WC_BEGIN/archiver/5.3.1/WC_END/archiver/index.js
- /dynamic-modules/third-party-npm/07569248/WC_BEGIN/exceljs/4.3.0/WC_END/exceljs/lib/stream/xlsx/workbook-writer.js
- /dynamic-modules/third-party-npm/07569248/WC_BEGIN/exceljs/4.3.0/WC_END/exceljs/lib/exceljs.nodejs.js
- /dynamic-modules/third-party-npm/07569248/WC_BEGIN/exceljs/4.3.0/WC_END/exceljs/excel.js
- /user-code/backend/excelJS.jsw
- /user-code/stubmodule-that-does-the-require.js
- /cloud-runtime-code/node_modules/scoped-require/index.js
- /cloud-runtime-code/packages/elementory/cloud-runtime/factories.js
- /cloud-runtime-code/packages/elementory/cloud-runtime/create-app.js
- /cloud-runtime-code/packages/elementory/cloud-runtime/cloud-grid-runner.js

Has anyone else encountered this issue? Any pointers on how to resolve it?

Thanks!

Hey. I’m encountering the exact same issue. Do you have any insight into why this is happening? I’ve noticed that removing the exceljs import allows the rest of the module to work.

Hi, thanks for pointing out the error. I’m actually encountering the same issue, I’ll update the tutorial if I find the cause and a solution.

Hey! Thanks for looking into this. I’d love to know the fix as well. More importantly, I’d like to know why this happened in the first place.

I don’t understand why it is happening, but I need to consider an alternative implementation. My users are complaining!

Hi everyone, I found the solution.

You need just to change the way to import the exceljs npm module:

Change

import ExcelJS from 'exceljs' 

as

import * as ExcelJS from "exceljs/dist/exceljs.min.js"; 

in your backend module.

I added a comment with the solution, hope it works.

It works beautifully, Qu4dri. Thank you so much for your help!

This is an excellent solution! Thank you. I was considering implementing a .csv solution instead. Do you have any idea why this might have happened? Should I change the way I import other packages as well?

Hi, I’m glad it worked.
But I’m an amateur in JavaScript coding, therefore I’m not sure about the exact cause.
I don’t know what to suggest you for the other imports, this is the only npm package I’m actually using. As long as they work I would keep them as they are.