Load actuals

September 5, 2026 Β· View on GitHub

Formualizer

Arrow Powered CI Python Coverage Rust Core Coverage crates.io PyPI npm Documentation License: MIT/Apache-2.0

Formualizer banner


A lightning-fast, embeddable spreadsheet runtime for Rust, Python, and JavaScript. Parse formulas, load and edit Excel workbooks, recalculate them incrementally, and expose spreadsheet models as deterministic, typed APIsβ€”without Excel.

No Excel COM automation. No LibreOffice UNO bridge. No stitching together slow Python libraries for workbook editing and partial formula evaluation.

Formualizer combines broad Excel-compatible formula support with Arrow-backed storage, dependency-aware recalculation, dynamic arrays, file I/O, undo/redo, and SheetPort. One permissively licensed Rust core ships to Rust, Python, browsers, and Node.js.


Highlights

400+ Excel functionsMath, text, lookup (XLOOKUP, VLOOKUP), date/time, statistics, financial, database, engineering
Three language targetsRust, Python (PyO3), and WASM (browser + Node) with consistent APIs
Arrow-powered storageApache Arrow columnar backing with spill overlays for efficient large-workbook evaluation
Dependency graphIncremental recalculation, cycle detection, topological scheduling, optional parallel evaluation
Dynamic arraysFILTER, UNIQUE, SORT, SORTBY, XLOOKUP with automatic spill semantics
Undo / redoTransactional changelog with action grouping, rollback, and replay
File I/OLoad and write XLSX (calamine, umya), CSV, JSON β€” all behind feature flags
SheetPortTreat any spreadsheet as a typed API with YAML manifests, schema validation, and batch evaluation
Deterministic modeInject clock, timezone, and RNG seed for reproducible evaluation (built for AI agents)
Experimental span evaluationOpt-in FormulaPlane runtime for accelerating eligible large copied-formula families

Documentation

πŸ“– formualizer.dev β€” full documentation, interactive tools, and API reference.

  • Quickstarts β€” get running in Rust, Python, or JS/WASM in minutes
  • Function Reference β€” 400+ built-in functions with examples
  • Formula Parser β€” interactive browser-based formula parser and AST inspector
  • SheetPort Guide β€” treat spreadsheets as typed, deterministic APIs
  • Core Concepts β€” dependency graph, FormulaPlane span evaluation, evaluation pipeline, coercion rules
  • Large Workbook Performance β€” loading, sparse ingest, and opt-in span acceleration guidance

Who is this for?

  • Fintech & insurance teams replacing Excel VBA or server-side workbook evaluation with a fast, deterministic engine that doesn't require Excel installed.
  • AI / agent builders who need programmatic spreadsheet manipulation with deterministic evaluation, auditable changelogs, and typed I/O via SheetPort.
  • SaaS products embedding spreadsheet logic β€” pricing calculators, planning tools, configurators β€” without shipping a full spreadsheet UI.
  • Data engineers extracting business logic trapped in spreadsheets into reproducible, testable pipelines.

Building an AI agent? Use agent-spreadsheet

Formualizer is the engine. If you want an agent to work with workbooks β€” read, profile, edit, recalculate, diff, and verify them safely β€” use agent-spreadsheet, the official agent tooling layer built on this engine:

your agent / app
      β”‚
agent-spreadsheet     CLI (`agent-spreadsheet` / `asp`) Β· MCP server Β· JS SDK
      β”‚
  formualizer         parsing Β· dependency graph Β· 400+ functions Β· recalc
  • CLI β€” stateless one-shot reads, safe edits, recalc, and verifiable diffs for shell-native agents and CI (npm i -g agent-spreadsheet or cargo install agent-spreadsheet)
  • MCP server β€” stateful multi-turn sessions with workbook forks, checkpoints, staged edits, and native recalculation (no LibreOffice fallback)
  • JS SDK β€” typed API for app integrations, backed by the MCP server or an embedded in-process WASM engine

Unlike screenshot-driven UI automation or MCP servers that can't compute a formula, agent-spreadsheet evaluates the actual workbook with this engine β€” every edit is recalculated, traceable, and diffable.

Quick start

Rust

use formualizer_workbook::Workbook;
use formualizer_common::LiteralValue;

let mut wb = Workbook::new();
wb.add_sheet("Sheet1")?;

// Populate data
wb.set_value("Sheet1", 1, 1, LiteralValue::Number(1000.0))?;  // A1: principal
wb.set_value("Sheet1", 2, 1, LiteralValue::Number(0.05))?;     // A2: rate
wb.set_value("Sheet1", 3, 1, LiteralValue::Number(12.0))?;     // A3: periods

// Monthly payment formula
wb.set_formula("Sheet1", 1, 2, "=PMT(A2/12, A3, -A1)")?;
let payment = wb.evaluate_cell("Sheet1", 1, 2)?;
// => ~85.61
# Cargo.toml
[dependencies]
formualizer = "0.6"

Python

pip install formualizer
import formualizer as fz

wb = fz.Workbook()
s = wb.sheet("Forecast")

# Load actuals
s.set_values_batch(1, 1, [
    [fz.LiteralValue.text("Month"), fz.LiteralValue.text("Revenue"), fz.LiteralValue.text("Growth")],
    [fz.LiteralValue.text("Jan"),   fz.LiteralValue.number(50000.0), fz.LiteralValue.empty()],
    [fz.LiteralValue.text("Feb"),   fz.LiteralValue.number(53000.0), fz.LiteralValue.empty()],
    [fz.LiteralValue.text("Mar"),   fz.LiteralValue.number(58000.0), fz.LiteralValue.empty()],
])

# Add growth formulas
s.set_formula(3, 3, "=(B3-B2)/B2")  # C3: Feb growth
s.set_formula(4, 3, "=(B4-B3)/B3")  # C4: Mar growth

print(wb.evaluate_cell("Forecast", 3, 3))  # 0.06 (6%)
print(wb.evaluate_cell("Forecast", 4, 3))  # ~0.094 (9.4%)

WASM (browser / Node)

npm install formualizer
import init, { Workbook } from 'formualizer';
await init();

const wb = new Workbook();
wb.addSheet('Pricing');
wb.setValue('Pricing', 1, 1, 100);     // base price
wb.setValue('Pricing', 2, 1, 0.15);    // discount
wb.setFormula('Pricing', 1, 2, '=A1*(1-A2)');

console.log(await wb.evaluateCell('Pricing', 1, 2)); // 85

Performance and experimental span evaluation

Formualizer's default execution path is the stable dependency graph. For large read-heavy XLSX workloads, the Calamine backend provides a sparse-compatible loading path. Formualizer 0.6 also includes experimental, opt-in FormulaPlane span evaluation for eligible copied-formula families.

Span evaluation is disabled by default:

use formualizer_workbook::{Workbook, WorkbookConfig};

let cfg = WorkbookConfig::interactive().with_span_evaluation(true);
let mut wb = Workbook::new_with_config(cfg);

Use it when you can validate critical workbooks against your own regression corpus. Unsupported formulas fall back to the legacy graph path; internal chains/running balances and array-literal families are not span-promoted in 0.6.

Custom functions (workbook-local)

You can register custom functions per workbook in Rust, Python, and JS/WASM.

  • Rust: register_custom_function / unregister_custom_function / list_custom_functions
  • Python: register_function / unregister_function / list_functions
  • JS/WASM: registerFunction / unregisterFunction / listFunctions

Semantics are consistent across hosts:

  • Function names are case-insensitive (my_fn, MY_FN, and My_Fn refer to the same function).
  • Custom functions are workbook-local and resolve before global built-ins.
  • Overriding built-ins is blocked by default; opt in with allow_override_builtin (Rust/Python) or allowOverrideBuiltin (JS).
  • Arguments are passed by value; range arguments are materialized as 2D arrays/lists.
  • Returning an array spills into the grid using normal dynamic-array behavior.
  • Callback failures become spreadsheet errors (ExcelError in Rust, #VALUE! mapping for Python/JS exceptions).

Runnable examples:

  • Rust callback custom function: cargo run -p formualizer-workbook --example custom_function_registration
  • Python callback custom function: python bindings/python/examples/custom_function_registration.py
  • JS/WASM callback custom function: cd bindings/wasm && npm run build && node examples/custom-function-registration.mjs
  • Rust WASM plugin inspect catalog: cargo run -p formualizer-workbook --features wasm_plugins --example wasm_plugin_inspect_catalog
  • Rust WASM plugin inspect + attach + bind: cargo run -p formualizer-workbook --features wasm_runtime_wasmtime --example wasm_plugin_inspect_attach_bind
  • Rust WASM plugin directory attach: cargo run -p formualizer-workbook --features wasm_runtime_wasmtime --example wasm_plugin_attach_dir

WASM plugin path (Rust workbook API):

  • Effect-free inspect APIs are available:
    • inspect_wasm_module_bytes
    • inspect_wasm_module_file (native only)
    • inspect_wasm_modules_dir (native only)
  • Explicit workbook-local attach/bind APIs are available:
    • attach_wasm_module_bytes / attach_wasm_module_file / attach_wasm_modules_dir
    • bind_wasm_function
  • Runtime behavior:
    • wasm_plugins only: runtime remains pending (#N/IMPL on bind)
    • wasm_runtime_wasmtime (native): use_wasmtime_runtime() enables executable plugin bindings

How is this different?

LibraryLanguageParseEvaluateWriteFunctionsDep. graphLicense
FormualizerRust / Python / WASMYesYesYes400+Yes (incremental)MIT / Apache-2.0
HyperFormulaJavaScriptYesYesNo~400YesAGPL-3.0 (or commercial)
calamineRustNoNoNoN/AN/AMIT / Apache-2.0
openpyxlPythonNoNoYesN/AN/AMIT
xlcalcPythonYesYesNo~50PartialMIT
formulajsJavaScriptNoYesNo~100NoMIT
  • HyperFormula is the closest feature competitor, but its AGPL-3.0 license requires you to open-source your entire application or purchase a commercial license from Handsontable. Formualizer is permissively licensed with no strings attached.
  • calamine is read-only β€” it extracts cached values from XLSX files but cannot evaluate formulas.
  • openpyxl reads and writes XLSX but has no formula evaluation engine.
  • xlcalc evaluates formulas but supports a fraction of Excel's function library and has limited dependency tracking.
  • Formualizer is a complete, permissively-licensed engine: parse formulas, track dependencies, evaluate with 400+ functions, mutate workbooks, undo/redo β€” from Rust, Python, or the browser.

Architecture

Formualizer is organized as a layered crate workspace. Pick the layer that fits your use case:

formualizer              <-- recommended: batteries-included re-export
  formualizer-workbook   <-- high-level workbook API, sheets, undo/redo, I/O
    formualizer-eval     <-- calculation engine, dependency graph, built-ins
      formualizer-parse  <-- tokenizer, parser, AST, pretty-printer
      formualizer-common <-- shared types (values, errors, references)
  formualizer-sheetport  <-- SheetPort runtime (spreadsheets as typed APIs)
CrateWhen to use it
formualizerDefault choice β€” re-exports workbook, engine, and SheetPort with feature flags
formualizer-workbookYou want the full workbook experience: sheets, I/O, undo/redo, batch operations
formualizer-evalYou own your own data model and want just the calculation engine with custom resolvers
formualizer-parseYou only need formula parsing, tokenization, AST analysis, or pretty-printing

SheetPort: spreadsheets as typed APIs

SheetPort lets you treat any spreadsheet as a deterministic function with typed inputs and outputs, defined by a YAML manifest:

from formualizer import SheetPortSession, Workbook

session = SheetPortSession.from_manifest_yaml(manifest_yaml, workbook)

# Write typed inputs β€” validated against schema
session.write_inputs({"loan_amount": 250000, "rate": 0.045, "term_months": 360})

# Evaluate and read typed outputs
result = session.evaluate_once(freeze_volatile=True)
print(result["monthly_payment"])  # deterministic, schema-validated

Use cases: financial model APIs, AI agent tool-use, configuration-driven business logic, batch scenario evaluation.

Performance

The evaluation engine is built on Apache Arrow columnar storage with:

  • Incremental dependency graph (only recalculates what changed)
  • CSR (Compressed Sparse Row) edge format for memory-efficient graphs
  • Optional parallel evaluation via Rayon
  • Warm-up planning for large workbooks
  • Spill overlays for dynamic array results

Formal benchmarks are in progress.

Bindings

TargetInstallDocs
Rustcargo add formualizerdocs.rs Β· guide
Pythonpip install formualizerREADME Β· guide
Python (Pyodide)await micropip.install(wheel_url) (compatible wheel URL)README Β· guide
WASMnpm install formualizerREADME Β· guide

Both Python and WASM bindings expose the same core API surface: tokenization, parsing, workbook operations, evaluation, undo/redo, and SheetPort.

WebAssembly runtime profiles

Formualizer supports two explicit wasm profiles so the right runtime assumptions are always in scope.

portable-wasm β€” raw / wasmtime-safe

No JS globals, no wasm-bindgen imports, no browser clock or entropy. Safe to compile as a raw wasm32-unknown-unknown module and instantiate inside wasmtime or any non-JS wasm host.

Time-dependent functions (NOW, TODAY, etc.) default to UTC epoch when no clock is injected; supply a FixedClock or implement ClockProvider to drive them deterministically.

# Cargo.toml
[dependencies]
formualizer = { version = "0.5", default-features = false, features = ["portable-wasm"] }

For individual crates:

formualizer-eval = { version = "0.5", default-features = false }

wasm-js β€” browser / Node via wasm-bindgen

Full browser-compatible runtime: web-time for performance.now() timing, JS-backed entropy (crypto.getRandomValues), and ambient wall-clock time for date functions. This is the profile used by the formualizer npm package.

# Cargo.toml β€” for crates that also depend on wasm-bindgen
[dependencies]
formualizer = { version = "0.5", default-features = false, features = ["wasm-js"] }

The formualizer npm package (bindings/wasm) selects this profile automatically.

Default (native)

No action needed for native Rust targets. cargo add formualizer gives the full stack with system clock, JSON/CSV support, and no wasm-specific features.


Roadmap

Roadmap and active development are tracked via GitHub Issues, milestones, and pull requests.

Contributing

Contributions are welcome. If you're looking for something to work on, browse open issues or open a new issue to discuss a proposal.

# Build and test
cargo test --workspace
cd bindings/python && maturin develop && pytest
cd bindings/wasm && wasm-pack build --target bundler && wasm-pack test --node

License

Dual-licensed under MIT or Apache-2.0, at your option.