Preview an XLSX file in Angular
To preview an XLSX file in Angular, serve the EatSheet worker as an asset and pass it to openWorkbook. This step matters: without it, a production build fails with WORKER_FAILED, even if the development server works.
1. Install it
npm install eatsheet2. Serve the worker as an asset
In
angular.json, add this to the build target's assets list:{ "glob": "**/*.js", "input": "node_modules/eatsheet/lib", "output": "eatsheet" }3. Add the styles
Add this line to
src/styles.css:@import 'eatsheet/eatsheet.css';4. Add src/app/excel-preview.ts
import { Component, ElementRef, effect, input, signal, viewChild } from '@angular/core';
import { openWorkbook } from 'eatsheet/worker';
@Component({
selector: 'excel-preview',
template: `
@if (error(); as failure) {
<p role="alert">Couldn't open this file: {{ failure.code ?? failure.message }}</p>
}
<div #box tabindex="0" [hidden]="error()" style="height: 480px; overflow: auto"></div>
`,
})
export class ExcelPreview {
file = input.required<File>();
box = viewChild.required<ElementRef<HTMLElement>>('box');
error = signal<any>(null);
constructor() {
effect(onCleanup => {
const file = this.file();
const element = this.box().nativeElement;
let book: any, gone = false, frame = 0;
this.error.set(null);
const draw = async () => {
const { html } = await book.render(0, { offset: element.scrollTop });
if (!gone) element.innerHTML = html; // EatSheet escapes cell content
};
const onScroll = () => {
cancelAnimationFrame(frame);
frame = requestAnimationFrame(() => draw().catch(failure => this.error.set(failure)));
};
// Angular's build doesn't copy the worker from node_modules, so it's served as an asset
const worker = new Worker('/eatsheet/worker-entry.js', { type: 'module' });
openWorkbook(file, { worker }).then(opened => {
if (gone) return opened.close();
book = opened;
element.addEventListener('scroll', onScroll);
return draw();
}).catch(failure => { if (!gone) this.error.set(failure); });
onCleanup(() => {
gone = true;
cancelAnimationFrame(frame);
element.removeEventListener('scroll', onScroll);
book?.close();
});
});
}
}5. Use it with a file input
import { Component, signal } from '@angular/core';
import { ExcelPreview } from './excel-preview';
@Component({
selector: 'app-root',
imports: [ExcelPreview],
template: `
<input type="file" accept=".xlsx,.csv,.tsv" (change)="choose($event)">
@if (file(); as chosen) {
<excel-preview [file]="chosen" />
}
`,
})
export class App {
file = signal<File | null>(null);
choose(event: Event) {
this.file.set((event.target as HTMLInputElement).files?.[0] ?? null);
}
}The component sets
innerHTML on the element directly because Angular's [innerHTML] binding removes inline styles, and EatSheet's output uses them. If your app isn't at the site root, change the worker's address to match.Tested on November 7, 2026 with EatSheet 0.2.3 and Angular 22, using ng serve and ng build: a valid file, a corrupt file, then a valid file again.
More: SheetJS alternative · SheetJS vs ExcelJS vs EatSheet · Show an uploaded Excel file · React · Vue · Next.js · Svelte · Electron · Troubleshooting · Security · Docs · Benchmarks