vue-canvas-sheet
v1.0.0
Published
A high-performance, modular Canvas-based spreadsheet engine with WASM-powered calculation.
Maintainers
Readme
🚀 Vue Canvas Sheet
A high-performance, Canvas-rendered Vue 3 spreadsheet component with a shared JS formula engine and optional Rust + WASM acceleration for verified numeric formulas. Built for million-cell workloads with an Excel-grade UX on the web.
中文文档 · Usage Guide · Live Demo
✨ Highlights
- ⚡ Canvas Cluster Rendering — Pure Canvas 2D pipeline, 60 FPS scrolling at 100k+ rows with zero DOM overhead.
- 🦀 Rust + WASM Engine — Accelerates the verified numeric-arithmetic subset; references, functions, strings, booleans, comparisons, and unsupported syntax use the shared JS RPN engine.
- 🧵 Worker + SharedArrayBuffer — Calculation lives off the UI thread; the interface stays responsive under heavy load.
- 💾 IndexedDB Persistence — Diff-based incremental save with instant "cold start" reload via
enable-persistence. - 🧩 Headless-Friendly Core —
Workbookruns standalone in Node.js or Web Workers; UI and core are fully decoupled. - 🤝 Realtime Collaboration — Built-in plugin for shared cursors, selection sync, and per-cell edit locks over WebSocket.
- 📥 Import / Export —
.xlsx / .xls / .csv / .tsv / .jsonround-trip via dedicated plugins (Excel export runs in a worker). - 🌈 Excel-Grade Features — 100+ formulas, merged cells, frozen panes, conditional formatting, formula bar, undo/redo, find & replace.
📊 Benchmarks
| Workload (50,000 rows / 200,000 cells) | Time | Notes |
| :--- | :--- | :--- |
| Initial load (setData) | ~200 ms | Includes parsing 50k dynamic formulas |
| First paint | ~16 ms | Hardware-accelerated Canvas, 60 FPS scroll |
📦 Install
npm install vue-canvas-sheet
# or
pnpm add vue-canvas-sheet
# or
yarn add vue-canvas-sheetThe Rust/WASM engine is pre-built and shipped with the package — no extra toolchain required.
Peer dependency: vue ^3.5.0.
🚀 Quick Start
<template>
<div style="height: 600px; width: 100%;">
<TableDesigner
ref="table"
:initial-data="initialData"
enable-persistence
sheet-id="my-demo-sheet"
/>
</div>
</template>
<script setup>
import { TableDesigner } from 'vue-canvas-sheet';
import 'vue-canvas-sheet/style.css';
const initialData = [
['Name', 'Price', 'Qty', 'Total'],
['Product A', 10, 5, { f: '=B2*C2' }],
['Product B', 20, 3, { f: '=B3*C3' }],
['Total', { f: '=SUM(B2:B3)' }, { f: '=SUM(C2:C3)' }, { f: '=SUM(D2:D3)' }],
];
</script>
TableDesignerfills its parent — make sure the parent has an explicit height.
🧩 Plugins
All plugins are factory functions exported from the package root.
import {
TableDesigner,
createSelectionHistoryPlugin,
createAutoSavePlugin,
createExportPlugin,
createImportPlugin,
createCollaborativeCursorPlugin,
createRealtimeCollaborationPlugin,
} from 'vue-canvas-sheet';| Plugin | Purpose |
| --- | --- |
| SelectionHistoryPlugin | Forward/back navigation across selection history |
| AutoSavePlugin | Event-driven or interval autosave to IndexedDB / localStorage |
| ExportPlugin | Export to .xlsx (worker-backed), .csv, .json |
| ImportPlugin | Import .xlsx / .xls / .csv / .tsv / .json |
| CollaborativeCursorPlugin | Render remote users' cursors and selections |
| RealtimeCollaborationPlugin | Sync edits, selections, and per-cell locks via WebSocket |
CSV export prefixes formula-like text with a single quote by default. Use exportCSV({ allowFormulas: true }) only for trusted data that must retain spreadsheet formula semantics.
<TableDesigner :plugins="[historyPlugin, exportPlugin]" :initial-data="data" />A reference WebSocket server is included — run npm run collab:server to try collaboration locally.
🛠 Workbook API (excerpt)
Grab the underlying Workbook from the component ref:
const wb = tableRef.value.workbook;
// Cells
wb.setCell(0, 0, { v: 100 });
wb.setCell(0, 1, { f: '=A1*2' });
const cell = wb.getCell(0, 1);
const value = wb.getCellValue(0, 1);
// Selection & styling
wb.setSelection(0, 0, 5, 5);
wb.setStyle(range, { bold: true, color: '#f00' });
wb.setBorder(range, 'all', '#000', 'solid');
// Structure
wb.mergeCells(range);
wb.setFreeze(1, 1);
wb.undo(); wb.redo();
// Engine
wb.registerFunction('DOUBLE', value => value * 2);
wb.setCell(0, 2, { f: '=DOUBLE(A1)' });
wb.enableWorker();
wb.recalcAll({ useWorker: true });
wb.getSystemReport();
// Events
const off = wb.on('cell-change', (changes) => { /* ... */ });
off();See the documentation hub for the complete reference (usage, API, plugins, formulas).
⚡ Performance Tips
Large dataset, main-thread vectorized mode — skip worker serialization overhead:
table.value.workbook.recalcAll({ useWorker: false });Lazy loading for 50k+ rows:
<TableDesigner
:initial-data="data"
:lazy-load="{ enabled: true, pageSize: 100, maxCachedPages: 10, preloadPages: 1 }"
/>Enable SharedArrayBuffer in production by serving these headers (required for multi-threaded WASM):
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corpThe engine gracefully falls back to main-thread execution when COOP/COEP is absent — call wb.getSystemReport() to inspect the current state.
📁 Package Exports
import { TableDesigner, Workbook, SvgIcon } from 'vue-canvas-sheet';
import { Workbook as CoreWorkbook } from 'vue-canvas-sheet/core';
import { OffscreenRenderer } from 'vue-canvas-sheet/render';🧪 Development
npm install
npm run dev # dev server (vite)
npm run demo # run samples/basic-usage
npm run test # unit tests (vitest)
npm run benchmark # performance benchmarks
npm run build # build WASM + JS bundle
npm run collab:server # reference collaboration server📄 License
Apache License 2.0 — Copyright © 2026-Present, Lee & Vue-Canvas-Sheet Team.
