Fortune Sheet is a powerful, open-source spreadsheet component for React that provides Excel-like functionality directly in your web application. It offers features like cell editing, formulas, formatting, and data manipulation, making it perfect for building data-intensive applications. This guide walks through setting up and creating your first interactive spreadsheet using Fortune Sheet with React, from installation to a working implementation. This is part 1 of a series on using Fortune Sheet with React.
Prerequisites
Before you begin, make sure you have:
- Node.js version 16.0 or higher installed
- npm, yarn, or pnpm package manager
- A React project (version 16.8 or higher) or create-react-app setup
- Basic knowledge of React hooks (useState, useEffect)
Installation
Install Fortune Sheet and its dependencies using your preferred package manager:
npm install fortune-sheet
Or with yarn:
yarn add fortune-sheet
Or with pnpm:
pnpm add fortune-sheet
After installation, your package.json should include the dependency:
{
"dependencies": {
"fortune-sheet": "^1.0.0",
"react": "^18.0.0",
"react-dom": "^18.0.0"
}
}
Project Setup
Fortune Sheet requires CSS styles to be imported. Create or update your main entry file (typically src/index.js or src/main.jsx) to include the necessary styles:
// src/index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import 'fortune-sheet/dist/fortune-sheet.css';
import App from './App';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
If you're using a CSS-in-JS solution or want to import styles differently, you can also import the CSS in your component file.
First Example / Basic Usage
Let's create a simple spreadsheet component to get started. Create a new file src/Spreadsheet.jsx:
// src/Spreadsheet.jsx
import React, { useRef, useEffect } from 'react';
import FortuneSheet from 'fortune-sheet';
function Spreadsheet() {
const containerRef = useRef(null);
useEffect(() => {
if (containerRef.current) {
// Initialize Fortune Sheet
const sheet = new FortuneSheet(containerRef.current, {
data: [
[
{ v: 'Name', ct: { fa: 'General', t: 'g' } },
{ v: 'Age', ct: { fa: 'General', t: 'g' } },
{ v: 'City', ct: { fa: 'General', t: 'g' } }
],
[
{ v: 'John', ct: { fa: 'General', t: 'g' } },
{ v: 25, ct: { fa: 'General', t: 'n' } },
{ v: 'New York', ct: { fa: 'General', t: 'g' } }
],
[
{ v: 'Jane', ct: { fa: 'General', t: 'g' } },
{ v: 30, ct: { fa: 'General', t: 'n' } },
{ v: 'London', ct: { fa: 'General', t: 'g' } }
]
],
defaultRowHeight: 25,
defaultColWidth: 100
});
// Cleanup on unmount
return () => {
if (sheet && sheet.destroy) {
sheet.destroy();
}
};
}
}, []);
return (
<div
ref={containerRef}
style={{ width: '100%', height: '500px' }}
/>
);
}
export default Spreadsheet;
Now, update your App.jsx to use the spreadsheet:
// src/App.jsx
import React from 'react';
import Spreadsheet from './Spreadsheet';
import './App.css';
function App() {
return (
<div className="App">
<h1>Fortune Sheet Example</h1>
<Spreadsheet />
</div>
);
}
export default App;
This creates a basic spreadsheet with three columns (Name, Age, City) and two rows of data. The spreadsheet is fully interactive - you can click on cells to edit them, navigate with arrow keys, and resize columns.
Understanding the Basics
Fortune Sheet uses a data structure where each cell is represented as an object with:
-
v: The cell value (can be a string, number, or formula) -
ct: Cell type information with format (fa) and type (t)-
t: 'g'for general/text -
t: 'n'for numbers
-
The component initializes with a container DOM element and a configuration object. The configuration includes:
-
data: A 2D array of cell objects representing the spreadsheet data -
defaultRowHeight: Height of rows in pixels -
defaultColWidth: Width of columns in pixels
Here's a simpler example with just text data:
// src/SimpleSpreadsheet.jsx
import React, { useRef, useEffect } from 'react';
import FortuneSheet from 'fortune-sheet';
function SimpleSpreadsheet() {
const containerRef = useRef(null);
useEffect(() => {
if (containerRef.current) {
const sheet = new FortuneSheet(containerRef.current, {
data: [
[{ v: 'A1' }, { v: 'B1' }, { v: 'C1' }],
[{ v: 'A2' }, { v: 'B2' }, { v: 'C2' }],
[{ v: 'A3' }, { v: 'B3' }, { v: 'C3' }]
]
});
return () => {
if (sheet && sheet.destroy) {
sheet.destroy();
}
};
}
}, []);
return (
<div
ref={containerRef}
style={{
width: '100%',
height: '400px',
border: '1px solid #ddd'
}}
/>
);
}
export default SimpleSpreadsheet;
Practical Example / Building Something Real
Let's build a more practical example - a budget tracker spreadsheet. This will demonstrate how to work with formulas and handle user interactions:
// src/BudgetTracker.jsx
import React, { useRef, useEffect, useState } from 'react';
import FortuneSheet from 'fortune-sheet';
import 'fortune-sheet/dist/fortune-sheet.css';
function BudgetTracker() {
const containerRef = useRef(null);
const [sheetInstance, setSheetInstance] = useState(null);
useEffect(() => {
if (containerRef.current) {
const sheet = new FortuneSheet(containerRef.current, {
data: [
[
{ v: 'Category', ct: { fa: 'General', t: 'g' } },
{ v: 'Budgeted', ct: { fa: 'General', t: 'g' } },
{ v: 'Spent', ct: { fa: 'General', t: 'g' } },
{ v: 'Remaining', ct: { fa: 'General', t: 'g' } }
],
[
{ v: 'Food', ct: { fa: 'General', t: 'g' } },
{ v: 500, ct: { fa: 'General', t: 'n' } },
{ v: 320, ct: { fa: 'General', t: 'n' } },
{ v: '=B2-C2', ct: { fa: 'General', t: 'g' } }
],
[
{ v: 'Transportation', ct: { fa: 'General', t: 'g' } },
{ v: 200, ct: { fa: 'General', t: 'n' } },
{ v: 150, ct: { fa: 'General', t: 'n' } },
{ v: '=B3-C3', ct: { fa: 'General', t: 'g' } }
],
[
{ v: 'Entertainment', ct: { fa: 'General', t: 'g' } },
{ v: 300, ct: { fa: 'General', t: 'n' } },
{ v: 280, ct: { fa: 'General', t: 'n' } },
{ v: '=B4-C4', ct: { fa: 'General', t: 'g' } }
],
[
{ v: 'Total', ct: { fa: 'General', t: 'g' } },
{ v: '=SUM(B2:B4)', ct: { fa: 'General', t: 'g' } },
{ v: '=SUM(C2:C4)', ct: { fa: 'General', t: 'g' } },
{ v: '=SUM(D2:D4)', ct: { fa: 'General', t: 'g' } }
]
],
defaultRowHeight: 30,
defaultColWidth: 120,
allowEdit: true
});
setSheetInstance(sheet);
return () => {
if (sheet && sheet.destroy) {
sheet.destroy();
}
};
}
}, []);
return (
<div style={{ padding: '20px' }}>
<h2>Monthly Budget Tracker</h2>
<div
ref={containerRef}
style={{
width: '100%',
height: '400px',
border: '1px solid #ccc',
borderRadius: '4px'
}}
/>
</div>
);
}
export default BudgetTracker;
This example creates a functional budget tracker with:
- Headers for categories and columns
- Budgeted and spent amounts
- Formulas to calculate remaining amounts
- A total row with SUM formulas
- Full editing capabilities
Common Issues / Troubleshooting
Spreadsheet not rendering: Make sure you've imported the CSS file (
fortune-sheet/dist/fortune-sheet.css). Without the styles, the spreadsheet may not display correctly.Container has no height: The container div must have an explicit height. Use inline styles or CSS to set a height (e.g.,
height: '500px').Memory leaks: Always clean up the sheet instance in the useEffect cleanup function by calling
sheet.destroy()to prevent memory leaks.Data format errors: Ensure your data array follows the correct structure with cell objects containing
v(value) andct(cell type) properties.
Next Steps
Now that you have a basic understanding of Fortune Sheet:
- Learn about advanced features like cell formatting, conditional formatting, and data validation
- Explore how to save and load spreadsheet data
- Discover how to customize the appearance and add custom functions
- Check out the official repository: https://github.com/ruilisi/fortune-sheet
- Look for part 2 of this series for more advanced topics
Summary
You've successfully set up Fortune Sheet in your React application and created your first interactive spreadsheet. You can now create spreadsheets with editable cells, formulas, and custom data structures. The library provides a solid foundation for building data-intensive applications with Excel-like functionality.

Top comments (0)