JavaScript Data Grid
The LemonadeJS data grid is a lightweight (5KBytes) and highly customizable JavaScript component that provides a free (MIT) solution for rendering data in rows and columns. It offers features like search, filter, pagination, and in-cell editing, making it ideal for building complex interfaces. With its lightweight design and virtual scrolling, the data grid ensures fast and efficient performance, even with large datasets. Its flexibility allows easy configuration to suit specific requirements, providing developers with a powerful tool for creating scalable and interactive user interfaces.
You can utilize this component with Vanilla JavaScript, LemonadeJS, or React.
Documentation
Installation
npm install @lemonadejs/datagrid
Settings
Attribute |
Description |
data: Array<Object> | The data that will be displayed on the grid based on the columns attribute. |
columns: Array<Object> | Each object represents a column of data to be displayed. The key 'name' refers to the data object key. |
pagination?: Number | Enable the pagination and define the number of items per page. |
search?: Boolean | Enable the search. Default: false |
editable?: Boolean | The grid is editable. Default: false |
Instance
Property |
Description |
data: Array<Object> | Change the state of data. |
page: Number | Change the page index. |
pagination: Number | Enable pagination. |
search: Boolean | Enable search. |
sort: Function(sortBy: String, sortAsc: Boolean) | Sort the data. |
setValue: Function(x: Number | String, y: Number, value: String) | Set the value of a cell. |
Events
Event |
Description |
onsearch?: (self) => void | Called when a search happens. |
onchangepage?: (self) => void | Called when the user changes the page. |
onupdate?: (self, object) => void | Called when cell data is changed. |
Important points
- Reserved Properties: This library automatically generates an item for each index in the data array. Each array item contains two unique reserved properties, the el and parent, representing the DOM element and its parent self.
- Modifying Cell Values: When a user double-clicks a cell, it becomes editable. You can exit the edition mode by pressing 'Enter' or clicking on a different cell in the data grid.
Columns Options
The columns property regulates the presentation of columns on the JavaScript data grid, specifying characteristics such as the sequence of columns, their width, and the positioning of data within them.
Option |
Description |
name?: string | Determines the key of the data object to which the column refers. |
title: string | Required. Determines the text that will be displayed in the column Header. |
width?: string | This option specifies the width of the column and should be provided as a string with the unit of measurement, such as '200px' or '2.5em'. By default, the width is set to '100px'. |
align?: string | This option determines the alignment of the text within the cells of the column. It should be provided as a string with a valid entry. The available options are 'left', 'right', 'center', and 'justify'. By default, the alignment is set to 'left'. |
render?: (cell, x, y, value, instance) => void | This option allows you to override the default rendering of the column and instead render a specific value. It is particularly useful for rendering HTML elements or components. In the context of this property, the keyword 'self' refers to the current row being rendered. |
Examples
Basic vanilla example
How to use the data grid in vanilla implementations.
JavaScript example
<html>
<script src="https://cdn.jsdelivr.net/npm/lemonadejs/dist/lemonade.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@lemonadejs/datagrid/dist/index.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@lemonadejs/datagrid/dist/style.min.css" />
<div id='data-grid'></div>
<button onclick="goToPage2()">Go To Page 2</button>
<button onclick="setItemValue()">Change Value in 'Product' Second Line</button>
<script>
const datagrid = Datagrid(document.getElementById('data-grid'), {
data: [
{
id: 1,
name: "T-Shirt",
price: 19.99,
description: "This is a high-quality cotton t-shirt in a variety of colors and sizes.",
},
{
id: 2,
name: "Jeans",
price: 49.99,
description: "These are premium denim jeans in a slim-fit style.",
},
{
id: 3,
name: "Sneakers",
price: 79.99,
description: "These are comfortable and stylish sneakers in a range of colors.",
},
{
id: 4,
name: "Backpack",
price: 39.99,
description: "This is a durable and spacious backpack with multiple compartments.",
},
],
columns: [
{ name: 'name', title: 'Product', width: '200px', align: 'left' },
{ name: 'price', title: 'Price', width: '100px', align: 'center' },
{ name: 'description', title: 'Description', width: '300px', align: 'left' },
],
pagination: 2,
onupdate: () => {
console.log('Data grid was updated')
},
onchangepage: () => {
console.log('Data grid page changed')
}
});
// This function assigns a value to the second cell of the column 'name'.
const setItemValue = function () {
datagrid.setValue('name', 1, 'Blue Jeans')
}
// This function update the current page in pagination to 2.
const goToPage2 = function () {
datagrid.page = 1;
}
</script>
</html>
LemonadeJS example
import Datagrid from '@lemonadejs/datagrid';
import '@lemonadejs/datagrid/dist/style.css';
export default function App() {
const self = this;
self.data = [
{
id: 1,
name: "T-Shirt",
price: 19.99,
description: "This is a high-quality cotton t-shirt in a variety of colors and sizes.",
},
{
id: 2,
name: "Jeans",
price: 49.99,
description: "These are premium denim jeans in a slim-fit style.",
},
{
id: 3,
name: "Sneakers",
price: 79.99,
description: "These are comfortable and stylish sneakers in a range of colors.",
},
{
id: 4,
name: "Backpack",
price: 39.99,
description: "This is a durable and spacious backpack with multiple compartments.",
},
]
self.columns = [
{ name: 'name', title: 'Product', width: '200px', align: 'left' },
{ name: 'price', title: 'Price', width: '100px', align: 'center' },
{ name: 'description', title: 'Description', width: '300px', align: 'left' },
]
// This function update the current page in pagination to 2.
const goToPage2 = function () {
self.ref.page = 1;
}
// This function assigns a value to the second cell of the column 'name'.
const setItemValue = function () {
self.ref.setValue('name', 2, 'Blue Jeans')
}
return `<div style="display: flex; justify-content: space-evenly">
<Datagrid
data="{{self.data}}"
columns="{{self.columns}}"
onupdate="console.log('Data grid was updated')"
onchangepage="console.log('Data grid page changed')"
:pagination="2"
:ref="self.ref" />
<input type="button" value="Go to Page 2" onclick="self.goToPage2()" />
<input type="button" value="Change Value in 'Name' Second Line" onclick="self.setItemValue()" />
</div>`
}
React example
import React, { useRef, useEffect, useState } from "react";
import Datagrid from '@lemonadejs/datagrid';
import '@lemonadejs/datagrid/dist/style.css';
export default function App() {
const domRef = useRef();
const datagrid = useRef();
const [columns, setColumns] = useState([
{ name: 'name', title: 'Product', width: '200px', align: 'left' },
{ name: 'price', title: 'Price', width: '100px', align: 'center' },
{ name: 'description', title: 'Description', width: '300px', align: 'left' },
])
const [data, setData] = useState([
{
id: 1,
name: "T-Shirt",
price: 19.99,
description: "This is a high-quality cotton t-shirt in a variety of colors and sizes.",
},
{
id: 2,
name: "Jeans",
price: 49.99,
description: "These are premium denim jeans in a slim-fit style.",
},
{
id: 3,
name: "Sneakers",
price: 79.99,
description: "These are comfortable and stylish sneakers in a range of colors.",
},
{
id: 4,
name: "Backpack",
price: 39.99,
description: "This is a durable and spacious backpack with multiple compartments.",
},
])
useEffect(() => {
if (! datagrid.current.innerText) {
datagrid.current = Datagrid(domRef.current, {
data,
column,
onupdate: () => {
console.log('Data grid was updated')
},
onchangepage: () => {
console.log('Data grid page changed')
}
});
}
}, []);
// This function assigns a value to the second cell of the column 'name'.
const setItemValue = function () {
datagrid.current.setValue('name', 1, 'Blue Jeans')
}
// This function update the current page in pagination to 2.
const goToPage2 = function () {
datagrid.current.page = 1;
}
return (<>
<div ref={domRef}></div>
<button onclick={() => goToPage2()}>Go To Page 2</button>
<button onclick={() => setItemValue()}>Change Value in 'Name' Second Line</button>
</>);
}
Working with large Data Sets
The following example demonstrates the performance of the data grid when handling large data sets.
In this example, the mock data is retrieved from fakerapi.it, which is a free API for generating fake data.
JavaScript example
<html>
<script src="https://cdn.jsdelivr.net/npm/lemonadejs/dist/lemonade.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@lemonadejs/datagrid/dist/index.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@lemonadejs/datagrid/dist/style.min.css" />
<div id='data-grid'></div>
<script>
const datagrid = Datagrid(document.getElementById('data-grid'), {
data: [],
columns: [
{ name: 'firstname', title: 'First Name', width: '100px', align: 'center' },
{ name: 'lastname', title: 'Last Name', width: '100px', align: 'center' },
{ name: 'email', title: 'Email', width: '250px', align: 'left' },
{ name: 'phone', title: 'Phone', width: '150px', align: 'center' },
{ name: 'address.country', title: 'Country', width: '200px', align: 'left' },
],
pagination: 10
})
fetch('https://fakerapi.it/api/v1/persons?_quantity=500&_seed=1')
.then(response => response.clone().json())
.then(body => {
datagrid.data = body.data;
})
</script>
</html>
LemonadeJS example
import Datagrid from '@lemonadejs/datagrid';
import '@lemonadejs/datagrid/dist/style.css';
export default function App() {
const self = this;
self.data = []
self.columns = [
{ name: 'firstname', title: 'First Name', width: '100px', align: 'center' },
{ name: 'lastname', title: 'Last Name', width: '100px', align: 'center' },
{ name: 'email', title: 'Email', width: '250px', align: 'left' },
{ name: 'phone', title: 'Phone', width: '150px', align: 'center' },
{ name: 'address.country', title: 'Country', width: '200px', align: 'left' },
]
fetch('https://fakerapi.it/api/v1/persons?_quantity=500&_seed=1')
.then(response => response.clone().json())
.then(body => {
self.data = body.data;
})
return `<Datagrid data="{{self.data}}" columns="{{self.columns}}" pagination="10" search="true" />`;
}
React example
import React, { useRef, useEffect } from "react";
import Datagrid from '@lemonadejs/datagrid';
import '@lemonadejs/datagrid/dist/style.css';
export default function App() {
const dom = useRef();
const datagrid = useRef();
useEffect(() => {
if (! datagrid.current.innerText) {
datagrid.current = Datagrid(dom.current, {
data: [],
columns: [
{ name: 'firstname', title: 'First Name', width: '100px', align: 'center' },
{ name: 'lastname', title: 'Last Name', width: '100px', align: 'center' },
{ name: 'email', title: 'Email', width: '250px', align: 'left' },
{ name: 'phone', title: 'Phone', width: '150px', align: 'center' },
{ name: 'address.country', title: 'Country', width: '200px', align: 'left' },
]
});
fetch('https://fakerapi.it/api/v1/persons?_quantity=500&_seed=1')
.then(response => response.clone().json())
.then(body => {
datagrid.current.data = body.data;
})
}
}, []);
return (
<div ref={dom}></div>
);
}
Render with two Data grids
See this example on codesandbox
The following example showcases how to utilize the render property to enable HTML rendering within cells. To successfully solve the puzzle depicted in this example, the column numbers within each grid should sum up to 10, and each group within its respective grid must consist of unique values.
Javascript example
<html>
<script src="https://cdn.jsdelivr.net/npm/lemonadejs/dist/lemonade.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@lemonadejs/datagrid/dist/index.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@lemonadejs/datagrid/dist/style.min.css" />
<div style="display: flex; justify-content: space-evenly;">
<div>
<h3>Data Grid 1</h3>
<div id='data-grid1'></div>
</div>
<div>
<h3>Data Grid 2</h3>
<div id='data-grid2'></div>
</div>
</div>
<script>
const check = function () {
if (datagrid1.data.reduce((acc, curr) => acc + curr.number, 0) == 10) {
if (datagrid1.data.every((obj, index) => {
const sameKeyValues = datagrid1.data.filter(item => item['group'] === obj['group']);
return sameKeyValues.length === 1;
})) {
alert('You solved it!')
}
}
}
const send = function (row, destination) {
if (destination == 2) {
datagrid1.splice(datagrid1.indexOf(row), 1)
datagrid2.push({ number: row.number, group: row.group })
} else if (destination == 1) {
datagrid2.splice(datagrid2.indexOf(row), 1)
datagrid1.push({ number: row.number, group: row.group })
}
datagrid1.refresh('data');
datagrid2.refresh('data');
setTimeout(() => check(), 100)
}
const datagrid1 = Datagrid(document.getElementById('data-grid1'), {
data: [
{ number: 2, group: "B" },
{ number: 1, group: "C" },
{ number: 2, group: "C" },
],
columns: [
{
name: 'group',
title: 'Group',
width: '100px',
align: 'center' },
{
name: 'actions',
title: 'Actions',
width: '100px',
render: function (e, x, y, value, instance) {
e.innerHTML = `<input type="button" class="jbutton dark" value="Send to Grid 2" />`
e.onclick = send.bind(this, instance, 2)
}
},
]
})
const datagrid2 = Datagrid(document.getElementById('data-grid2'), {
data: [
{ number: 6, group: "A" },
{ number: 2, group: "A" },
{ number: 3, group: "B" },
{ number: 4, group: "D" }
],
columns: [
{
name: 'number',
title: 'Number',
width: '100px',
align: 'center' },
{
name: 'actions',
title: 'Actions',
width: '100px',
render: function (e, x, y, value, instance) {
e.innerHTML = `<input type="button" class="jbutton dark" value="Send to Grid 1" />`
e.onclick = send.bind(this, instance, 1)
}
},
]
})
</script>
</html>
LemonadeJS example
import Datagrid from '@lemonadejs/datagrid';
import '@lemonadejs/datagrid/dist/style.css';
export default function App() {
const self = this;
self.data1 = [
{ number: 2, group: "B" },
{ number: 1, group: "C" },
{ number: 2, group: "C" },
];
self.data2 = [
{ number: 6, group: "A" },
{ number: 2, group: "A" },
{ number: 3, group: "B" },
{ number: 4, group: "D" }
];
self.columns1 = [
{
name: 'group',
title: 'Group',
width: '100px',
align: 'center' },
{
name: 'actions',
title: 'Actions',
width: '100px',
render: function (e, x, y, value, instance) {
e.innerHTML = `<input type="button" class="jbutton dark" value="Send to Grid 2" />`
e.onclick = self.send.bind(self, instance, 2)
}
},
]
self.columns2 = [
{
name: 'number',
title: 'Number',
width: '100px',
align: 'center' },
{
name: 'actions',
title: 'Actions',
width: '100px',
render: function (e, x, y, value, instance) {
e.innerHTML = `<input type="button" class="jbutton dark" value="Send to Grid 1" />`
e.onclick = self.send.bind(self, instance, 1)
}
},
]
self.send = function (row, destination) {
if (destination == 2) {
self.data1.splice(self.data1.indexOf(row), 1)
self.data2.push({ number: row.number, group: row.group })
} else if (destination == 1) {
self.data2.splice(self.data2.indexOf(row), 1)
self.data1.push({ number: row.number, group: row.group })
}
self.grid1.refresh('data');
self.grid2.refresh('data');
setTimeout(() => self.check(), 100)
}
self.check = function () {
if (self.grid1.data.reduce((acc, curr) => acc + curr.number, 0) == 10) {
if (self.grid1.data.every((obj, index) => {
const sameKeyValues = self.grid1.data.filter(item => item['group'] === obj['group']);
return sameKeyValues.length === 1;
})) {
alert('You solved it!')
}
}
}
return `<div style="display: flex; justify-content: space-evenly;">
<div>
<h2>Grid 1</h2>
<Datagrid data="{{self.data1}}" columns="{{self.columns1}}" :ref="self.grid1" />
</div>
<div>
<h2>Grid 2</h2>
<Datagrid data="{{self.data2}}" columns="{{self.columns2}}" :ref="self.grid2" />
</div>
</div>`
}
React example
import React, { useRef, useEffect, use } from "react";
import Datagrid from '@lemonadejs/datagrid';
import '@lemonadejs/datagrid/dist/style.css';
default export function App() {
const datagrid1 = useRef();
const datagrid2 = useRef();
const dgDom1 = useRef();
const dgDom2 = useRef();
const [data1, setData1] = useState([
{ number: 2, group: "B" },
{ number: 1, group: "C" },
{ number: 2, group: "C" },
])
const [data2, setData2] = useState([
{ number: 6, group: "A" },
{ number: 2, group: "A" },
{ number: 3, group: "B" },
{ number: 4, group: "D" }
])
useEffect(() => {
if (! datagrid.current.innerText) {
datagrid1.current = Datagrid(dgDom1.current, {
data: data1,
columns: [
{
name: 'group',
title: 'Group',
width: '100px',
align: 'center' },
{
name: 'actions',
title: 'Actions',
width: '100px',
render: function (e, x, y, value, instance) {
e.innerHTML = `<input type="button" class="jbutton dark" value="Send to Grid 2" />`
e.onclick = self.send.bind(self, instance, 2)
}
},
]
})
datagrid2.current = Datagrid(dgDom2.current, {
data: data2,
columns: [
{
name: 'number',
title: 'Number',
width: '100px',
align: 'center' },
{
name: 'actions',
title: 'Actions',
width: '100px',
render: function (e, x, y, value, instance) {
e.innerHTML = `<input type="button" class="jbutton dark" value="Send to Grid 1" />`
e.onclick = self.send.bind(self, instance, 1)
}
},
]
})
}
})
const check = function () {
if (data1.reduce((acc, curr) => acc + curr.number, 0) == 10) {
if (data1.every((obj, index) => {
const sameKeyValues = data1.filter(item => item['group'] === obj['group']);
return sameKeyValues.length === 1;
})) {
alert('You solved it!')
}
}
}
const send = function (row, destination) {
if (destination == 2) {
setData1(data1.splice(data1.indexOf(row), 1))
setData2(data2.push({ number: row.number, group: row.group }))
} else if (destination == 1) {
setData1(data2.splice(data2.indexOf(row), 1))
setData2(data1.push({ number: row.number, group: row.group }))
}
datagrid1.refresh('data');
datagrid2.refresh('data');
setTimeout(() => check(), 100)
}
return (<div style="display: flex; justify-content: space-evenly;">
<div>
<h2>Grid 1</h2>
<div ref={dgDom1}/>
</div>
<div>
<h2>Grid 2</h2>
<div ref={dgDom2}/>
</div>
</div>)
}
React Wrapper
React Wrapper: Use the Data Grid React component to integrate the grid into your React applications seamlessly. This wrapper simplifies the process, allowing you to display, manipulate, and interact with large datasets effortlessly using React's declarative and component-based approach.
Installing the React Wrapper
npm install @lemonadejs/react-data-grid
Data Grid Example
import React, { useRef, useState } from "react";
import Datagrid from '@lemonadejs/react-data-grid';
import '@lemonadejs/datagrid/dist/style.css';
default export function App() {
const datagrid = useRef();
const [columns, setColumns] = useState([
{ name: 'name', title: 'Product', width: '200px', align: 'left' },
{ name: 'price', title: 'Price', width: '100px', align: 'center' },
{ name: 'description', title: 'Description', width: '300px', align: 'left' },
])
const [data, setData] = useState([
{
id: 1,
name: "T-Shirt",
price: 19.99,
description: "This is a high-quality cotton t-shirt in a variety of colors and sizes.",
},
{
id: 2,
name: "Jeans",
price: 49.99,
description: "These are premium denim jeans in a slim-fit style.",
},
{
id: 3,
name: "Sneakers",
price: 79.99,
description: "These are comfortable and stylish sneakers in a range of colors.",
},
{
id: 4,
name: "Backpack",
price: 39.99,
description: "This is a durable and spacious backpack with multiple compartments.",
},
])
// This function assigns a value to the second cell of the column 'name'.
const setItemValue = function () {
datagrid.current.setValue('name', 1, 'Blue Jeans')
}
// This function update the current page in pagination to 2.
const goToPage2 = function () {
datagrid.current.page = 1;
}
return (<>
<Datagrid
ref={datagrid}
data={data}
columns={columns}
pagination={2}
onupdate={() => console.log('Datagrid was updated')}
onchangepage={() => console.log('Datagrid page changed')}
/>
<button onclick={() => goToPage2()}>Go To Page 2</button>
<button onclick={() => setItemValue()}>Change Value in 'Name' Second Line</button>
</>);
}
Enterprise Data Grid
Jspreadsheet
Jspreadsheet is a remarkable commercial
data grid solution that offers a lightweight and efficient platform for building professional-grade data grids. It stands out with its Excel-like controls, providing users with a familiar and intuitive interface for data manipulation. With Jspreadsheet, developers can effortlessly create stunning and highly functional data grids that meet the highest standards of usability and aesthetics. Its lightweight design ensures optimal performance, allowing for seamless data rendering and interaction. Whether organizing, sorting, filtering, or performing complex calculations, Jspreadsheet empowers users to create fantastic, sophisticated data grids tailored to their needs.
Jspreadsheet Data grid