Skip to main content

How to Export Excel Files from Salesforce Using ExcelJS

You might expect that creating an Excel file in Salesforce would be easy — perhaps even supported natively. But as with many things in the life of a Salesforce consultant, it’s often not that simple.

In general, you have three options for exporting Excel files:

  • Do it in the frontend
  • Do it in the backend (Apex)
  • Do it ou>sforce altogether

When to Use the Frontend Approach

The frontend (JavaScript-based) method is ideal when:

  • You don’t need to store the file in Salesforce
  • You want to avoid hitting Apex CPU or heap size limits
  • You want an easy, lightweight solution

All you need is a JavaScript file with a library like ExcelJS uploaded as a static resource. It’s straightforward, efficient, and avoids unnecessary backend processing.

When to Use the Backend Approach

If you need to store the file in Salesforce, generate it on a schedule, or integrate with server-side logic, then Apex is your go-to. Here's a good example using Apex and ZIP-based Excel file reading: Salesforce Blog: Reading Excel Files Using Apex.

External File Exports

Exporting Excel files outside of Salesforce (e.g., via middleware or external platforms) is outside the scope of this article.

Scenario

Let’s focus on the frontend case. We have an existing Excel template that contains a very complex header — so complex, in fact, that nobody wants to recreate it in code. We want to add a list of Accounts to the file and tTrigger the download from an LWC.

Step-by-Step Guide

Upload ExcelJS as a Static Resource

Download ExcelJS from npm or the GitHub repo, and upload the minified version as a static resource:

Setup > Static Resources
Upload exceljs.min.js with the name: excelJS

Upload Your Excel Template

Upload your .xlsx file as another static resource:

Setup > Static Resources
Upload your Excel template with the name: excelTemplate

Prepare Apex Controller

Create a simple Apex class ExcelExportController to fetch Account data:

public with sharing class ExcelExportController {
    @AuraEnabled(cacheable=true)
    public static List<Account> getAccounts() {
        return [SELECT Name, Industry FROM Account];
    }
}

Build the LWC Component

Let's LWC named excelExport.

In excelExport.html, we’ll simply add a button that triggers the Excel file generation.

<template>
    <lightning-button
        variant="brand"
        label="Excel Export"
        title="Excel Export"
        onclick={generateExcel}
    >
    </lightning-button> 
</template>

The button calls the generateExcel method, which needs to be implemented in excelExport.js and should perform the following steps:

  1. Load the ExcelJS library
  2. Load the Excel template
  3. Retrieve data from the Apex controller
  4. Generate the output
  5. Provide the file to the user
import { LightningElement } from 'lwc';
import { loadScript } from 'lightning/platformResourceLoader';
import excelJS from '@salesforce/resourceUrl/excelJS';
import excelTemplate from '@salesforce/resourceUrl/excelTemplate';
import getData from '@salesforce/apex/ExcelExportController.getAccounts';

export default class ExcelExport extends LightningElement {

    async generateExcel() {
        // Attach ExcelJS to the window object
        await loadScript(this, excelJS);

        // Load the excel template
        const template = await fetch(excelTemplate);
        const arrayBuffer = await template.arrayBuffer();
        const workbook = new window.ExcelJS.Workbook();
        await workbook.xlsx.load(arrayBuffer);

        // Get the first worksheet
        const worksheet = workbook.getWorksheet(1);        
        
        // Get Account data from the Apex Controller
        const data = await getData();

        // Set constants for generating output
        const offsetRows = 3;
        const columnSize = 4;
        
        // Add Account data into the Excel
        for (let i = 0; i < data.length; i++) {

            // Get row
            const row = worksheet.getRow(offsetRows + i + 1);

            // Add data into the correct columns
            row.getCell(1).value = data[i].Name;
            row.getCell(columnSize+1).value = data[i].Industry;
            row.getCell(2*columnSize+1).value = data[i].Ownership;

            // Merge cells (top right corner, button left corner)
            worksheet.mergeCells(offsetRows + i + 1,1,offsetRows + i + 1,columnSize);
            worksheet.mergeCells(offsetRows + i + 1,columnSize + 1,offsetRows + i + 1,2*columnSize);
            worksheet.mergeCells(offsetRows + i + 1,2*columnSize + 1,offsetRows + i + 1,3*columnSize);

            // Commit changes on the row
            row.commit();
        }

        // Generate Excel as blob
        const buffer = await workbook.xlsx.writeBuffer();

        // mime-type: application/zip is correct as Excel is in fact zipped
        const blob = new Blob([buffer], {
            type: 'application/zip',
        });

        // return file to the user
        const link = document.createElement('a');
        link.href = URL.createObjectURL(blob);
        link.download = 'ExcelExport.xlsx';
        link.click();
        URL.revokeObjectURL(link.href);        
    }
}

In excelExport.js-meta.xml we're going to specify, that LWC can be placed on record page.

<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>63.0</apiVersion>
    <isExposed>true</isExposed>
    <masterLabel>Excel Export</masterLabel>
    <targets>
        <target>lightning__RecordPage</target>
    </targets>
</LightningComponentBundle>

Final step

Add the excelExport component to the Account record page in the Lightning App Builder, and you’re done!

Check the final output below.

Looking for an experienced Salesforce Architect?

  • Are you considering Salesforce for your company but unsure of where to begin?
  • Planning a Salesforce implementation and in need of seasoned expertise?
  • Already using Salesforce but not fully satisfied with the outcome?
  • Facing roadblocks in your Salesforce implementation and require assistance to progress?

Feel free to review my profile and reach out for tailored support to meet your needs!

Comments

About author

My photo
Jan Binder
Experienced Salesforce Technical Architect and Team Lead with a proven track record of delivering successful enterprise projects for major global companies across diverse industries, including automotive, oil & gas, and construction.