Excel-like grid component built with React, with editors, keyboard navigation, copy & paste, and the like
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Cell formatting functionality providing built-in formatters and infrastructure for custom cell display components.
Pre-built formatter components for common cell display scenarios.
const formatters = {
/** Basic cell formatter for simple value display */
SimpleCellFormatter: React.ComponentType<FormatterProps>;
/** Checkbox formatter for select-all functionality in headers */
SelectAll: React.ComponentType<FormatterProps>;
};
interface FormatterProps {
/** Cell value to format and display */
value: any;
/** Complete row data object */
row: any;
/** Column definition */
column: Column;
/** Row index */
rowIdx: number;
/** Whether the cell is currently selected */
isSelected?: boolean;
/** Whether row selection is enabled */
isRowSelected?: boolean;
/** Additional properties passed from column definition */
[key: string]: any;
}Basic formatter that displays cell values as text with null/undefined handling.
/**
* Simple cell formatter for displaying basic text values
* Handles null/undefined values gracefully
* Supports title attribute for hover tooltips
*/
const SimpleCellFormatter: React.ComponentType<FormatterProps>;Usage Example:
import ReactDataGrid, { formatters } from 'react-data-grid';
const columns = [
{ key: 'id', name: 'ID' },
{
key: 'name',
name: 'Name',
formatter: <formatters.SimpleCellFormatter />
},
{
key: 'description',
name: 'Description',
formatter: <formatters.SimpleCellFormatter />
}
];
<ReactDataGrid
columns={columns}
rowGetter={i => rows[i]}
rowsCount={rows.length}
minHeight={400}
/>Specialized formatter for rendering select-all checkboxes in header cells.
/**
* Select-all checkbox formatter for header cells
* Provides master checkbox to select/deselect all rows
* Works with rowSelection configuration
*/
const SelectAll: React.ComponentType<FormatterProps>;Usage Example:
import ReactDataGrid, { formatters } from 'react-data-grid';
const columns = [
{
key: 'select',
name: '',
formatter: <formatters.SelectAll />,
width: 60,
resizable: false,
sortable: false,
frozen: true
},
{ key: 'id', name: 'ID' },
{ key: 'name', name: 'Name' }
];
const rowSelection = {
showCheckbox: true,
enableShiftSelect: true,
onRowsSelected: (rows) => console.log('Selected:', rows),
onRowsDeselected: (rows) => console.log('Deselected:', rows),
selectBy: {
indexes: selectedIndexes
}
};
<ReactDataGrid
columns={columns}
rowGetter={i => rows[i]}
rowsCount={rows.length}
minHeight={400}
rowSelection={rowSelection}
/>Creating custom formatters for specialized cell display requirements.
/**
* Interface for custom formatter components
* Receives cell data and returns JSX for display
*/
interface CustomFormatter extends React.Component<FormatterProps> {
render(): JSX.Element;
}Custom Formatter Examples:
import React from 'react';
// Currency formatter
const CurrencyFormatter = ({ value }) => {
const formattedValue = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD'
}).format(value || 0);
return (
<div style={{ textAlign: 'right', fontWeight: 'bold' }}>
{formattedValue}
</div>
);
};
// Status badge formatter
const StatusFormatter = ({ value }) => {
const getStatusColor = (status) => {
switch (status) {
case 'active': return '#28a745';
case 'inactive': return '#dc3545';
case 'pending': return '#ffc107';
default: return '#6c757d';
}
};
return (
<span
style={{
padding: '4px 8px',
borderRadius: '4px',
backgroundColor: getStatusColor(value),
color: 'white',
fontSize: '12px',
fontWeight: 'bold'
}}
>
{value || 'unknown'}
</span>
);
};
// Progress bar formatter
const ProgressFormatter = ({ value }) => {
const percentage = Math.max(0, Math.min(100, value || 0));
return (
<div style={{ width: '100%', backgroundColor: '#e9ecef', borderRadius: '4px' }}>
<div
style={{
width: `${percentage}%`,
height: '20px',
backgroundColor: '#007bff',
borderRadius: '4px',
textAlign: 'center',
lineHeight: '20px',
color: 'white',
fontSize: '12px'
}}
>
{percentage}%
</div>
</div>
);
};
// Link formatter
const LinkFormatter = ({ value, row }) => {
return (
<a
href={value}
target="_blank"
rel="noopener noreferrer"
style={{ color: '#007bff', textDecoration: 'underline' }}
>
{row.linkText || 'View'}
</a>
);
};
// Image formatter
const ImageFormatter = ({ value }) => {
return value ? (
<img
src={value}
alt="Preview"
style={{
width: '40px',
height: '40px',
objectFit: 'cover',
borderRadius: '4px'
}}
/>
) : (
<div style={{
width: '40px',
height: '40px',
backgroundColor: '#f8f9fa',
border: '1px dashed #dee2e6',
borderRadius: '4px'
}} />
);
};
// Usage in columns
const columns = [
{ key: 'id', name: 'ID' },
{ key: 'name', name: 'Name' },
{
key: 'price',
name: 'Price',
formatter: <CurrencyFormatter />
},
{
key: 'status',
name: 'Status',
formatter: <StatusFormatter />
},
{
key: 'progress',
name: 'Progress',
formatter: <ProgressFormatter />
},
{
key: 'website',
name: 'Website',
formatter: <LinkFormatter />
},
{
key: 'avatar',
name: 'Avatar',
formatter: <ImageFormatter />,
width: 80
}
];Using formatters with conditional logic based on cell values or row data.
// Conditional cell styling formatter
const ConditionalFormatter = ({ value, row, column }) => {
const getStyle = () => {
if (column.key === 'amount' && value < 0) {
return { color: 'red', fontWeight: 'bold' };
}
if (column.key === 'status' && row.priority === 'high') {
return { backgroundColor: '#fff3cd', padding: '4px' };
}
return {};
};
return (
<div style={getStyle()}>
{value}
</div>
);
};
// Multi-value formatter
const TagsFormatter = ({ value }) => {
const tags = Array.isArray(value) ? value : [];
return (
<div style={{ display: 'flex', gap: '4px', flexWrap: 'wrap' }}>
{tags.map((tag, index) => (
<span
key={index}
style={{
padding: '2px 6px',
backgroundColor: '#e9ecef',
borderRadius: '12px',
fontSize: '11px',
color: '#495057'
}}
>
{tag}
</span>
))}
</div>
);
};
// Date formatter
const DateFormatter = ({ value }) => {
if (!value) return null;
const date = new Date(value);
const formattedDate = date.toLocaleDateString('en-US', {
year: 'numeric',
month: 'short',
day: 'numeric'
});
return (
<div title={date.toISOString()}>
{formattedDate}
</div>
);
};Column-level configuration for cell formatting and display.
interface FormattedColumn extends Column {
/** Custom formatter component for cell display */
formatter?: React.ComponentType<FormatterProps>;
/** Additional properties passed to formatter */
[key: string]: any;
}Complex formatting scenarios with interactive elements and custom styling.
// Interactive formatter with buttons
const ActionFormatter = ({ value, row, rowIdx }) => {
const handleEdit = () => {
console.log('Edit row:', rowIdx, row);
};
const handleDelete = () => {
console.log('Delete row:', rowIdx, row);
};
return (
<div style={{ display: 'flex', gap: '4px' }}>
<button
onClick={handleEdit}
style={{
padding: '2px 8px',
fontSize: '12px',
backgroundColor: '#007bff',
color: 'white',
border: 'none',
borderRadius: '4px',
cursor: 'pointer'
}}
>
Edit
</button>
<button
onClick={handleDelete}
style={{
padding: '2px 8px',
fontSize: '12px',
backgroundColor: '#dc3545',
color: 'white',
border: 'none',
borderRadius: '4px',
cursor: 'pointer'
}}
>
Delete
</button>
</div>
);
};
// Rich content formatter
const RichContentFormatter = ({ value, row }) => {
return (
<div style={{ padding: '4px' }}>
<div style={{ fontWeight: 'bold', fontSize: '14px' }}>
{row.title}
</div>
<div style={{ fontSize: '12px', color: '#6c757d' }}>
{row.subtitle}
</div>
<div style={{ fontSize: '11px', marginTop: '2px' }}>
{value}
</div>
</div>
);
};
// Sparkline formatter (requires chart library)
const SparklineFormatter = ({ value }) => {
const data = Array.isArray(value) ? value : [];
return (
<div style={{ width: '100px', height: '30px' }}>
{/* Would integrate with chart library like Chart.js or D3 */}
<svg width="100" height="30">
{data.map((point, index) => (
<circle
key={index}
cx={index * (100 / data.length)}
cy={30 - (point * 30)}
r="2"
fill="#007bff"
/>
))}
</svg>
</div>
);
};import React, { memo } from 'react';
// Optimized formatter with memoization
const OptimizedFormatter = memo(({ value, row }) => {
const expensiveCalculation = useMemo(() => {
return complexCalculation(value);
}, [value]);
return (
<div>
{expensiveCalculation}
</div>
);
});Install with Tessl CLI
npx tessl i tessl/npm-react-data-grid@6.1.1