Spreadsheet 2 - Composable, declarative Spreadsheet component.
Beautifully designed Spreadsheet component for React. Composable. Customizable. Declarative
Built for Developers
Spreadsheet is rendered in HTML Canvas, giving you ~60fps rendering performance and ability to display millions of rows and columns without peformance impact. With escape hatches, you can access the internals and customize it to your liking.
Compose the perfect Spreadsheet
Pick and choose the components you need to build your Spreadsheet.
You can also build own custom functionality on top of Spreadsheet 2, with the many callbacks from CanvasGrid.
import {
SpreadsheetProvider, CanvasGrid, Toolbar, ButtonUndo,
ButtonRedo, RangeSelector, FormulaBarLabel, FormulaBarInput,
BottomBar, SheetSwitcher, SheetTabs, SheetStatus
} from "@rowsncolumns/spreadsheet"
const App = () => {
const sheet = { id: 1, rowCount: 1000, columnCount: 1000 }
return (
<>
<Toolbar>
<ButtonUndo onClick={} />
<ButtonRedo onClick={} />
{/*..import all other controls*/}
</Toolbar>
<FormulaBar>
<RangeSelector />
<FormulaBarInput />
</FormulaBar>
<CanvasGrid
sheetId={sheet.id}
rowCount={sheet.rowCount}
columnCount={sheet.columnCount}
getCellData={(sheetId: number, rowIndex: number, columnIndex: number): CellData => {
return {
userEnteredValue: {
formulaValue: '=SUM(4,4)'
},
formattedValue: "8"
}
}}
/>
<BottomBar>
<SheetSwitcher />
<SheetTabs />
<SheetStatus />
</BottomBar>
</>
)
}
{/* Wrap your app in SpreadsheetProvider */}
export const Spreadsheet = () => (
<SpreadsheetProvider>
<App />
</SpreadsheetProvider>
)
Bring your own Data model and State management
Or use `useSpreadsheetState` hook
Spreadsheet components are all uncontrolled and stateless. It renders based on the props that you pass-in and invokes callbacks to user actions.
Spreadsheet 2 is agnostic of your data persistence model. You can choose any database for storage or real-time collaboration.
import type { CellData } from "@rowsncolumns/spreadsheet"
import {
useSpreadsheetState,
Sheet,
SheetData,
RowData
} from "@rowsncolumns/spreadsheet-state"
type MyCellData = CellData & {
customProperty: 'hello'
}
type SheetData<T extends CellData> = Record<number, RowData<T>[]>
type RowData<T> = {
values: T[]
}
const App = () => {
const [ sheets, onChangeSheets ] = useState<Sheet[]>([])
const [ sheetData, onChangeSheetData ] = useState<SheetData<MyCellData>>({ })
return (
<CanvasGrid<MyCellData>
getCellData={(sheetId, rowIndex, columnIndex) => {
return sheetData[sheetId]?.[rowIndex]?.values?.[columnIndex]
}}
onChange={(value: string, sheetId: number, rowIndex: number, columnIndex: number) => {
// Persist and generate undo/redo patches
// If you are using useSpreadsheetState hook,its all built-in
onChangeSheetData(prevData => ...)
}}
/>
)
}
{/* Wrap your app in SpreadsheetProvider */}
export const Spreadsheet = () => (
<SpreadsheetProvider>
<App />
</SpreadsheetProvider>
)
useSpreadsheetState hook
This is an optional (Production grade) hook if you want to get started with the Spreadsheet with complete state management.
- Uses immer for state management.
- Full undo/redo capability.
- Calculation framework with optional web worker support.
- Real time collaboration built-in.
import {
defaultSpreadsheetTheme,
Sheet,
SheetData,
} from "@rowsncolumns/spreadsheet-state"
import {
CellData,
NamedRange,
EmbeddedObject,
EmbeddedChart,
TableView,
SpreadsheetTheme,
CanvasGrid,
SpreadsheetProvider
} from "@rowsncolumns/spreadsheet"
const App = () => {
const [sheets, onChangeSheets] = useState<Sheet[]>([]);
const [sheetData, onChangeSheetData] = useState<SheetData<CellData>>({});
const [scale, onChangeScale] = useState(1);
const [colorMode, onChangeColorMode] = useState<ColorMode>();
const [charts, onChangeCharts] = useState<EmbeddedChart[]>([]);
const [embeds, onChangeEmbeds] = useState<EmbeddedObject[]>([]);
const [tables, onChangeTables] = useState<TableView[]>([]);
const [namedRanges, onChangeNamedRanges] = useState<NamedRange[]>();
const [theme, onChangeTheme] = useState<SpreadsheetTheme>(defaultSpreadsheetTheme);
const locale = "en-GB";
const currency = "USD";
const {
activeCell,
activeSheetId,
selections,
rowCount,
columnCount,
frozenColumnCount,
frozenRowCount,
rowMetadata,
columnMetadata,
merges,
protectedRanges,
bandedRanges,
conditionalFormats,
isDarkMode,
spreadsheetColors,
canRedo,
canUndo,
undo,
redo,
...// other Spreadsheet methods
} = useSpreadsheetState({
sheets,
sheetData,
tables,
functions,
namedRanges,
theme,
colorMode,
locale,
onChangeSheets,
onChangeSheetData,
onChangeEmbeds,
onChangeCharts,
onChangeTables,
onChangeNamedRanges,
onChangeTheme,
})
return (
<>
<Toolbar>
<ButtonUndo onClick={onUndo} disabled={!canUndo} />
<ButtonRedo onClick={onRedo} disabled={!canRedo} />
{/*..import all other controls*/}
</Toolbar>
<CanvasGrid
{...spreadsheetColors}
enableTextOverflow
stickyEditor={true}
scale={scale}
conditionalFormats={conditionalFormats}
sheetId={activeSheetId}
rowCount={rowCount}
columnCount={columnCount}
frozenColumnCount={frozenColumnCount}
frozenRowCount={frozenRowCount}
rowMetadata={rowMetadata}
columnMetadata={columnMetadata}
activeCell={activeCell}
selections={selections}
theme={theme}
merges={merges}
charts={charts}
embeds={embeds}
tables={tables}
{/* Other methods from the hook */}
/>
</>
)
}
{/* Wrap your app in SpreadsheetProvider */}
export const Spreadsheet = () => (
<SpreadsheetProvider>
<App />
</SpreadsheetProvider>
)
Spreadsheet features
Built for developers. Spreadsheet is rendered in HTML Canvas, giving you ~60fps rendering performance and ability to display millions of rows and columns without peformance impact.
- Conditional formatting with colors and color scales
- Data validation
- Frozen rows and columns
- Custom editors
- Custom tooltips
- Custom cell renderers
- Custom charts and embeds
- Cell styling
- Copy, Paste and Cut support
- Custom fonts
- Context menu
- Merge cells
- Show, Hide rows and columns
- Insert, Delete, Move rows and columns
- Dark mode
- Custom formula functions
- Protected ranges
- Named ranges
- Banded ranges
- Calculated columns
- Sorting filtering sheets and tables
- Easily integrate 3rd party APIs like OpenAI and other language models.