
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
react-excel-lite
Advanced tools
A lightweight, Excel-like editable grid component for React with cell selection, copy/paste, auto-fill with arithmetic sequence detection, and customizable styling.
A lightweight, Excel-like editable grid component for React.
npm install react-excel-lite
import { useState } from "react";
import { ExcelGrid } from "react-excel-lite";
function App() {
const [data, setData] = useState([
["100", "200", "300"],
["400", "500", "600"],
["700", "800", "900"],
]);
return <ExcelGrid data={data} onChange={setData} />;
}
| Prop | Type | Required | Description |
|---|---|---|---|
data | string[][] | Yes | 2D array of strings |
onChange | (data: string[][]) => void | Yes | Callback when data changes |
rowHeaders | HeaderGroup[] | No | Grouped row headers |
colHeaders | HeaderGroup[] | No | Grouped column headers |
className | string | No | CSS class for container |
rowHeaderTitle | string | No | Title for row header column |
styles | GridStyles | No | Style configuration object |
cellStyles | (coord: CellCoord) => string|undefined | No | Function to style individual cells |
import { useState } from "react";
import { ExcelGrid } from "react-excel-lite";
import type { HeaderGroup } from "react-excel-lite";
function App() {
const [data, setData] = useState([
["100", "200", "300", "400"],
["500", "600", "700", "800"],
]);
const colHeaders: HeaderGroup[] = [
{
label: "Q1",
description: "First quarter",
headers: [
{ key: "jan", label: "Jan", description: "January" },
{ key: "feb", label: "Feb", description: "February" },
],
},
{
label: "Q2",
description: "Second quarter",
headers: [
{ key: "mar", label: "Mar", description: "March" },
{ key: "apr", label: "Apr", description: "April" },
],
},
];
const rowHeaders: HeaderGroup[] = [
{
label: "Products",
description: "Product categories",
headers: [
{ key: "prodA", label: "Product A", description: "Main product line" },
{ key: "prodB", label: "Product B", description: "Secondary product" },
],
},
];
return (
<ExcelGrid
data={data}
onChange={setData}
colHeaders={colHeaders}
rowHeaders={rowHeaders}
rowHeaderTitle="Category"
/>
);
}
When all HeaderGroups have no label, the grid displays a single header row/column instead of two levels:
const colHeaders: HeaderGroup[] = [
{
// No label - single header row
headers: [
{ key: "jan", label: "Jan" },
{ key: "feb", label: "Feb" },
],
},
{
headers: [
{ key: "mar", label: "Mar" },
{ key: "apr", label: "Apr" },
],
},
];
const rowHeaders: HeaderGroup[] = [
{
// No label - single header column
headers: [
{ key: "row1", label: "Row 1" },
{ key: "row2", label: "Row 2" },
],
},
];
<ExcelGrid
data={data}
onChange={setData}
colHeaders={colHeaders}
rowHeaders={rowHeaders}
/>;
If at least one group has a label, the grid shows the two-level layout (group labels + individual headers).
The component comes with sensible default styles built-in. You can customize styles using the styles prop with CSS class strings from any styling solution.
Out of the box, the grid has:
import type { GridStyles } from "react-excel-lite";
const styles: GridStyles = {
cell: "text-sm",
selected: "bg-purple-100 ring-2 ring-inset ring-purple-500",
fillTarget: "bg-purple-50",
fillHandle: "bg-purple-500",
colGroup: "bg-purple-100 text-purple-700",
colHeader: "bg-purple-50",
rowHeader: "bg-slate-200",
};
<ExcelGrid data={data} onChange={setData} styles={styles} />;
import styles from "./grid.module.css";
import type { GridStyles } from "react-excel-lite";
const gridStyles: GridStyles = {
selected: styles.selectedCell,
fillTarget: styles.fillTargetCell,
fillHandle: styles.fillHandle,
};
<ExcelGrid data={data} onChange={setData} styles={gridStyles} />;
const styles: GridStyles = {
selected: "my-selected-cell",
fillTarget: "my-fill-target",
fillHandle: "my-fill-handle",
};
<ExcelGrid data={data} onChange={setData} styles={styles} />;
/* styles.css */
.my-selected-cell {
background-color: #f3e8ff;
outline: 2px solid #a855f7;
outline-offset: -2px;
}
.my-fill-target {
background-color: #faf5ff;
}
.my-fill-handle {
background-color: #a855f7;
}
interface GridStyles {
cell?: string; // CSS class for data cells
selected?: string; // CSS class for selected cells (overrides default)
fillTarget?: string; // CSS class for fill target cells (overrides default)
fillHandle?: string; // CSS class for fill handle (overrides default)
colGroup?: string; // CSS class for column group headers
colHeader?: string; // CSS class for individual column headers
rowHeader?: string; // CSS class for row headers
}
Style individual column headers and groups:
const colHeaders: HeaderGroup[] = [
{
label: "Revenue",
className: "bg-green-100 text-green-700",
headers: [
{ key: "q1r", label: "Q1", className: "bg-green-50" },
{ key: "q2r", label: "Q2", className: "bg-green-50" },
],
},
];
Style individual row headers:
const rowHeaders: HeaderGroup[] = [
{
label: "Regions",
className: "bg-slate-700 text-white",
headers: [
{
key: "regionA",
label: "Region A",
className: "bg-slate-600 text-white",
},
{
key: "regionB",
label: "Region B",
className: "bg-slate-500 text-white",
},
],
},
];
Use the cellStyles prop to apply styles to specific cells based on their coordinates:
import { useCallback } from "react";
import type { CellCoord } from "react-excel-lite";
function App() {
const [data, setData] = useState([
["100", "200", "300"],
["400", "500", "600"],
["700", "800", "900"],
]);
// Memoize to prevent unnecessary re-renders
const cellStyles = useCallback((coord: CellCoord) => {
// Highlight first row
if (coord.row === 0) return "bg-yellow-100";
// Highlight specific cell
if (coord.row === 1 && coord.col === 1) return "bg-red-100 font-bold";
// Highlight cells with negative values (check data)
return undefined;
}, []);
return <ExcelGrid data={data} onChange={setData} cellStyles={cellStyles} />;
}
Common use cases:
// Alternating row colors
const cellStyles = useCallback((coord: CellCoord) => {
return coord.row % 2 === 0 ? "bg-gray-50" : "bg-white";
}, []);
// Value-based styling (check data in callback)
const cellStyles = useCallback(
(coord: CellCoord) => {
const value = Number(data[coord.row]?.[coord.col]);
if (value < 0) return "bg-red-100 text-red-700";
if (value > 1000) return "bg-green-100 text-green-700";
return undefined;
},
[data],
);
Select cells with a numeric pattern and drag the fill handle to auto-fill:
1, 2, 3 → drag down → 4, 5, 6, 7, ...100, 200, 300 → drag down → 400, 500, 600, ...10, 8, 6 → drag down → 4, 2, 0, -2, ...| Shortcut | Action |
|---|---|
Arrow Keys | Move selection |
Shift + Arrow Keys | Extend selection range |
Enter | Enter edit mode (select all text) |
Any character | Enter edit mode and start typing |
Escape | Exit edit mode |
Ctrl+C / Cmd+C | Copy selected cells |
Ctrl+V / Cmd+V | Paste from clipboard |
Delete / Backspace | Clear selected cells |
ExcelGrid - Main grid componentGridCell - Individual cell componentuseGridSelection - Cell selection logicuseGridClipboard - Copy/paste and keyboard navigation logicuseGridDragFill - Fill handle logiccn - Classname merge utilitycoordToKey - Convert coordinate to string keykeyToCoord - Convert string key to coordinategetCellsInRange - Get all cells in a rangeisCellInRange - Check if cell is in rangeparseTSV - Parse TSV string to 2D arraytoTSV - Convert 2D array to TSV stringnormalizeRange - Normalize selection rangegetFillTargetCells - Get fill target cellsinterface CellCoord {
row: number;
col: number;
}
interface SelectionRange {
start: CellCoord | null;
end: CellCoord | null;
}
interface Header {
key: string;
label: string;
description?: string;
className?: string;
}
interface HeaderGroup {
label?: string;
headers: Header[];
description?: string;
className?: string;
}
interface GridStyles {
cell?: string;
selected?: string;
fillTarget?: string;
fillHandle?: string;
colGroup?: string;
colHeader?: string;
rowHeader?: string;
}
interface ExcelGridProps {
data: string[][];
onChange: (data: string[][]) => void;
rowHeaders?: HeaderGroup[];
colHeaders?: HeaderGroup[];
className?: string;
rowHeaderTitle?: string;
styles?: GridStyles;
cellStyles?: (coord: CellCoord) => string | undefined;
}
MIT License © 2025 prkgnt
FAQs
A lightweight, Excel-like editable grid component for React with cell selection, copy/paste, auto-fill with arithmetic sequence detection, and customizable styling.
We found that react-excel-lite demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.