Opening a spreadsheet, fixing a few cells, and downloading the result sounds like a small task. Building a browser application that handles that workflow involves more than putting an editable grid on a page.
An Excel workbook carries formulas, formatting, merged cells, multiple sheets, and sometimes charts or pivot tables. Those details need to survive the trip through the editor.
I'm building Workbook Editor, a browser-based spreadsheet editor built with Next.js, React, and Univer. This article covers three parts of its implementation: file conversion, loading the editor, and recovering local work.
The architecture: separate the grid from the file format
The project gives different libraries different jobs:
| Layer | Responsibility |
|---|---|
| Next.js and React | Pages, navigation, and application UI |
| Univer | The spreadsheet editing experience |
| ExcelJS | The main XLSX import and export path |
| SheetJS | The XLS, CSV, and TSV conversion path |
| IndexedDB | Local recovery snapshots and version history |
Univer provides the interactive workbook: cells, selection, formatting controls, and spreadsheet operations. The application registers presets for features such as sorting, filtering, data validation, and find and replace.
The file conversion layer connects that editing model to the files people bring with them.
For XLSX files, the basic flow is:
Local file
β
ExcelJS workbook
β
Application adapter
β
Univer workbook
β
User edits
β
Application adapter + export preservation steps
β
Downloaded XLSX file
Keeping conversion in a separate module makes the boundary explicit. UI code can work with the editor model, while the adapter handles differences in how workbook features are represented.
Importing values is only the beginning
A useful adapter has to translate more than cell contents.
The XLSX adapter handles values and formulas alongside fonts, fills, borders, number formats, alignment, merged cells, row heights, column widths, and sheet metadata.
Even dimensions require conversion. The implementation translates column widths into pixel dimensions for the editor and converts them back during export. Those calculations are approximations: a workbook's appearance also depends on font metrics and rendering behavior.
This is why βthe file openedβ is a narrow measure of compatibility. The more useful question is what happens after someone opens a workbook, edits it, exports it, and opens the result again.
The project also uses a separate adapter for XLS, CSV, and TSV. These formats have different capabilities, so the conversion paths have different expectations. CSV, for example, cannot carry a workbook's styling or multiple worksheets.
Preserve workbook parts outside the main conversion path
Some Excel features need handling beyond the main ExcelJS conversion path.
The project includes a preservation layer for native chart, pivot, and shape parts. During import, it captures relevant parts from the original XLSX package and stores serialized information with the workbook's resources. During export, it attempts to restore those parts into the generated package.
That adds a second path alongside normal cell conversion:
Original XLSX parts β captured resources β export restoration
Preserving a feature and editing it are separate capabilities. Keeping an original workbook part does not automatically give the browser editor complete control over that feature.
Restoration also has conditions. Sheet changes and drawing conflicts can affect whether an original part can be restored. This approach extends compatibility, but it does not establish complete Excel fidelity.
Load the spreadsheet engine when it is needed
The application includes both content pages and an interactive editor. They have different loading needs.
The editor component uses a client-side dynamic import with server rendering disabled. Its loading state displays a message and navigation links while the interactive component loads.
There is another asynchronous boundary inside the editor: Univer setup lives in a dedicated factory module, which the editor imports dynamically.
That module centralizes the engine's presets, styles, and locale configuration. The separation is intended to keep the engine's dependencies apart from frequently changed application code and improve opportunities for browser caching.
It is a loading strategy, not a performance measurement. Bundle output and cache behavior still need inspection to establish what a particular release actually delivers.
Keep recovery data in the browser
For the local-file editing path, the application reads file bytes in the browser and converts them into the editor's workbook model.
Recovery storage follows the same local approach. The application stores a current snapshot and a separate version history in IndexedDB.
Versions include metadata such as their timestamp, source, and affected sheets. The code distinguishes automatic versions, manual versions, and versions captured before importing content or restoring an earlier state.
History also needs limits. The retention policy keeps recent versions, thins older entries into time buckets, and applies count and storage caps. Users can pin a limited number of versions.
These snapshots help recover work in the same browser. Exporting a file remains useful for keeping a separate copy, since browser storage can be cleared.
Small tools alongside the full editor
The project also includes focused spreadsheet utilities: removing duplicate rows, trimming whitespace, comparing files, and converting between formats such as XLSX, JSON, and Markdown.
Those tools provide shorter paths for tasks that do not require a full editing session. Someone who only needs a format conversion can start with that operation directly.
What this project has taught me
The visible grid is one part of a spreadsheet application. Much of the implementation lives at its boundaries: converting workbook models, preserving file parts, loading large dependencies, and retaining enough local history to recover from mistakes.
Separating those responsibilities makes the application easier to reason about. It also makes compatibility limits easier to describe precisely.
You can try the project at workbookeditor.com.
If you've built spreadsheet tooling, which workbook feature has been the hardest to preserve through an import-and-export cycle?
Top comments (0)