Multi-Instance Spreadsheets
Run multiple spreadsheet instances in the same application
The SpreadsheetMultiInstanceProvider component allows you to render multiple spreadsheet instances within the same application while maintaining isolated state and preventing conflicts between instances.
Overview
By default, each spreadsheet instance shares global context through SpreadsheetProvider. When you need to render multiple spreadsheets simultaneously (e.g., side-by-side comparison, multi-document interface), you need to wrap them in SpreadsheetMultiInstanceProvider to ensure proper isolation.
Basic Usage
import {
SpreadsheetProvider,
SpreadsheetMultiInstanceProvider,
CanvasGrid,
} from "@rowsncolumns/spreadsheet";
import { useSpreadsheetState } from "@rowsncolumns/spreadsheet-state";
function MultiSpreadsheetApp() {
return (
<SpreadsheetMultiInstanceProvider>
<SpreadsheetProvider>
<SpreadsheetInstance instanceId="spreadsheet-1" />
</SpreadsheetProvider>
<SpreadsheetProvider>
<SpreadsheetInstance instanceId="spreadsheet-2" />
</SpreadsheetProvider>
</SpreadsheetMultiInstanceProvider>
);
}
function SpreadsheetInstance({ instanceId }: { instanceId: string }) {
const {
activeCell,
activeSheetId,
selections,
// ... other state
} = useSpreadsheetState({
// configuration
});
return (
<CanvasGrid
instanceId={instanceId}
sheetId={activeSheetId}
activeCell={activeCell}
selections={selections}
// ... other props
/>
);
}Instance ID
Each CanvasGrid instance should have a unique instanceId prop when rendered within SpreadsheetMultiInstanceProvider:
<CanvasGrid
instanceId="spreadsheet-a" // Unique identifier
sheetId={activeSheetId}
// ... other props
/>Why Instance IDs Matter
Instance IDs ensure that:
Keyboard events are routed to the correct spreadsheet
Editor state is isolated between instances
Context menus and dialogs appear in the correct location
Formula evaluation doesn't cross instance boundaries
Use Cases
Side-by-Side Comparison
Compare two spreadsheets side by side:
function SpreadsheetComparison() {
return (
<SpreadsheetMultiInstanceProvider>
<div className="flex gap-4">
<div className="flex-1">
<h2>Version 1</h2>
<SpreadsheetProvider>
<SpreadsheetView instanceId="version-1" />
</SpreadsheetProvider>
</div>
<div className="flex-1">
<h2>Version 2</h2>
<SpreadsheetProvider>
<SpreadsheetView instanceId="version-2" />
</SpreadsheetProvider>
</div>
</div>
</SpreadsheetMultiInstanceProvider>
);
}Multi-Document Interface
Create a tabbed or windowed interface with multiple spreadsheet documents:
function MultiDocumentInterface() {
const [documents] = useState([
{ id: "doc-1", name: "Budget 2024" },
{ id: "doc-2", name: "Sales Report" },
{ id: "doc-3", name: "Inventory" },
]);
return (
<SpreadsheetMultiInstanceProvider>
{documents.map((doc) => (
<SpreadsheetProvider key={doc.id}>
<div className="document-window">
<h3>{doc.name}</h3>
<SpreadsheetView instanceId={doc.id} />
</div>
</SpreadsheetProvider>
))}
</SpreadsheetMultiInstanceProvider>
);
}Master-Detail View
Display a master spreadsheet with a detail view:
function MasterDetailView() {
const [selectedRow, setSelectedRow] = useState<number | null>(null);
return (
<SpreadsheetMultiInstanceProvider>
<div className="grid grid-cols-2 gap-4">
{/* Master spreadsheet */}
<SpreadsheetProvider>
<h2>Sales Overview</h2>
<SpreadsheetView
instanceId="master"
onRowSelect={setSelectedRow}
/>
</SpreadsheetProvider>
{/* Detail spreadsheet */}
{selectedRow && (
<SpreadsheetProvider>
<h2>Row Details</h2>
<SpreadsheetView
instanceId="detail"
rowData={selectedRow}
/>
</SpreadsheetProvider>
)}
</div>
</SpreadsheetMultiInstanceProvider>
);
}Complete Example
import React, { useState } from "react";
import {
SpreadsheetProvider,
SpreadsheetMultiInstanceProvider,
CanvasGrid,
Sheet,
} from "@rowsncolumns/spreadsheet";
import {
useSpreadsheetState,
SheetData,
CellData,
} from "@rowsncolumns/spreadsheet-state";
function App() {
return (
<SpreadsheetMultiInstanceProvider>
<div className="flex gap-5 h-screen p-5">
<div className="flex-1">
<SpreadsheetProvider>
<SpreadsheetA />
</SpreadsheetProvider>
</div>
<div className="flex-1">
<SpreadsheetProvider>
<SpreadsheetB />
</SpreadsheetProvider>
</div>
</div>
</SpreadsheetMultiInstanceProvider>
);
}
function SpreadsheetA() {
const [sheets, setSheets] = useState<Sheet[]>([
{ sheetId: 1, rowCount: 100, columnCount: 26, title: "Sheet A" }
]);
const [sheetData, setSheetData] = useState<SheetData<CellData>>({});
const {
activeCell,
activeSheetId,
selections,
getCellData,
onChangeActiveCell,
onChangeSelections,
onChange,
} = useSpreadsheetState({
sheets,
sheetData,
onChangeSheets: setSheets,
onChangeSheetData: setSheetData,
});
return (
<CanvasGrid
instanceId="spreadsheet-a"
sheetId={activeSheetId}
activeCell={activeCell}
selections={selections}
getCellData={getCellData}
onChangeActiveCell={onChangeActiveCell}
onChangeSelections={onChangeSelections}
onChange={onChange}
/>
);
}
function SpreadsheetB() {
// Similar implementation with different instance ID
// ...
return (
<CanvasGrid
instanceId="spreadsheet-b"
// ... props
/>
);
}Best Practices
Always Use Unique Instance IDs: Ensure each spreadsheet has a unique
instanceIdto prevent conflictsWrap Each Instance: Each spreadsheet should have its own
SpreadsheetProviderwrapperIsolate State: Use separate state management for each spreadsheet instance
Performance Considerations: Be mindful of rendering multiple large spreadsheets simultaneously - consider lazy loading or virtualization
Memory Management: Clean up instances when they're no longer needed to free up resources
Limitations
Each instance maintains its own calculation engine and state
Cross-instance formulas are not supported (formulas cannot reference cells from other instances)
Each instance requires separate data management
Performance Tips
When rendering multiple instances:
// Use React.memo to prevent unnecessary re-renders
const SpreadsheetInstance = React.memo(({ instanceId, data }) => {
// ... implementation
});
// Lazy load instances that aren't immediately visible
const LazySpreadsheet = lazy(() => import('./SpreadsheetInstance'));
function MultiInstance() {
return (
<SpreadsheetMultiInstanceProvider>
<Suspense fallback={<div>Loading...</div>}>
<LazySpreadsheet instanceId="lazy-1" />
</Suspense>
</SpreadsheetMultiInstanceProvider>
);
}Troubleshooting
Keyboard Events Not Working
If keyboard events aren't working properly, ensure:
Each
CanvasGridhas a uniqueinstanceIdThe
SpreadsheetMultiInstanceProviderwraps all instancesOnly one spreadsheet has focus at a time
State Conflicts
If you experience state conflicts between instances:
Verify each instance has its own
SpreadsheetProviderCheck that state management is properly isolated
Ensure instance IDs are unique and don't change during component lifecycle
Last updated
Was this helpful?