I am facing an issue with my Next.js app where the React Data Grid table UI crashes after building and deploying with npm run build
and npm run start
. During development (npm run dev
), everything works fine. I'm seeking assistance to identify and resolve this issue.
After deploying with npm run build and npm run start
"use client";
import { useMemo, useState } from "react";
import { createPortal } from "react-dom";
import { faker } from "@faker-js/faker";
import "../../styles/globals.css";
import DataGrid, {
SelectCellFormatter,
SelectColumn,
textEditor,
type Column,
type SortColumn,
} from "react-data-grid";
const dateFormatter = new Intl.DateTimeFormat("en");
const currencyFormatter = new Intl.NumberFormat("en", {
style: "currency",
currency: "eur",
});
interface SummaryRow {
id: string;
totalCount: number;
yesCount: number;
}
interface Row {
id: number;
title: string;
client: string;
area: string;
country: string;
contact: string;
assignee: string;
progress: number;
startTimestamp: number;
endTimestamp: number;
budget: number;
transaction: string;
account: string;
version: string;
available: boolean;
}
function getColumns(direction: any): readonly Column<Row, SummaryRow>[] {
return [
SelectColumn,
{
key: "id",
name: "ID",
frozen: true,
resizable: false,
renderSummaryCell() {
return <strong>Total</strong>;
},
},
{
key: "title",
name: "Task",
frozen: true,
renderEditCell: textEditor,
renderSummaryCell({ row }) {
return `${row.totalCount} records`;
},
},
{
key: "client",
name: "Client",
width: "max-content",
draggable: true,
renderEditCell: textEditor,
},
{
key: "area",
name: "Area",
renderEditCell: textEditor,
},
{
key: "contact",
name: "Contact",
renderEditCell: textEditor,
},
{
key: "assignee",
name: "Assignee",
renderEditCell: textEditor,
},
{
key: "progress",
name: "Completion",
renderCell(props) {
const value = props.row.progress;
return (
<>
<progress max={100} value={value} style={{ inlineSize: 50 }} />{" "}
{Math.round(value)}%
</>
);
},
renderEditCell({ row, onRowChange, onClose }) {
return createPortal(
<div
dir={direction}
className={"dialogContainerClassname"}
onKeyDown={(event) => {
if (event.key === "Escape") {
onClose();
}
}}
>
<dialog open>
<input
autoFocus
type="range"
min="0"
max="100"
value={row.progress}
onChange={(e) =>
onRowChange({ ...row, progress: e.target.valueAsNumber })
}
/>
<menu>
<button type="button" onClick={() => onClose()}>
Cancel
</button>
<button type="button" onClick={() => onClose(true)}>
Save
</button>
</menu>
</dialog>
</div>,
document.body
);
},
editorOptions: {
displayCellContent: true,
},
},
{
key: "startTimestamp",
name: "Start date",
renderCell(props) {
return dateFormatter.format(props.row.startTimestamp);
},
},
{
key: "endTimestamp",
name: "Deadline",
renderCell(props) {
return dateFormatter.format(props.row.endTimestamp);
},
},
{
key: "budget",
name: "Budget",
renderCell(props) {
return currencyFormatter.format(props.row.budget);
},
},
{
key: "transaction",
name: "Transaction type",
},
{
key: "account",
name: "Account",
},
{
key: "version",
name: "Version",
renderEditCell: textEditor,
},
{
key: "available",
name: "Available",
renderCell({ row, onRowChange, tabIndex }) {
return (
<SelectCellFormatter
value={row.available}
onChange={() => {
onRowChange({ ...row, available: !row.available });
}}
tabIndex={tabIndex}
/>
);
},
renderSummaryCell({ row: { yesCount, totalCount } }) {
return `${Math.floor((100 * yesCount) / totalCount)}% ✔️`;
},
},
];
}
function rowKeyGetter(row: Row) {
return row.id;
}
function createRows(): readonly Row[] {
const now = Date.now();
const rows: Row[] = [];
for (let i = 0; i < 1000; i++) {
rows.push({
id: i,
title: `Task #${i + 1}`,
client: faker.company.name(),
area: faker.person.jobArea(),
country: faker.location.country(),
contact: faker.internet.exampleEmail(),
assignee: faker.person.fullName(),
progress: Math.random() * 100,
startTimestamp: now - Math.round(Math.random() * 1e10),
endTimestamp: now + Math.round(Math.random() * 1e10),
budget: 500 + Math.random() * 10500,
transaction: faker.finance.transactionType(),
account: faker.finance.iban(),
version: faker.system.semver(),
available: Math.random() > 0.5,
});
}
return rows;
}
type Comparator = (a: Row, b: Row) => number;
function getComparator(sortColumn: string): Comparator {
switch (sortColumn) {
case "assignee":
case "title":
case "client":
case "area":
case "country":
case "contact":
case "transaction":
case "account":
case "version":
return (a, b) => {
return a[sortColumn].localeCompare(b[sortColumn]);
};
case "available":
return (a, b) => {
return a[sortColumn] === b[sortColumn] ? 0 : a[sortColumn] ? 1 : -1;
};
case "id":
case "progress":
case "startTimestamp":
case "endTimestamp":
case "budget":
return (a, b) => {
return a[sortColumn] - b[sortColumn];
};
default:
throw new Error(`unsupported sortColumn: "${sortColumn}"`);
}
}
function Home() {
const [rows, setRows] = useState(createRows);
const [sortColumns, setSortColumns] = useState<readonly SortColumn[]>([]);
const [selectedRows, setSelectedRows] = useState(
(): ReadonlySet<number> => new Set()
);
const columns = useMemo(() => getColumns("ltr"), []);
const summaryRows = useMemo((): readonly SummaryRow[] => {
return [
{
id: "total_0",
totalCount: rows.length,
yesCount: rows.filter((r) => r.available).length,
},
];
}, [rows]);
const sortedRows = useMemo((): readonly Row[] => {
if (sortColumns.length === 0) return rows;
return [...rows].sort((a, b) => {
for (const sort of sortColumns) {
const comparator = getComparator(sort.columnKey);
const compResult = comparator(a, b);
if (compResult !== 0) {
return sort.direction === "ASC" ? compResult : -compResult;
}
}
return 0;
});
}, [rows, sortColumns]);
const gridElement = (
<DataGrid
rowKeyGetter={rowKeyGetter}
columns={columns}
rows={sortedRows}
defaultColumnOptions={{
sortable: true,
resizable: true,
}}
selectedRows={selectedRows}
onSelectedRowsChange={setSelectedRows}
onRowsChange={setRows}
sortColumns={sortColumns}
onSortColumnsChange={setSortColumns}
topSummaryRows={summaryRows}
bottomSummaryRows={summaryRows}
className="fill-grid"
direction="ltr"
/>
);
return (
<div>
<div className={"toolbarClassname"}></div>
{gridElement}
</div>
);
}
export default Home;
What I've Tried:
Working fine locally means that when I run npm run dev
Mention whether the issue persists only after deployment or also locally.
Expected Outcome:
Next.js Environment:
React version: 18
Next.js version: 14.1.0
react-data-grid version: 7.0.0-beta.42
Package manager: npm
react-data-grid built with zero runtime, that's why occurs this error. You should import data grid css file globally. It will be working fine in vite.