OpenSheets

An MIT-licensed spreadsheet component for React, with a built-in formula evaluator and an optional collaboration relay.

npm install opensheets

Releases are published to npm from GitHub releases, with npm provenance linking each version to its commit.

The live demo is a shared playground. Anyone can open it and edit, and it resets every night, so nothing you put there will last.

The OpenSheets grid in the demo app, with a formatting toolbar and formula bar above, a sheet of numbers with frozen header rows, and sheet tabs along the bottom.
The grid, formula bar and toolbar as they appear in the demo.

What it does

The core package, with no optional entries installed.

  • Grid

    Rows and columns are virtualized, so large sheets stay responsive. Frozen rows and columns, merged cells, column and row resizing, and insert or delete of rows and columns with formula references updated.

  • Editing

    Edit in the cell or in the formula bar. Keyboard navigation, multi-range selection, copy and paste, a fill handle for copies and series, sort, and document-scoped undo and redo.

  • Formulas

    A built-in evaluator with cell references, ranges, absolute references and cross-sheet references, and a fixed set of functions: SUM, AVERAGE, COUNT, MIN, MAX, IF, AND, OR, text functions such as LEN, UPPER, LEFT and MID, and the date functions TODAY and NOW, among others. Formulas recalculate live, including across sheets.

  • Formatting

    Fonts, bold, italic, underline, strikethrough, text and fill colours, alignment, wrapping and borders. Number formats such as currency, percent and dates, plus conditional formatting rules and templates.

  • Data

    Per-column filters and sorting, data validation with dropdowns, comments with replies, find and replace, and bar, line and pie charts rendered as SVG.

  • Files

    CSV import and export out of the box. XLSX import and export through the optional SheetJS entry, opensheets/excel.

  • Persistence

    A localStorage adapter with version history is included. Anything that implements the PersistenceAdapter interface can take its place.

  • Theming

    Every colour is a CSS custom property, with light and dark themes included. This page uses the same tokens.

  • Accessibility

    The grid uses ARIA grid semantics and can be operated entirely from the keyboard.

A sheet with bold headers, currency and percent number formats, a fill colour on one column, and the conditional formatting panel open on the right.
Number formats, colours and a conditional formatting rule.

Collaboration

The relay ships in the package as opensheets/server. On the client, one hook connects a spreadsheet to it; on the server, you mount the relay in your own Node HTTP server.

WebSocket relay
Cell edits and selections travel through a WebSocket relay; every connected tab sees them.
Accounts
People can create an account and sign in. Without one, a tab collaborates as a guest.
Presence per person
Presence is tracked per person, not per tab, so a second tab does not show up as a second collaborator.
Protected ranges
A range can be protected so that other collaborators do not edit it.
Offline queue
Edits made while disconnected are queued; the client reconnects on its own and sends them.
Last-write-wins merge
Concurrent edits to the same cell merge last-write-wins, so every client ends up with the same sheet.
Several instances
One instance keeps its state in memory. To run several, point them at one Redis; any instance can then serve any client.
Two people editing the same sheet: coloured selection outlines with name labels on different cells, and avatars for both collaborators in the header.
Two collaborators on one sheet, each with their own selection colour.

The relay checks the origin of connections, rate-limits connections, messages and sign-in, caps sizes, and enforces protected-range ownership. What it does not do is decide who may open which sheet: every connected client can read and write every sheet unless you supply an authorize hook. The security policy has the details.

Use it in your app

Install the package, import the two stylesheets once, and wrap the grid in a provider.

import { SpreadsheetProvider, SpreadsheetGrid, FormulaBar, FormattingToolbar } from 'opensheets';
import 'opensheets/styles.css';
import 'opensheets/styles/tokens.css';

export function Sheet() {
  return (
    <SpreadsheetProvider spreadsheetId="quarterly" persistence="local">
      <FormattingToolbar />
      <FormulaBar />
      <SpreadsheetGrid />
    </SpreadsheetProvider>
  );
}
  • opensheets/excel adds XLSX import and export. It needs the xlsx peer dependency (SheetJS).
  • opensheets/hyperformula swaps in a formula engine backed by HyperFormula, which is GPL-3.0-only. It is opt in; the core does not need it.
  • opensheets/server is the collaboration relay. It needs ws, and redis if you run several instances.

How it is built

  • MIT licence. The optional HyperFormula entry is the only part with a different licence, and it is kept out of the core.
  • One runtime dependency, for row and column virtualization. React is a peer dependency.
  • ESM and CommonJS builds, with TypeScript types.
  • Releases come only from CI. A GitHub release triggers the publish, with npm provenance linking the tarball to the commit and workflow run.
  • Pinned dependencies through a committed lockfile, and dependency install scripts never run.
  • Tests run on every push to the main branch and on every pull request, along with type checking and lint.

The dependency and release policy is written down in docs/SUPPLY-CHAIN.md.