saurus-excel
v0.2.1
Published
π¦ Blazingly fast Excel parser for the web - powered by Rust & WebAssembly
Maintainers
Readme
π¦ Saurus-Excel
Blazingly fast Excel & CSV parser for the web - powered by Rust & WebAssembly
Saurus-Excel is a high-performance spreadsheet parser designed for handling large datasets in the browser. Built with Rust and compiled to WebAssembly, it processes millions of rows without crashing your browser.
β¨ Features
- π Blazingly Fast - 10x faster than SheetJS for large files
- πΎ Memory Efficient - Minimal RAM usage
- π Multiple Formats - XLSX, XLS, XLSM, XLSB, ODS, CSV
- βοΈ Framework Support - React hooks included
- π§ TypeScript First - Full type definitions
- π CSV Support - Parse CSV with custom delimiters
π¦ Installation
npm install saurus-excelπ Usage
Vanilla JavaScript (Browser)
<!DOCTYPE html>
<html>
<head>
<title>Excel Parser</title>
</head>
<body>
<input type="file" id="fileInput" accept=".xlsx,.xls" />
<pre id="output"></pre>
<script type="module">
// Import from pkg folder for direct Wasm access
import init, { parseExcel } from "saurus-excel/pkg/excelsaurus.js";
// Initialize Wasm module (required once)
await init();
document
.getElementById("fileInput")
.addEventListener("change", async (e) => {
const file = e.target.files[0];
if (!file) return;
// Read file as bytes
const arrayBuffer = await file.arrayBuffer();
const bytes = new Uint8Array(arrayBuffer);
// Parse Excel
const result = parseExcel(bytes, { hasHeaders: true });
console.log(result.headers); // ["Name", "Email", "Age"]
console.log(result.rows); // [["John", "[email protected]", 30], ...]
document.getElementById("output").textContent = JSON.stringify(
result,
null,
2,
);
});
</script>
</body>
</html>With Bundler (Vite, Webpack, etc.)
// Note: Wasm needs special bundler config (see below)
import init, {
parseExcel,
getSheetNames,
} from "saurus-excel/pkg/excelsaurus.js";
// Initialize once at app startup
let initialized = false;
async function ensureInit() {
if (!initialized) {
await init();
initialized = true;
}
}
export async function parseExcelFile(file: File) {
await ensureInit();
const arrayBuffer = await file.arrayBuffer();
const bytes = new Uint8Array(arrayBuffer);
return parseExcel(bytes, {
hasHeaders: true,
limit: 1000, // Optional: limit rows
});
}βοΈ React 19 / Next.js (Plug & Play)
Quick Start
import { useExcelParser } from "saurus-excel/react";
function ImportPage() {
const { parse, data, loading, error, ready } = useExcelParser();
if (!ready) return <p>Loading parser...</p>;
return (
<div>
<input
type="file"
accept=".xlsx,.xls"
onChange={(e) => e.target.files?.[0] && parse(e.target.files[0])}
/>
{loading && <p>Parsing...</p>}
{error && <p style={{ color: "red" }}>{error.message}</p>}
{data && (
<table>
<thead>
<tr>
{data.headers.map((h, i) => (
<th key={i}>{h}</th>
))}
</tr>
</thead>
<tbody>
{data.rows.map((row, i) => (
<tr key={i}>
{row.map((cell, j) => (
<td key={j}>{cell}</td>
))}
</tr>
))}
</tbody>
</table>
)}
</div>
);
}With Provider (Recommended for Next.js App Router)
// app/providers.tsx
"use client";
import { ExcelProvider } from "saurus-excel/react";
export function Providers({ children }: { children: React.ReactNode }) {
return <ExcelProvider>{children}</ExcelProvider>;
}
// app/layout.tsx
import { Providers } from "./providers";
export default function RootLayout({ children }) {
return (
<html>
<body>
<Providers>{children}</Providers>
</body>
</html>
);
}Drop-in Component
import { ExcelDropzone } from "saurus-excel/react";
function UploadPage() {
return (
<ExcelDropzone
onData={(result) => {
console.log("Headers:", result.headers);
console.log("Rows:", result.rows);
}}
onError={(err) => console.error(err)}
options={{ hasHeaders: true, limit: 1000 }}
/>
);
}Available Hooks
| Hook | Description |
| ------------------ | --------------------------- |
| useExcelParser() | Main hook for parsing files |
| useExcelSheets() | Get sheet names from file |
Hook Return Values
const {
parse, // (file: File, options?) => Promise<ParseResult>
data, // ParseResult | null
loading, // boolean
error, // Error | null
progress, // number (0-100)
ready, // boolean - Wasm loaded
reset, // () => void
} = useExcelParser();βοΈ Bundler Configuration
Vite
// vite.config.ts
import { defineConfig } from "vite";
export default defineConfig({
optimizeDeps: {
exclude: ["saurus-excel"],
},
build: {
target: "esnext",
},
});Next.js
// next.config.js
module.exports = {
webpack: (config) => {
config.experiments = {
...config.experiments,
asyncWebAssembly: true,
};
return config;
},
};Webpack 5
// webpack.config.js
module.exports = {
experiments: {
asyncWebAssembly: true,
},
};π API Reference
parseExcel(bytes, options?)
Parse an Excel file from Uint8Array.
const result = parseExcel(bytes, {
sheet: 0, // Sheet index or name (default: 0)
limit: 1000, // Max rows to parse (default: all)
skipRows: 0, // Rows to skip from start
hasHeaders: true, // Treat first row as headers
});Returns:
{
headers: string[]; // Column headers
rows: CellValue[][]; // Row data
rowCount: number; // Total rows parsed
sheetName: string; // Sheet name
}getSheetNames(bytes)
Get list of sheet names in workbook.
const sheets = getSheetNames(bytes);
// ["Sheet1", "Sheet2", "Data"]parseCsv(bytes, options?)
Parse a CSV file.
import { parseCsv } from "saurus-excel";
const result = await parseCsv(file, {
delimiter: ",", // Delimiter character (default: ',')
hasHeaders: true, // First row is headers
limit: 1000, // Max rows to parse
skipRows: 0, // Rows to skip
});
console.log(result.headers); // ["name", "email", "age"]
console.log(result.rows); // [["John", "[email protected]", 30], ...]Supported delimiters: , (comma), ; (semicolon), \t (tab), | (pipe)
parseSpreadsheet(file, options?)
Auto-detect file type and parse (Excel or CSV).
import { parseSpreadsheet } from "saurus-excel";
// Automatically handles .xlsx, .xls, .csv files
const result = await parseSpreadsheet(file, { hasHeaders: true });π Performance
Tested with 100,000 rows:
| Metric | SheetJS | Saurus-Excel | Improvement | | ------------ | ------- | ------------ | --------------- | | Parse Time | ~15s | ~2s | 7.5x faster | | Memory Usage | ~800MB | ~100MB | 8x less | | Bundle Size | ~300KB | ~150KB | 2x smaller |
π License
MIT Β© trietcn
