lara-light-indigo

DataTable

DataTable displays data in tabular format.

Customers
Name
Country
Agent
Date
Balance
Status
Activity
Showing 0 to 0 of 0 entries
10
Import via Module

import { DataTable } from 'primereact/datatable';
 
Import via CDN

<script src="https://unpkg.com/primereact/core/core.min.js"></script>
<script src="https://unpkg.com/primereact/column/column.min.js"></script>
<script src="https://unpkg.com/primereact/datatable/datatable.min.js"></script>
 
Getting Started

DataTable requires a value as an array of objects and columns defined with Column component. Throughout the samples, a product interface having code, name, description, image, category, quantity, price, inventoryStatus and rating properties is used to define an object to be displayed by the datatable. Products are loaded by a CustomerService that connects to a server to fetch the products. Note that this is only for demo purposes, DataTable does not have any restrictions on how data is provided.


export default class ProductService {

    getProductsSmall() {
		return fetch('data/products-small.json').then(res => res.json()).then(d => d.data);
	}

	getProducts() {
		return fetch('data/products.json').then(res => res.json()).then(d => d.data);
    }

    getProductsWithOrdersSmall() {
		return fetch('data/products-orders-small.json').then(res => res.json()).then(d => d.data);
	}
}
 

Following sample datatable has 4 columns and retrieves the data from a service on componentDidMount.


export const DataTableDemo = () => {

    const [products, setProducts] = useState([])

    useEffect(() => {
        productService = new ProductService();
        productService.getProductsSmall().then(data => setProducts(data));
    }, [])

    return (
        <DataTable value={products}>
            <Column field="code" header="Code"></Column>
            <Column field="name" header="Name"></Column>
            <Column field="category" header="Category"></Column>
            <Column field="quantity" header="Quantity"></Column>
        </DataTable>
    );

}
 

Dynamic columns are also possible by creating the column component dynamically.


export const DataTableDemo = () => {

    const [products, setProducts] = useState([]);

    const columns = [
        {field: 'code', header: 'Code'},
        {field: 'name', header: 'Name'},
        {field: 'category', header: 'Category'},
        {field: 'quantity', header: 'Quantity'}
    ];

    useEffect(() => {
        productService = new ProductService();
        productService.getProductsSmall().then(data => setProducts(data));
    }, [])

    const dynamicColumns = columns.map((col,i) => {
        return <Column key={col.field} field={col.field} header={col.header} />;
    });

    return (
        <DataTable value={products}>
            {dynamicColumns}
        </DataTable>
    );
}
 
Column Component

Column component defines various options to specify corresponding features.

Properties
NameTypeDefaultDescription
columnKeystringnullIdentifier of a column if field property is not defined. Only utilized by reorderableColumns feature at the moment.
fieldstringnullProperty of a row data.
sortFieldstringnullProperty of a row data used for sorting, defaults to field.
filterFieldstringnullProperty of a row data used for filtering, defaults to field.
exportFieldstringnullProperty of a row data used for exporting, defaults to field.
headeranynullHeader content of the column.
bodyanynullBody content of the column.
bodyStyleobjectnullInline style of the body.
bodyClassNamestring | functionnullStyle class of the body. If using a function must return a string.
footeranynullFooter content of the column.
sortablebooleanfalseDefines if a column is sortable.
sortFunctionfunctionnullSort function for custom sorting.
sortableDisabledbooleanfalseWhen enabled, the data of columns with this property cannot be sorted or changed by the user.
dataTypestringnullDepending on the dataType of the column, suitable match modes are displayed.
filterbooleanfalseDefines if a column can be filtered.
filterMatchModestringnullDefines filterMatchMode; "startsWith", "contains", "endsWith", "equals", "notEquals", "in", "lt", "lte", "gt", "gte" and "custom".
filterTypestringtextType of the filter input field.
filterPlaceholderstringnullDefines placeholder of the input fields.
filterMaxlengthnumbernullSpecifies the maximum number of characters allowed in the filter element.
filterElementobjectnullElement for custom filtering.
filterFunctionfunctionnullCustom filter function.
excludeGlobalFilterbooleanfalseWhether to exclude from global filtering or not.
filterHeaderStyleobjectnullInline style of the filter header.
filterHeaderClassNamestringnullStyle class of the filter header.
showFilterMenubooleantrueWhether to display the filter overlay.
showFilterOperatorbooleantrueWhen enabled, match all and match any operator selector is displayed.
showClearButtonbooleantrueDisplays a button to clear the column filtering.
showApplyButtonbooleantrueDisplays a button to apply the column filtering.
showFilterMatchModesbooleantrueWhether to show the match modes selector.
showFilterMenuOptionsbooleantrueWhether to show the match modes selector and match operator selector.
showAddButtonbooleantrueWhen enabled, a button is displayed to add more rules.
filterMatchModeOptionsarraynullAn array of label-value pairs to override the global match mode options.
maxConstraintsnumber2Maximum number of constraints for a column filter.
filterMenuClassNamestringnullStyle class of the column filter overlay.
filterMenuStyleobjectnullInline style of the column filter overlay.
alignstringnullAligns the content of the column, valid values are left, right and center.
alignHeaderstringnullAligns the header of the column, valid values are left, right and center.
alignFrozenstringleftPosition of a frozen column, valid values are left and right.
hiddenbooleanfalseWhether the column is rendered.
onFilterClearfunctionnullCallback to invoke when the filter meta is cleared.
onFilterApplyClickfunctionnullCallback to invoke when the apply button is clicked.
onFilterMatchModeChangefunctionnullCallback to invoke when the match mode option is changed.
onFilterOperatorChangefunctionnullCallback to invoke when the filter operator option is changed.
onFilterConstraintAddfunctionnullCallback to invoke when a new constraint is added.
onFilterConstraintRemovefunctionnullCallback to invoke when a constraint is removed.
filterClearanynullTemplate of clear element in menu.
filterApplyanynullTemplate of apply element in menu.
filterHeaderanynullTemplate of header element in menu.
filterFooteranynullTemplate of footer element in menu.
styleobjectnullInline style of the column.
classNamestringnullStyle class of the column.
headerStyleobjectnullInline style of the header.
headerClassNamestringnullStyle class of the header.
headerTooltipanynullContent of the header tooltip.
headerTooltipOptionsobjectnullConfiguration of the header tooltip, refer to the tooltip documentation for more information.
footerStyleobjectnullInline style of the footer.
footerClassNamestringnullStyle class of the footer.
expanderbooleanfalseDisplays an icon to toggle row expansion.
frozenbooleanfalseWhether the column is fixed in horizontal scrolling or not.
selectionModestringnullDefines column based selection mode, options are "single" and "multiple".
colSpannumbernullNumber of columns to span for grouping.
rowSpannumbernullNumber of rows to span for grouping.
editorfunctionnullFunction to provide the cell editor input.
cellEditValidatorfunctionnullValidator function to validate the cell input value.
cellEditValidatorEventstringclickEvent to trigger the validation, possible values are "click" and "blur".
onBeforeCellEditShowfunctionnullCallback to invoke before the cell editor is shown.
onBeforeCellEditHidefunctionnullCallback to invoke before the cell editor is hidden.
onCellEditInitfunctionnullCallback to invoke when cell edit is initiated.
onCellEditCompletefunctionnullCallback to execute when editor is submitted.
onCellEditCancelfunctionnullCallback to execute when editor is cancelled.
rowReorderbooleanfalseWhether this column displays an icon to reorder the rows.
rowReorderIconstringpi pi-barsIcon of the drag handle to reorder rows.
rowEditorbooleanfalseDisplays icons to edit row.
exportablebooleantrueDefines whether the column is exported or not.
reorderablebooleannullUsed to defined reorderableColumns per column when reorderableColumns of table is enabled, defaults to value of reorderableColumns.
resizeablebooleannullUsed to defined resizeableColumns per column when resizeableColumns of table is enabled, defaults to value of resizeableColumns.
Table Layout

Default table-layout is fixed meaning the cell widths do not depend on their content. If you require cells to scale based on their contents set autoLayout property to true. Note that Scrollable and/or Resizable tables do not support auto layout due to technical limitations.

Templates

Field data of a corresponding row is displayed as the cell content by default, this can be customized using templating where current row data and column properties are passed to the body template. On the other hand, header and footer properties of a column are used to define the content of these sections by accepting either simple string values or JSX for advanced content. Similarly DataTable itself also provides header and footer properties for the main header and footer of the table.


export const DataTableTemplatingDemo = () => {

    const [products, setProducts] = useState([])

    useEffect(() => {
        productService = new ProductService();
        productService.getProductsSmall().then(data => setProducts(data));
    }, []);

    const formatCurrency = (value) => {
        return value.toLocaleString('en-US', { style: 'currency', currency: 'USD' });
    }

    const imageBodyTemplate = (rowData) => {
        return <img src={`images/product/${rowData.image}`} alt={rowData.image} className="product-image" />;
    }

    const priceBodyTemplate = (rowData) => {
        return formatCurrency(rowData.price);
    }

    const ratingBodyTemplate = (rowData) => {
        return <Rating value={rowData.rating} readonly cancel={false} />;
    }

    const statusBodyTemplate = (rowData) => {
        return <span className={`product-badge status-${rowData.inventoryStatus.toLowerCase()}`}>{rowData.inventoryStatus}</span>;
    }

    const header = (
        <div className="table-header">
            Products
            <Button icon="pi pi-refresh" />
        </div>
    );
    const footer = `In total there are ${products ? products.length : 0} products.`;

    return (
        <DataTable value={products} header={header} footer={footer}>
            <Column field="name" header="Name"></Column>
            <Column header="Image" body={imageBodyTemplate}></Column>
            <Column field="price" header="Price" body={priceBodyTemplate}></Column>
            <Column field="category" header="Category"></Column>
            <Column field="rating" header="Reviews" body={ratingBodyTemplate}></Column>
            <Column header="Status" body={statusBodyTemplate}></Column>
        </DataTable>
    );

}
 
Size

In addition to the regular table, a smal and a large version are available with different paddings.


<DataTable value={products} header="Small Table" size="small">
    <Column field="code" header="Code"></Column>
    <Column field="name" header="Name"></Column>
    <Column field="category" header="Category"></Column>
    <Column field="quantity" header="Quantity"></Column>
</DataTable>

<DataTable value={products} header="Small Table" size="normal">
    <Column field="code" header="Code"></Column>
    <Column field="name" header="Name"></Column>
    <Column field="category" header="Category"></Column>
    <Column field="quantity" header="Quantity"></Column>
</DataTable>

<DataTable value={products} header="Small Table" size="large">
    <Column field="code" header="Code"></Column>
    <Column field="name" header="Name"></Column>
    <Column field="category" header="Category"></Column>
    <Column field="quantity" header="Quantity"></Column>
</DataTable>
 
Column Group

Columns can be grouped at header and footer sections by defining a ColumnGroup component as the headerColumnGroup and footerColumnGroup properties.


import React, { Component } from 'react';
import {DataTable} from 'primereact/datatable';
import {Column} from 'primereact/column';
import {ColumnGroup} from 'primereact/columngroup';
import {Row} from 'primereact/row';

export const DataTableColGroupDemo = () => {

    const sales = [
        {product: 'Bamboo Watch', lastYearSale: 51, thisYearSale: 40, lastYearProfit: 54406, thisYearProfit: 43342},
        {product: 'Black Watch', lastYearSale: 83, thisYearSale: 9, lastYearProfit: 423132, thisYearProfit: 312122},
        {product: 'Blue Band', lastYearSale: 38, thisYearSale: 5, lastYearProfit: 12321, thisYearProfit: 8500},
        {product: 'Blue T-Shirt', lastYearSale: 49, thisYearSale: 22, lastYearProfit: 745232, thisYearProfit: 65323},
        {product: 'Brown Purse', lastYearSale: 17, thisYearSale: 79, lastYearProfit: 643242, thisYearProfit: 500332},
        {product: 'Chakra Bracelet', lastYearSale: 52, thisYearSale:  65, lastYearProfit: 421132, thisYearProfit: 150005},
        {product: 'Galaxy Earrings', lastYearSale: 82, thisYearSale: 12, lastYearProfit: 131211, thisYearProfit: 100214},
        {product: 'Game Controller', lastYearSale: 44, thisYearSale: 45, lastYearProfit: 66442, thisYearProfit: 53322},
        {product: 'Gaming Set', lastYearSale: 90, thisYearSale: 56, lastYearProfit: 765442, thisYearProfit: 296232},
        {product: 'Gold Phone Case', lastYearSale: 75, thisYearSale: 54, lastYearProfit: 21212, thisYearProfit: 12533}
    ];

    const lastYearSaleBodyTemplate = (rowData) => {
        return `${rowData.lastYearSale}%`;
    }

    const thisYearSaleBodyTemplate = (rowData) => {
        return `${rowData.thisYearSale}%`;
    }

    const lastYearProfitBodyTemplate = (rowData) => {
        return `${formatCurrency(rowData.lastYearProfit)}`;
    }

    const thisYearProfitBodyTemplate = (rowData) => {
        return `${formatCurrency(rowData.thisYearProfit)}`;
    }

    const formatCurrency = (value) =>  {
        return value.toLocaleString('en-US', {style: 'currency', currency: 'USD'});
    }

    const lastYearTotal = () => {
        let total = 0;
        for(let sale of sales) {
            total += sale.lastYearProfit;
        }

        return formatCurrency(total);
    }

    const thisYearTotal = () => {
        let total = 0;
        for(let sale of sales) {
            total += sale.thisYearProfit;
        }

        return formatCurrency(total);
    }

    let headerGroup = <ColumnGroup>
                        <Row>
                            <Column header="Product" rowSpan={3} />
                            <Column header="Sale Rate" colSpan={4} />
                        </Row>
                        <Row>
                            <Column header="Sales" colSpan={2} />
                            <Column header="Profits" colSpan={2} />
                        </Row>
                        <Row>
                            <Column header="Last Year" sortable field="lastYearSale"/>
                            <Column header="This Year" sortable field="thisYearSale"/>
                            <Column header="Last Year" sortable field="lastYearProfit"/>
                            <Column header="This Year" sortable field="thisYearProfit"/>
                        </Row>
                    </ColumnGroup>;

    let footerGroup = <ColumnGroup>
                        <Row>
                            <Column footer="Totals:" colSpan={3} footerStyle={{textAlign: 'right'}}/>
                            <Column footer={lastYearTotal} />
                            <Column footer={thisYearTotal} />
                        </Row>
                        </ColumnGroup>;
    return (
        <DataTable value={sales} headerColumnGroup={headerGroup} footerColumnGroup={footerGroup}>
            <Column field="product" />
            <Column field="lastYearSale" body={lastYearSaleBodyTemplate} />
            <Column field="thisYearSale" body={thisYearSaleBodyTemplate} />
            <Column field="lastYearProfit" body={lastYearProfitBodyTemplate} />
            <Column field="thisYearProfit" body={thisYearProfitBodyTemplate} />
        </DataTable>
    );
}
 

When using sorting with column groups, define sort properties like sortable at columns inside column groups not at the direct children of DataTable component.

Pagination

Pagination is enabled by setting paginator property to true, rows property defines the number of rows per page and optionally pageLinks specify the the number of page links to display. See paginator component for more information about further customization options such as paginator template.

Pagination can either be used in Controlled or Uncontrolled manner. In controlled mode, first and onPage properties need to be defined to control the paginator state.


export const DataTablePaginatorDemo = () => {

    const [products, setProducts] = useState([]);
    const [first, setFirst] = useState(0);

    useEffect(() => {
        productService = new ProductService();
        productService.getProductsSmall().then(data => setProducts(data));
    }, [])

    return (
        <DataTable value={products} paginator rows={10} first={first} onPage={(e) => setFirst(e.first)}>
            <Column field="code" header="Code"></Column>
            <Column field="name" header="Name"></Column>
            <Column field="category" header="Category"></Column>
            <Column field="quantity" header="Quantity"></Column>
        </DataTable>
    );
}
 

In uncontrolled mode, only paginator and rows need to be enabled. Index of the first record can be still be provided using the first property in uncontrolled mode however it is evaluated at initial rendering and ignored in further updates. If you programmatically need to update the paginator state, prefer to use the component as controlled.


export const DataTablePaginatorDemo = () => {

    const [products, setProducts] = useState([]);

    useEffect(() => {
        productService = new ProductService();
        productService.getProductsSmall().then(data => setProducts(data));
    }, [])

    return (
        <DataTable value={products} paginator rows={10}>
            <Column field="code" header="Code"></Column>
            <Column field="name" header="Name"></Column>
            <Column field="category" header="Category"></Column>
            <Column field="quantity" header="Quantity"></Column>
        </DataTable>
    );
}
 

Elements of the paginator can be customized using the paginatorTemplate by the DataTable. Refer to the template section of the paginator documentation for further options.


<DataTable value={products} paginator rows={10} first={start}
    paginatorTemplate="RowsPerPageDropdown PageLinks FirstPageLink PrevPageLink CurrentPageReport NextPageLink LastPageLink">
    <Column field="code" header="Code"></Column>
    <Column field="name" header="Name"></Column>
    <Column field="category" header="Category"></Column>
    <Column field="quantity" header="Quantity"></Column>
</DataTable>
 
Sorting

Enabling sortable property at column component would be enough to make a column sortable. The property to use when sorting is field by default and can be customized using sortField.


<Column field="name" header="Name" sortable/>
 

By default sorting is executed on the clicked column only. To enable multiple field sorting, set sortMode property to "multiple" and use metakey when clicking on another column.


<DataTable value={products} sortMode="multiple">
 

In case you'd like to display the table as sorted per a single column by default on mount, use sortField and sortOrder properties in Controlled or Uncontrolled manner. In controlled mode, sortField, sortOrder and onSort properties need to be defined to control the sorting state.


const onSort = (e) => {
    setSortField(e.sortField);
    setSortOrder(e.sortOrder);
}
 

<DataTable value={products} sortField={sortField} sortOrder={sortOrder} onSort={onSort}>
    <Column field="code" header="Code" sortable></Column>
    <Column field="name" header="Name" sortable></Column>
    <Column field="category" header="Category" sortable></Column>
    <Column field="quantity" header="Quantity" sortable></Column>
</DataTable>
 

In multiple mode, use the multiSortMeta property and bind an array of SortMeta objects instead.


<DataTable value={products} multiSortMeta={multiSortMeta} onSort={(e) => setMultiSortMeta(e.multiSortMeta)}>
    <Column field="code" header="Code" sortable></Column>
    <Column field="name" header="Name" sortable></Column>
    <Column field="category" header="Category" sortable></Column>
    <Column field="quantity" header="Quantity" sortable></Column>
</DataTable>
 

let multiSortMeta = [];
multiSortMeta.push({field: 'code', order: 1});
multiSortMeta.push({field: 'name', order: -1});
 

In uncontrolled mode, no additional properties need to be enabled. Initial sort field can be still be provided using the sortField property in uncontrolled mode however it is evaluated at initial rendering and ignored in further updates. If you programmatically need to update the sorting state, prefer to use the component as controlled.


<DataTable value={products} sortField="name" sortOrder={1}>
    <Column field="code" header="Code" sortable></Column>
    <Column field="name" header="Name" sortable></Column>
    <Column field="category" header="Category" sortable></Column>
    <Column field="quantity" header="Quantity" sortable></Column>
</DataTable>
 

To customize sorting algorithm, define a sortFunction that sorts the list.


<DataTable value={products} >
    <Column field="code" header="Code" sortable></Column>
    <Column field="name" header="Name" sortable sortFunction={mysort}></Column>
    <Column field="category" header="Category" sortable></Column>
    <Column field="quantity" header="Quantity" sortable></Column>
</DataTable>
 

const mysort = (event) => {
    //event.data = Data
    //event.field = Field to sort
    //event.order = Sort order
    //event.multiSortMeta = An array of SortMeta objects to sort the data by default in multiple sort mode.
}
 

Getting access to the sorted data is provided by the onValueChange callback.


<DataTable value={products} onValueChange={sortedData => console.log(sortedData)}>
    <Column field="code" header="Code" sortable></Column>
    <Column field="name" header="Name" sortable></Column>
    <Column field="category" header="Category" sortable></Column>
    <Column field="quantity" header="Quantity" sortable></Column>
</DataTable>
 
Filtering

DataTable has advanced filtering capabilities that does the heavy lifting while providing flexible customization. Filtering has two layout alternatives defined with the filterDisplay. In row setting, filter elements are displayed in a separate row at the header section whereas in menu mode filter elements are displayed inside an overlay. The template filter gets a value, filterCallback and filterApplyCallback, use value to populate the filter with your own form components and call the filterCallback with the event of your choice like onInput, onChange, onClick. FilterCallback adds new values in hidden 'filters' state in DataTable and when filterApplyCallback is called, data is filtered. The filterApplyCallback method can be used directly if you want to filter by value directly.


const DataTableFilterDemo = () => {
    const [customers, setCustomers] = useState(null);
    const filters = {
        'name': { value: null, matchMode: FilterMatchMode.STARTS_WITH }
    }

    return (
        <DataTable value={customers} filters={filters}>
            <Column field="name" header="Name" filter></Column>
        </DataTable>
    )
}
 
Filter Row

Input field is displayed in a separate header row.


<DataTable value={customers} filters={filters} filterDisplay="row">
    <Column field="name" header="Name" filter></Column>
</DataTable>
 
Filter Menu

Input field is displayed in an overlay.


<DataTable value={customers} filters={filters} filterDisplay="menu">
    <Column field="name" header="Name" filter></Column>
</DataTable>
 
Multiple Constraints

In "menu" display, it is possible to add more constraints to a same filter. In this case, metadata could be an array of constraints. The operator defines whether all or any of the constraints should match.


const filters = {
    'name': {operator: FilterOperator.AND, constraints: [{value: null, matchMode: FilterMatchMode.STARTS_WITH}]},
}
 
Populate Filters

Providing a filters with predefined values would be enough to display the table as filtered by default. This approach can also be used to clear filters progammatically.


const filters = {
    'name': {operator: FilterOperator.AND, constraints: [
        {value: 'Prime', matchMode: FilterMatchMode.STARTS_WITH},
        {value: 'React', matchMode: FilterMatchMode.CONTAINS}
    ]}
}
 
Match Modes

Depending on the dataType of the column, suitable match modes are displayed. Default configuration is available at PrimeReact.filterMatchModeOptions which can be used to customize the modes globally for all tables.


PrimeReact.filterMatchModeOptions = {
    text: [
        FilterMatchMode.STARTS_WITH,
        FilterMatchMode.CONTAINS,
        FilterMatchMode.NOT_CONTAINS,
        FilterMatchMode.ENDS_WITH,
        FilterMatchMode.EQUALS,
        FilterMatchMode.NOT_EQUALS
    ],
    numeric: [
        FilterMatchMode.EQUALS,
        FilterMatchMode.NOT_EQUALS,
        FilterMatchMode.LESS_THAN,
        FilterMatchMode.LESS_THAN_OR_EQUAL_TO,
        FilterMatchMode.GREATER_THAN,
        FilterMatchMode.GREATER_THAN_OR_EQUAL_TO
    ],
    date: [
        FilterMatchMode.DATE_IS,
        FilterMatchMode.DATE_IS_NOT,
        FilterMatchMode.DATE_BEFORE,
        FilterMatchMode.DATE_AFTER
    ]
}
 

If you need to override the match modes for a particular column use the filterMatchModeOptions property and provide an array with label-value pairs.


const matchModes = [
    {label: 'Starts With', value: FilterMatchMode.STARTS_WITH},
    {label: 'Contains', value: FilterMatchMode.CONTAINS},
];
...
<Column field="name" header="Name" filterMatchModeOptions={matchModes} />
 
Custom Filter

Custom filtering is implemented using the FilterService, first register your filter and add it to your filterMatchModeOptions.


import {FilterService} from 'primereact/api';

FilterService.register('myfilter', (a,b) => a === b);
...

const matchModes = [
    {label: 'My Filter', value: "myfilter"},
    {label: 'Starts With', value: FilterMatchMode.STARTS_WITH},
    {label: 'Contains', value: FilterMatchMode.CONTAINS},
]
 

By default, input fields are used as filter elements and this can be customized using the filterElement property of the Column that calls the filter function of the table instance by passing the value, field and the match mode.


export const DataTableCustomFilterDemo = () => {

    const [products, setProducts] = useState([]);
    const filters = {
        'inventoryStatus': { value: null, matchMode: FilterMatchMode.EQUALS }
    };

    useEffect(() => {
        productService = new ProductService();
        productService.getProductsSmall().then(data => setProducts(data));
    } ,[])

    const onStatusChange = (e, options) => {
        options.filterCallback(event.value);
    }

    let inventoryStatuses = [
            {label: 'All Status', value: null},
            {label: 'INSTOCK', value: 'INSTOCK'},
            {label: 'LOWSTOCK', value: 'LOWSTOCK'},
            {label: 'OUTOFSTOCK', value: 'OUTOFSTOCK'}
        ];

    let statusFilter = (options) => <Dropdown style={{width: '100%'}} className="ui-column-filter"
        value={options.value} options={inventoryStatuses} onChange={(e) => onStatusChange(e, options)}/>

    return (
        <DataTable value={products} filters={filters}>
            <Column field="code" header="Code" filter></Column>
            <Column field="name" header="Name" filter></Column>
            <Column field="category" header="Category" filter></Column>
            <Column field="inventoryStatus" header="Status" filter filterElement="statusFilter"></Column>
        </DataTable>
    );
}
 

In case you'd like to display the table as filtered by default on mount, use filters property in Controlled or Uncontrolled manner. In controlled mode, filters and onFilter properties need to be defined to control the filtering state.


export const DataTableDefaultFilteredDemo = () => {

    const [products, setProducts] = useState([]);
    const [filters, setFilters] = useState({
        'inventoryStatus': {
            value: 'INSTOCK'
        });

    useEffect(() => {
        productService = new ProductService();
        productService.getProductsSmall().then(data => setProducts(data));
    } ,[])

    return (
        <DataTable value={products} filters={filters} onFilter={(e) => setFilters(e.filters)}>
            <Column field="code" header="Code" filter></Column>
            <Column field="name" header="Name" filter></Column>
            <Column field="category" header="Category" filter></Column>
            <Column field="inventoryStatus" header="Status" filter></Column>
        </DataTable>
    );
}
 

In uncontrolled filtering, no additional properties need to be enabled. Initial filtering can be still be provided using the filters property in uncontrolled mode however it is evaluated at initial rendering and ignored in further updates. If you programmatically need to update the filtering state, prefer to use the component as controlled.


<DataTable value={products}>
    <Column field="code" header="Code" filter></Column>
    <Column field="name" header="Name" filter></Column>
    <Column field="category" header="Category" filter></Column>
    <Column field="inventoryStatus" header="Status" filter></Column>
</DataTable>
 

Getting access to the filtered data is provided by the onValueChange callback.


<DataTable value={products} onValueChange={filteredData => console.log(filteredData)}>
    <Column field="code" header="Code" filter></Column>
    <Column field="name" header="Name" filter></Column>
    <Column field="category" header="Category" filter></Column>
    <Column field="inventoryStatus" header="Status" filter></Column>
</DataTable>
 
Selection

DataTable provides single and multiple selection modes on click of a row or cell. Selected rows are bound to the selection property for reading and updated using onSelectionChange callback. Alternatively column based selection can be done using radio buttons or checkboxes using selectionMode of a particular column. In addition onRowSelect-onRowUnselect / onCellSelect-onCellUnselect events are provided as optional callbacks.

In single mode, selection binding is an object reference.


export const DataTableSelectionDemo = () => {

    const [products, setProducts] = useState([]);
    const [selectedProduct1, setSelectedProduct1] = useState(null);
    const [selectedProduct2, setSelectedProduct2] = useState(null);

    useEffect(() => {
        productService = new ProductService();
        productService.getProductsSmall().then(data => setProducts(data));
    }, [])

    return (
        <>
            <DataTable value={products} selectionMode="single"
                selection={selectedProduct1} onSelectionChange={e => setSelectedProduct1(e.value)}>
                <Column field="code" header="Code"></Column>
                <Column field="name" header="Name"></Column>
                <Column field="category" header="Category"></Column>
                <Column field="inventoryStatus" header="Status"></Column>
            </DataTable>

            <DataTable value={products} selectionMode="single" cellSelection
                selection={selectedProduct2} onSelectionChange={e => setSelectedProduct2(e.value)}>
                <Column field="code" header="Code"></Column>
                <Column field="name" header="Name"></Column>
                <Column field="category" header="Category"></Column>
                <Column field="inventoryStatus" header="Status"></Column>
            </DataTable>
        </>
    );
}
 

In multiple mode, selection binding should be an array and multiple items can either be selected using metaKey or toggled individually depending on the value of metaKeySelection property value which is true by default. On touch enabled devices metaKeySelection is turned off automatically. Also, ShiftKey is supported for range selection. In addition, the rectangular selection can be dragged over the desired rows or cells thanks to the dragSelection property. In this way, a range of rows or cells can be selected.


export const DataTableSelectionDemo = () => {

    const [products, setProducts] = useState([]);
    const [selectedProducts1, setSelectedProducts1] = useState(null);
    const [selectedProducts2, setSelectedProducts2] = useState(null);
    const [selectedProducts3, setSelectedProducts3] = useState(null);
    const [selectedProducts4, setSelectedProducts4] = useState(null);

    useEffect(() => {
        productService = new ProductService();
        productService.getProductsSmall().then(data => setProducts(data));
    }, []);

    return (
        <>
            <DataTable value={products} selectionMode="multiple"
                selection={selectedProducts1} onSelectionChange={e => setSelectedProducts1(e.value)}>
                <Column field="code" header="Code"></Column>
                <Column field="name" header="Name"></Column>
                <Column field="category" header="Category"></Column>
                <Column field="inventoryStatus" header="Status"></Column>
            </DataTable>

            <DataTable value={products} selectionMode="multiple"
                selection={selectedProducts2} onSelectionChange={e => setSelectedProducts2(e.value)}>
                <Column field="code" header="Code"></Column>
                <Column field="name" header="Name"></Column>
                <Column field="category" header="Category"></Column>
                <Column field="inventoryStatus" header="Status"></Column>
            </DataTable>

            <DataTable value={products} selectionMode="multiple" cellSelection
                selection={selectedProducts3} onSelectionChange={e => setSelectedProducts3(e.value)}>
                <Column field="code" header="Code"></Column>
                <Column field="name" header="Name"></Column>
                <Column field="category" header="Category"></Column>
                <Column field="inventoryStatus" header="Status"></Column>
            </DataTable>

            <DataTable value={products} selectionMode="multiple" cellSelection dragSelection
                selection={selectedProducts4} onSelectionChange={e => setSelectedProducts4(e.value)}>
                <Column field="code" header="Code"></Column>
                <Column field="name" header="Name"></Column>
                <Column field="category" header="Category"></Column>
                <Column field="inventoryStatus" header="Status"></Column>
            </DataTable>
        </>
    );

}
 

If you prefer a radiobutton or a checkbox instead of a row click, use the selectionMode of a column instead. Following datatable displays a checkbox at the first column of each row and automatically adds a header checkbox to toggle selection of all rows.

Tip: Use showSelectionElement function in case you need to hide selection element for a particular row.


<h5>Row and Checkbox Selection</h5>
<DataTable value={products} selection={selectedProducts} onSelectionChange={e => setSelectedProducts(e.value))}>
    <Column selectionMode="multiple" />
    <Column field="code" header="Code"></Column>
    <Column field="name" header="Name"></Column>
    <Column field="category" header="Category"></Column>
    <Column field="inventoryStatus" header="Status"></Column>
</DataTable>

<h5>Checkbox-Only Selection</h5>
<DataTable value={products} selection={selectedProducts} selectionMode="checkbox" onSelectionChange={e => setSelectedProducts(e.value))}>
    <Column selectionMode="multiple" />
    <Column field="code" header="Code"></Column>
    <Column field="name" header="Name"></Column>
    <Column field="category" header="Category"></Column>
    <Column field="inventoryStatus" header="Status"></Column>
</DataTable>
 
Cell Editing

Incell editing feature provides a way to quickly edit data inside the table. A cell editor is defined using the editor property that refers to a function to return an input element for the editing.


<DataTable value={products}>
    <Column field="code" header="Code" editor={codeEditor}></Column>
    <Column field="name" header="Name" editor={nameEditor}></Column>
    <Column field="inventoryStatus" header="Status" body={statusBodyTemplate} editor={statusEditor}></Column>
    <Column field="price" header="Price" editor={priceEditor}></Column>
</DataTable>
 

const onEditorValueChange = (props, value) => {
    let updatedProducts = [...props.value];
    updatedProducts[props.rowIndex][props.field] = value;
    setProducts(updatedProducts);
}

const inputTextEditor = (props, field) => {
    return <InputText type="text" value={props.rowData[field]} onChange={(e) => onEditorValueChange(props, e.target.value)} />;
}

const codeEditor = (props) => {
    return inputTextEditor(props, 'code');
}

const nameEditor = (props) => {
    return inputTextEditor(props, 'name');
}

const priceEditor = (props) => {
    return inputTextEditor(props, 'price');
}

const statusEditor = (props) => {
    return (
        <Dropdown value={props.rowData['inventoryStatus']} options={statuses} optionLabel="label" optionValue="value"
            onChange={(e) => onEditorValueChange(props, e.value)} style={{ width: '100%' }} placeholder="Select a Status"
            itemTemplate={(option) => {
                return <span className={`product-badge status-${option.value.toLowerCase()}`}>{option.label}</span>
            }} />
    );
}
 

Clicking outside the cell or hitting enter key closes the cell, however this may not be desirable if the input is invalid. In order to decide whether to keep the cell open or not, provide a cellEditValidator function that validates the value. Optionally onCellEditComplete and onCellEditCancelevents are available at the column component to provide callbacks whenever an editor is submitted or cancelled.


<DataTable value={products}>
    <Column field="code" header="Code" editor={codeEditor} cellEditValidator={requiredValidator} />
    <Column field="name" header="Name" editor={nameEditor} />
    <Column field="price" header="Price" editor={priceDateEditor} />
</DataTable>
 

const requiredValidator = (e) => {
    let props = e.columnProps;
    let value = props.rowData[props.field];
    return value && value.length > 0;
}
 
Row Editing

Row editing toggles the visibility of the all editors in the row at once and provides additional options to save and cancel editing.


<DataTable value={products} editMode="row" rowEditValidator={onRowEditValidator}>
    <Column field="code" header="Code" />
    <Column field="name" header="Name" />
    <Column field="inventoryStatuses" header="Status" editor={statusEditor} />
    <Column rowEditor />
</DataTable>
 

const onRowEditValidator = (rowData) => {
    let value = rowData['inventoryStatuses'];
    return value.length > 0;
}
 
ContextMenu

DataTable provides exclusive integration with ContextMenu. contextMenuSelection and onContextMenuSelectionChange are used to get a reference of the the selected row and onContextMenu callback is utilized to display a particular context menu.


export const DataTableContextMenuDemo = () => {

    const [products, setProducts] = useState([]);
    const [selectedProduct, setSelectedProduct] = useState(null);
    const toast = useRef(null);
    const cm = useRef(null);

    const menuModel = [
        {label: 'View', icon: 'pi pi-fw pi-search', command: () => viewProduct(selectedProduct)},
        {label: 'Delete', icon: 'pi pi-fw pi-times', command: () => deleteProduct(selectedProduct)}
    ];

    useEffect(() => {
        productService = new ProductService();
        productService.getProductsSmall().then(data => setProducts(data));
    }, [])

    const viewProduct = (product) => {
        toast.current.show({severity: 'info', summary: 'Product Selected', detail: product.name});
    }

    const deleteProduct = (product) => {
        let products = [...products];
        products = products.filter((p) => p.id !== product.id);
        toast.current.show({severity: 'info', summary: 'Product Deleted', detail: product.name});
        setProducts(products);
    }

    const formatCurrency = (value) => {
        return value.toLocaleString('en-US', {style: 'currency', currency: 'USD'});
    }

    const priceBodyTemplate = (rowData) => {
        return formatCurrency(rowData.price);
    }

    return (
        <div>
            <Toast ref={toast}></Toast>

            <ContextMenu model={menuModel} ref={cm} onHide={() => setSelectedProduct(null)}/>

            <DataTable value={products} contextMenuSelection={selectedProduct}
                onContextMenuSelectionChange={e => setSelectedProduct(e.value)}
                onContextMenu={e => cm.current.show(e.originalEvent)}>
                <Column field="code" header="Code"></Column>
                <Column field="name" header="Name"></Column>
                <Column field="category" header="Category"></Column>
                <Column field="price" header="Price" body={priceBodyTemplate} />
            </DataTable>
        </div>
    );
}
 
Expandable Rows

Row expansion allows displaying detailed content for a particular row. To use this feature, add an expander column, define a rowExpansionTemplate as a function to return the expanded content and bind toexpandedRows property to read the expanded rows along with the onRowToggle property to update it. expandedRows property either accepts an array of row data or a map whose key is the dataKey of the record. Using expandable rows with a dataKey is suggested for better performance.


export const DataTableRowExpansionDemo = () => {

    const [products, setProducts] = useState([]);
    const [expandedRows, setExpandedRows] = useState(null);

    useEffect(() => {
        productService = new ProductService();
        productService.getProductsWithOrdersSmall().then(data => setProducts(data));
    }, [])

    const onRowExpand = (event) => {
        toast.current.show({severity: 'info', summary: 'Product Expanded', detail: event.data.name, life: 3000});
    }

    const onRowCollapse = (event) => {
        toast.current.show({severity: 'success', summary: 'Product Collapsed', detail: event.data.name, life: 3000});
    }

    const expandAll = () => {
        let _expandedRows = {};
        products.forEach(p => expandedRows[`${p.id}`] = true);

        setExpandedRows(_expandedRows);
    }

    const collapseAll = () => {
        setExpandedRows(null);
    }

    const formatCurrency = (value) => {
        return value.toLocaleString('en-US', {style: 'currency', currency: 'USD'});
    }

    const amountBodyTemplate = (rowData) => {
        return formatCurrency(rowData.amount);
    }

    const statusOrderBodyTemplate = (rowData) => {
        return <span className={`order-badge order-${rowData.status.toLowerCase()}`}>{rowData.status}</span>;
    }

    const searchBodyTemplate = () => {
        return <Button icon="pi pi-search" />;
    }

    const imageBodyTemplate = (rowData) => {
        return <img src={`images/product/${rowData.image}`} alt={rowData.image} className="product-image" />;
    }

    const priceBodyTemplate = (rowData) => {
        return formatCurrency(rowData.price);
    }

    const ratingBodyTemplate = (rowData) => {
        return <Rating value={rowData.rating} readonly cancel={false} />;
    }

    const statusBodyTemplate = (rowData) => {
        return <span className={`product-badge status-${rowData.inventoryStatus.toLowerCase()}`}>{rowData.inventoryStatus}</span>;
    }

    const rowExpansionTemplate = (data) => {
        return (
            <div className="orders-subtable">
                <h5>Orders for {data.name}</h5>
                <DataTable value={data.orders}>
                    <Column field="id" header="Id" sortable></Column>
                    <Column field="customer" header="Customer" sortable></Column>
                    <Column field="date" header="Date" sortable></Column>
                    <Column field="amount" header="Amount" body={amountBodyTemplate} sortable></Column>
                    <Column field="status" header="Status" body={statusOrderBodyTemplate} sortable></Column>
                    <Column headerStyle={{ width: '4rem'}} body={searchBodyTemplate}></Column>
                </DataTable>
            </div>
        );
    }

    const header = (
        <div className="table-header-container">
            <Button icon="pi pi-plus" label="Expand All" onClick={expandAll} className="mr-2" />
            <Button icon="pi pi-minus" label="Collapse All" onClick={collapseAll} />
        </div>
    );

    return (
        <DataTable value={products} expandedRows={expandedRows} onRowToggle={(e) => setExpandedRows(e.data)}
            onRowExpand={onRowExpand} onRowCollapse={onRowCollapse}
            rowExpansionTemplate={rowExpansionTemplate} dataKey="id" header={header}>
            <Column expander style={{ width: '3em' }} />
            <Column field="name" header="Name" sortable />
            <Column header="Image" body={imageBodyTemplate} />
            <Column field="price" header="Price" sortable body={priceBodyTemplate} />
            <Column field="category" header="Category" sortable />
            <Column field="rating" header="Reviews" sortable body={ratingBodyTemplate} />
            <Column field="inventoryStatus" header="Status" sortable body={statusBodyTemplate} />
        </DataTable>
    );
}
 
Column Resize

Columns can be resized using drag drop by setting the resizableColumns to true. There are two resize modes; "fit" and "expand". Fit is the default one and the overall table width does not change when a column is resized. In "expand" mode, table width also changes along with the column width. onColumnResizeEnd is a callback that passes the resized column header as a parameter.


<DataTable value={products} resizableColumns>
    <Column field="code" header="Code"></Column>
    <Column field="name" header="Name"></Column>
    <Column field="category" header="Category"></Column>
    <Column field="quantity" header="Quantity"></Column>
</DataTable>
 

It is important to note that when you need to change column widths, since table width is 100%, giving fixed pixel widths does not work well as browsers scale them, instead give percentage widths.


<DataTable value={products} resizableColumns>
    <Column field="code" header="Code" style={{width:'20%'}}></Column>
    <Column field="name" header="Name" style={{width:'40%'}}></Column>
    <Column field="category" header="Category" style={{width:'20%'}}></Column>
    <Column field="quantity" header="Quantity" style={{width:'30%'}}></Column>
</DataTable>
 

You can choose which columns are resizeable per column.


<DataTable value={products} resizableColumns>
    <Column field="code" header="Code" style={{width:'20%'}}></Column>
    <Column field="name" header="Name" style={{width:'40%'}}></Column>
    <Column field="category" header="Category (not resizable)" style={{width:'20%'}} resizeable={false} />
    <Column field="quantity" header="Quantity" style={{width:'30%'}}></Column>
</DataTable>
 
Column Reorder

Columns can be reordered using drag drop by setting the reorderableColumns to true. onColReorder is a callback that is invoked when a column is reordered. DataTable keeps the column order state internally using keys that identifies a column using the field property. If the column has no field, use columnKey instead.


<DataTable value={products} reorderableColumns>
    <Column field="code" header="Code"></Column>
    <Column field="name" header="Name"></Column>
    <Column field="category" header="Category"></Column>
    <Column field="quantity" header="Quantity"></Column>
</DataTable>
 
Row Reorder

Data can be reordered using drag drop by adding a reorder column that will display an icon as a drag handle. onRowReorder is a callback that is invoked when a column is reordered, use this callback to update the new order. The reorder icon can be customized using rowReorderIcon of the column component.

Tip: Use showRowReorderElement function in case you need to hide selection element for a particular row.


<DataTable value={products} reorderableColumns onRowReorder={(e) => setProducts(e.value)}>
    <Column rowReorder style={{width: '2em'}} />
    <Column columnKey="code" field="code" header="Code"></Column>
    <Column columnKey="name" field="name" header="Name"></Column>
    <Column columnKey="category" field="category" header="Category"></Column>
    <Column columnKey="quantity" field="quantity" header="Quantity"></Column>
</DataTable>
 
Data Export

DataTable can export its data in CSV format using exportCSV() method.


export const DataTableExportDemo = () => {

    const [products, setProducts] = useState([]);
    const dt = useRef(null);

    useEffect(() => {
        productService = new ProductService();
        productService.getProductsSmall().then(data => setProducts(data));
    }, [])

    const export = () => {
        dt.exportCSV();
    }

    const header = <div style={{textAlign:'left'}}><Button type="button" icon="pi pi-external-link" iconPos="left" label="CSV" onClick={export}></Button></div>;

    return (
        <DataTable value={products} header={header} ref={dt}>
            <Column field="code" header="Code"></Column>
            <Column field="name" header="Name"></Column>
            <Column field="category" header="Category"></Column>
            <Column field="quantity" header="Quantity"></Column>
        </DataTable>
    );
}
 
RowGrouping

RowGrouping has two modes defined be the rowGroupMode property, in "subheader" option rows are grouped by a groupRowsBy and in "rowspan" mode grouping is done based on the sort field. In both cases, data should be sorted initally using the properties such as sortField and sortOrder. In "subheader" mode,rowGroupHeaderTemplate property should be defined to provide the content of the header and optionally rowGroupFooterTemplate is available to provide a footer for the group.


const DataTableRowGroupDemo = () => {

    const [products, setProducts] = useState(null);

    useEffect(() => {
        productService = new ProductService();
        productService.getProductsSmall().then(data => setProducts(data));
    }, []);

    const headerTemplate = (data) => {
        return data.name;
    }

    const footerTemplate = (data, index) => {
        return ([
                    <td key={data.brand + '_footerTotalLabel'} colSpan="3" style={{textAlign: 'right'}}>Total Price</td>,
                    <td key={data.brand + '_footerTotalValue'}>{calculateGroupTotal(data.name)}</td>
                ]
        );
    }

    const calculateGroupTotal = (name) => {
        let total = 0;

        if (products) {
            for (let product of products) {
                if (product.name === name) {
                    total += product.price;
                }
            }
        }

        return total;
    }

    return (
        <div>
            <DataTable header="SubHeader" value={products} rowGroupMode="subheader" sortField="brand" sortOrder={1} groupRowsBy="name"
                rowGroupHeaderTemplate={headerTemplate} rowGroupFooterTemplate={footerTemplate}>
                <Column field="code" header="Code"></Column>
                <Column field="name" header="Name"></Column>
                <Column field="category" header="Category"></Column>
                <Column field="price" header="Price"></Column>
            </DataTable>

            <DataTable header="RowSpan" value={products} rowGroupMode="rowspan" sortField="brand" sortOrder={1} groupRowsBy="name"
                style={{marginTop:'30px'}}>
                <Column field="code" header="Code"></Column>
                <Column field="name" header="Name"></Column>
                <Column field="category" header="Category"></Column>
                <Column field="price" header="Price"></Column>
            </DataTable>
        </div>
    );

}
 
Scrolling

DataTable supports both horizontal and vertical scrolling as well as frozen columns and rows. Scrollable DataTable is enabled using scrollable property and scrollHeight to define the viewport height.


<DataTable value={products} scrollable scrollHeight="400px">
    <Column field="code" header="Code"></Column>
    <Column field="name" header="Name"></Column>
    <Column field="category" header="Category"></Column>
    <Column field="quantity" header="Quantity"></Column>
</DataTable>
 
Column Widths of a Scrollable Table

Scrollable table uses flex layout so there are a couple of rules to consider when adjusting the widths of columns.

  • Use min-width in vertical scrolling only so that when there is enough space columns may grow and for smaller screens a horizontal scrollbar is displayed to provide responsive design.
  • When horizontal scrolling is enabled, prefer width instead of min-width.
  • In vertical scrolling only, use flex to disable grow and shrink while defining a initial width. When horizontal scrolling is enabled, this is not required as columns do not grow or shrink in horizontal scrolling.

<Column field="code" header="Code" style={{ flex: '0 0 4rem' }}></Column>
 
Flex Scroll

In cases where viewport should adjust itself according to the table parent's height instead of a fixed viewport height, set scrollHeight option as flex. In example below, table is inside a Dialog where viewport size dynamically responds to the dialog size changes such as maximizing.


<Button label="Show" icon="pi pi-external-link" onClick={() => setDialogVisible(true)} />
<Dialog header="Flex Scroll" visible={dialogVisible} onHide={() => setDialogVisible(false)} style={{ width: '50vw' }} maximizable modal contentStyle={{ height: '300px' }}>
    <DataTable value={products} scrollable scrollHeight="flex">
        <Column field="code" header="Code"></Column>
        <Column field="name" header="Name"></Column>
        <Column field="category" header="Category"></Column>
        <Column field="quantity" header="Quantity"></Column>
    </DataTable>
</Dialog>
 
Full Page Scroll

FlexScroll can also be used for cases where scrollable viewport should be responsive with respect to the window size. See the full page demo for an example.


<div style={{ height: 'calc(100vh - 143px)' }}>
    <DataTable value={products} scrollable scrollHeight="flex">
        <Column field="code" header="Code"></Column>
        <Column field="name" header="Name"></Column>
        <Column field="category" header="Category"></Column>
        <Column field="quantity" header="Quantity"></Column>
    </DataTable>
</div>
 
Horizontal Scrolling

For horizontal scrolling, it is required to set scrollDirection to "horizontal" and give fixed widths to columns.


<DataTable value={products} scrollable scrollDirection="horizontal">
    <Column field="code" header="Code" style={{ width: '200px' }}></Column>
    <Column field="name" header="Name" style={{ width: '200px' }}></Column>
    <Column field="category" header="Category" style={{ width: '200px' }}></Column>
    <Column field="quantity" header="Quantity" style={{ width: '200px' }}></Column>
    <Column field="code" header="Code" style={{ width: '200px' }}></Column>
    <Column field="name" header="Name" style={{ width: '200px' }}></Column>
    <Column field="category" header="Category" style={{ width: '200px' }}></Column>
    <Column field="quantity" header="Quantity" style={{ width: '200px' }}></Column>
</DataTable>
 
Horizontal and Vertical Scrolling

Set scrollDirection to "both" and give fixed widths to columns to scroll both ways.


<DataTable value={products} scrollable scrollDirection="both" scrollHeight="400px">
    <Column field="code" header="Code" style={{ width: '200px' }}></Column>
    <Column field="name" header="Name" style={{ width: '200px' }}></Column>
    <Column field="category" header="Category" style={{ width: '200px' }}></Column>
    <Column field="quantity" header="Quantity" style={{ width: '200px' }}></Column>
    <Column field="code" header="Code" style={{ width: '200px' }}></Column>
    <Column field="name" header="Name" style={{ width: '200px' }}></Column>
    <Column field="category" header="Category" style={{ width: '200px' }}></Column>
    <Column field="quantity" header="Quantity" style={{ width: '200px' }}></Column>
</DataTable>
 
Frozen Rows

Frozen rows are used to fix certain rows while scrolling, this data is defined with the frozenValue property.


<DataTable value={customers} frozenValue={lockedCustomers} scrollable scrollHeight="400px">
    <Column field="name" header="Name"></Column>
    <Column field="country.name" header="Country"></Column>
    <Column field="representative.name" header="Representative"></Column>
    <Column field="status" header="Status"></Column>
</DataTable>
 
Frozen Columns

Certain columns can be frozen by using the frozen property of the column component. In addition alignFrozen is available to define whether the column should be fixed on the left or right.


<DataTable value={customers} scrollable scrollHeight="400px" scrollDirection="both">
    <Column field="name" header="Name" style={{ width:'200px' }} frozen></Column>
    <Column field="id" header="Id" style={{ width:'100px' }}  frozen></Column>
    <Column field="name" header="Name" style={{ width:'200px' }} </Column>
    <Column field="country.name" header="Country" style={{ width:'200px' }} ></Column>
    <Column field="date" header="Date" style={{ width:'200px' }} ></Column>
    <Column field="company" header="Company" style={{ width:'200px' }} ></Column>
    <Column field="status" header="Status" style={{ width:'200px' }} ></Column>
    <Column field="activity" header="Activity" style={{ width:'200px' }} ></Column>
    <Column field="representative.name" header="Representative" style={{ width:'200px' }} ></Column>
    <Column field="balance" header="Balance" style={{ width:'200px' }} frozen alignFrozen="right"></Column>
</DataTable>
 
Scrollable RowGroup

Row groups with subheaders have exclusive support for filtering, when the table scrolls the subheaders stay fixed as long as their data are still displayed. No additional configuration is required to enable this feature. View the Row Group demo for an example.

Lazy Loading

Lazy mode is handy to deal with large datasets, instead of loading the entire data, small chunks of data is loaded by invoking corresponding callbacks everytime paging, sorting and filtering happens. Sample belows imitates lazy paging by using an in memory list. It is also important to assign the logical number of rows to totalRecords by doing a projection query for paginator configuration so that paginator displays the UI assuming there are actually records of totalRecords size although in reality they aren't as in lazy mode, only the records that are displayed on the current page exist.

In lazy mode, pagination, sorting and filtering must be used in controlled mode in addition to enabling lazy property. Here is a sample paging implementation with in memory data.


export const DataTableLazyDemo = () => {

    const [products, setProducts] = useState([]);
    const [loading, setLoading] = useState(true);
    const [first, setFirst] = useState(0);
    const [rows, setRows] = useState(10);
    const [totalRecords, setTotalRecords] = useState(0);
    const [dataSource, setDataSource] = useState([]);

    useEffect((() => {
        productService = new ProductService();
        productService.getProducts().then(data => {
            setDataSource(data);
            setTotalRecords(data.length);
            setProducts(datasource.slice(0, rows));
            setLoading(false);
        });
    }, []);

    const onPage = (event) => {
        setLoading(true);

        //imitate delay of a backend call
        setTimeout(() => {
            const startIndex = event.first;
            const endIndex = event.first + rows;

            setFirst(startIndex);
            setProducts(datasource.slice(startIndex, endIndex));
            setLoading(false);
        }, 250);
    }

    return (
        <DataTable value={products} paginator rows={rows} totalRecords={totalRecords}
            lazy first={first} onPage={onPage} loading={loading}>
            <Column field="code" header="Code"></Column>
            <Column field="name" header="Name"></Column>
            <Column field="category" header="Category"></Column>
            <Column field="quantity" header="Quantity"></Column>
        </DataTable>
    )
}
 
TableState

Stateful table allows keeping the state such as page, sort and filtering either at local storage or session storage so that when the page is visited again, table would render the data using its last settings. Enabling state is easy as defining a unique stateKey, the storage to keep the state is defined with the stateStorage property that accepts session for sessionStorage and local for localStorage. Also, a special storage implementation can be made with customSaveState and customRestoreState methods using stateStorage="custom". Currently following features are supported by TableState; paging, sorting, filtering, column resizing, column reordering, row expansion and row selection.


export const DataTableStateDemo = () => {

    const [products, setProducts] = useState([]);
    const [selectedProducts, setSelectedProducts] = useState(null);

    useEffect(() => {
        productService = new ProductService();
        productService.getProductsSmall().then(data => setProducts(data));
    }, [])

    return (
        <DataTable value={products} selectionMode="multiple" resizableColumns
                    selection={selectedProducts} onSelectionChange={e => setSelectedProducts(e.value)}
                    paginator rows={10} stateKey="tablestatedemo-session">
            <Column field="code" header="Code" sortable filter></Column>
            <Column field="name" header="Name" sortable filter></Column>
            <Column field="category" header="Category" sortable filter></Column>
            <Column field="quantity" header="Quantity" sortable filter></Column>
        </DataTable>
    );
}
 
Responsive

DataTable responsive layout can be achieved in two ways; first approach is displaying a horizontal scrollbar for smaller screens and second one is defining a breakpoint to display the cells of a row as stacked. Scrollable tables use the scroll layout approach internally and do not require additional configuration.

Scroll Layout

Set responsiveLayout to scroll to enabled this layout. Note that, when scroll mode is enabled table-layout automatically switches to auto from fixed as a result table widths are likely to differ and resizable columns are not supported. Read more about table-layout for more details.


<DataTable value={products} responsiveLayout="scroll">

</DataTable>
 
Stack Layout

In stack layout, columns are displayed as stacked after a certain breakpoint. Default is '960px'.


<DataTable value={products} responsiveLayout="stack" breakpoint="640px">

</DataTable>
 
Properties

Any valid attribute is passed to the root element implicitly, extended properties are as follows;

NameTypeDefaultDescription
idstringnullUnique identifier of the element.
valuearraynullAn array of objects to display.
headeranynullHeader content of the table.
footeranynullFooter content of the table.
styleobjectnullInline style of the component.
classNameanynullStyle class of the component.
tableStyleobjectnullInline style of the table element.
tableClassNamestringnullStyle class of the table element.
paginatorbooleanfalseWhen specified as true, enables the pagination.
paginatorPositionstringbottomPosition of the paginator, options are "top","bottom" or "both".
alwaysShowPaginatorbooleantrueWhether to show it even there is only one page.
paginatorClassNamestringnullStyle class of the paginator element.
paginatorTemplatestring|objectFirstPageLink PrevPageLink PageLinks
NextPageLink LastPageLink RowsPerPageDropdown
Template of the paginator. For details, refer to the template section of the paginator documentation for further options.
paginatorLeftElementnullContent for the left side of the paginator.
paginatorRightElementnullContent for the right side of the paginator.
pageLinkSizenumber5Number of page links to display.
rowsPerPageOptionsarraynullArray of integer values to display inside rows per page dropdown.
currentPageReportTemplatestring({currentPage} of {totalPages})Template of the current page report element. Available placeholders are {currentPage}, {totalPages}, {rows}, {first}, {last} and {totalRecords}
paginatorDropdownAppendToDOM element | stringdocument.bodyDOM element instance where the overlay panel should be mounted. Valid values are any DOM Element and 'self'. The self value is used to render a component where it is located.
firstnumber0Index of the first row to be displayed.
rowsnumbernullNumber of rows to display per page.
totalRecordsnumbernullNumber of total records, defaults to length of value when not defined.
lazybooleanfalseDefines if data is loaded and interacted with in lazy manner.
sortFieldstringnullName of the field to sort data by default.
sortOrdernumbernullOrder to sort the data by default.
multiSortMetaarraynullAn array of SortMeta objects to sort the data by default in multiple sort mode.
sortModestringsingleDefines whether sorting works on single column or on multiple columns.
defaultSortOrdernumber1Default sort order of an unsorted column.
removableSortbooleanfalseWhen enabled, columns can have an un-sorted state.
emptyMessageanyNo records foundText to display when there is no data.
selectionModestringnullSpecifies the selection mode, valid values are "single", "multiple", "radiobutton" and "checkbox".
dragSelectionbooleanfalseWhen enabled, a rectangle that can be dragged can be used to make a range selection.
selectionanynullSelected row in single mode or an array of values in multiple mode.
selectionAriaLabelstringnullA field property from the row to add "Select {field}" and "Unselect {field}" ARIA labels to checkbox/radio buttons.
contextMenuSelectionanynullSelected row in single mode or an array of values in multiple mode.
compareSelectionBystringdeepEqualsAlgorithm to define if a row is selected, valid values are "equals" that compares by reference and
"deepEquals" that compares all fields.
dataKeystringnullA property to uniquely identify a record in data.
metaKeySelectionbooleantrueDefines whether metaKey is requred or not for the selection.
When true metaKey needs to be pressed to select or unselect an item and
when set to false selection of each item can be toggled individually. On touch enabled devices, metaKeySelection is turned off automatically.
selectionPageOnlybooleanfalseWhen enabled with paginator and checkbox selection mode, the select all checkbox in the header will select all rows on the current page.
selectionAutoFocusbooleantrueWhen a selectable row is clicked on RadioButton and Checkbox selection, it automatically decides whether to focus on elements such as checkbox or radio.
selectOnEditbooleantrueDetermines whether the cell editor will be opened when clicking to select any row on Selection and Cell Edit modes.
headerColumnGroupColumnGroupnullColumnGroup component for header.
footerColumnGroupColumnGroupnullColumnGroup component for footer.
rowExpansionTemplatefunctionnullFunction that receives the row data as the parameter and returns the expanded row content. You can override the rendering of the content by setting options.customRendering = true.
expandedRowsarray|objectnullA collection of rows or a map object row data keys that are expanded.
resizableColumnsbooleanfalseWhen enabled, columns can be resized using drag and drop.
columnResizeModestringfitDefines whether the overall table width should change on column resize,
valid values are "fit" and "expand".
reorderableColumnsbooleanfalseWhen enabled, columns can be reordered using drag and drop.
reorderableRowsbooleanfalseWhen enabled, rows can be reordered using drag and drop.
filtersarraynullAn array of FilterMetadata objects to provide external filters.
filterDelaynumber300Delay in milliseconds before filtering the data.
filterLocalestringundefinedLocale to use in filtering. The default locale is the host environment's current locale.
scrollablebooleanfalseWhen specified, enables horizontal and/or vertical scrolling.
scrollHeightstringnullHeight of the scroll viewport.
scrollDirectionstringvertical|horizontalOrientation of the scrolling, options are "vertical", "horizontal" and "both".
virtualScrollerOptionsobjectnullWhether to use the virtualScroller feature. The properties of VirtualScroller component can be used like an object in it.
Note: Currently only vertical orientation mode is supported.
frozenWidthstringnullWidth of the frozen part in scrollable DataTable.
frozenValuearraynullItems of the frozen part in scrollable DataTable.
csvSeparatorstring,Character to use as the csv separator.
exportFilenamestringdownloadName of the exported file.
rowGroupModestringnullDefines the row grouping mode, valid values are "subheader" and "rowgroup".
autoLayoutbooleanfalseWhether the cell widths scale according to their content or not.
rowClassNamefunctionnullFunction that takes the row data and
returns an object in "{'styleclass' : condition}" format to define a classname for a particular now.
cellClassNamefunctionnullFunction that takes the cell data and
returns an object in "{'styleclass' : condition}" format to define a classname for a particular now.
rowGroupHeaderTemplatefunctionnullFunction to provide the content of row group header.
rowGroupFooterTemplatefunctionnullFunction to provide the content of row group footer.
loadingbooleanfalseDisplays a loader to indicate data load is in progress.
loadingIconstringpi pi-spinnerThe icon to show while indicating data load is in progress.
tabIndexnumbernullIndex of the element in tabbing order.
stateKeystringnullUnique identifier of a stateful table to use in state storage.
stateStoragestringsessionDefines where a stateful table keeps its state,
valid values are "session" for sessionStorage, "local" for localStorage and "custom".
editModestringcellDefines editing mode, options are "cell" and "row".
editingRowsarray|objectnullA collection of rows to represent the current editing data in row edit mode.
exportFunctionfunctionnullA function to implement custom export. Need to return string value.
event.data: Field data.
event.field: Column field. event.rowData: Row data. event.column: Column.
expandableRowGroupsbooleanfalseMakes row groups toggleable, default is false.
rowHoverbooleanfalseWhen enabled, background of the rows change on hover.
showGridlinesbooleanfalseWhether to show grid lines between cells.
stripedRowsbooleanfalseWhether to displays rows with alternating colors.
sizestringnormalDefine to set alternative sizes. Valid values: "small", "normal" and "large".
responsiveLayoutstringstackDefines the responsive mode, valid options are "stack" and "scroll".
breakpointstring960pxThe breakpoint to define the maximum width boundary when using stack responsive layout.
filterDisplaystringmenuLayout of the filter elements, valid values are "row" and "menu".
expandedRowIconstringpi pi-chevron-downIcon of the row toggler to display the row as expanded.
collapsedRowIconstringpi pi-chevron-upIcon of the row toggler to display the row as collapsed.
globalFilteranynullValue of the global filter to use in filtering.
globalFilterFieldsstring[]nullDefine fields to be filtered globally.
globalFilterMatchModestringcontainsDefines filterMatchMode; "startsWith", "contains", "endsWith", "equals", "notEquals", "in", "lt", "lte", "gt", "gte" and "custom".
showSelectionElementfunctionnullFunction that returns a boolean by passing the row data to decide if the radio or checkbox should be displayed per row.
showRowReorderElementfunctionnullFunction that returns a boolean by passing the row data to decide if the row reorder element should be displayed per row.
isDataSelectablefunctionnullFunction that returns a boolean to decide whether the data should be selectable.
customSaveStatefunctionnullA function to implement custom saveState with stateStorage="custom".
state: the object to be stored.
customRestoreStatefunctionnullA function to implement custom restoreState with stateStorage="custom". Need to return state object.
Events
NameParametersDescription
onSelectionChangeevent.originalEvent: Browser event
event.value: Selection object
Callback to invoke when selection changes.
onContextMenuSelectionChangeevent.originalEvent: Browser event
event.value: Selection object
Callback to invoke when a row selected with right click.
onRowToggleevent.data: Expanded rowsCallback to invoke when a row is toggled or collapsed.
onColumnResizeEndevent.element: DOM element of the resized column.
event.column: Properties of the resized column.
event.delta: Change in column width
Callback to invoke when a column is resized.
onColumnResizerClickevent.originalEvent: Browser event
event.element: DOM element of the column.
event.column: Properties of the column.
Callback to invoke when a resizer element is clicked.
onColumnResizerDoubleClickevent.originalEvent: Browser event
event.element: DOM element of the column.
event.column: Properties of the column.
Callback to invoke when a resizer element is double clicked.
onSortevent.sortField: Field to sort against.
event.sortOrder: Sort order as integer.
event.multiSortMeta: MultiSort metadata.
Callback to invoke on sort.
onPageevent.first: Index of the first row.
event.rows: Rows per page.
Callback to invoke on pagination.
onFilterevent.filters: Collection of active filters.Callback to invoke on filtering.
onAllRowsSelectevent.originalEvent: Browser event.
event.data: Selected rows data.
event.type: Type of the selection, valid value is "all".
Callback to invoke when all rows are selected using the header checkbox.
onAllRowsUnselectevent.originalEvent: Browser event.
event.data: Unselected rows data.
event.type: Type of the selection, valid value is "all".
Callback to invoke when all rows are unselected using the header checkbox.
onRowClickevent.originalEvent: Browser event
event.data: Clicked row data
event.index: Clicked row data index
Callback to invoke when a row is clicked.
onRowDoubleClickevent.originalEvent: Browser event
event.data: Clicked row data
event.index: Clicked row data index
Callback to invoke when a row is double clicked.
onRowSelectevent.originalEvent: Browser event.
event.data: Selected row data.
event.type: Type of the selection, valid values are "row", "radio" or "checkbox".
Callback to invoke when a row is selected.
onRowUnselectevent.originalEvent: Browser event.
event.data: Unselected row data.
event.type: Type of the selection, valid values are "row", "radio" or "checkbox".
Callback to invoke when a row is unselected.
onRowExpandevent.originalEvent: Browser event.
event.data: Expanded row data.
Callback to invoke when a row is expanded.
onRowCollapseevent.originalEvent: Browser event.
event.data: Collapsed row data.
Callback to invoke when a row is collapsed.
onContextMenuevent.originalEvent: Original event instance.
event.data: Collapsed row data
Callback to invoke when a context menu is clicked.
onColReorderevent.originalEvent: Browser event
event.dragIndex: Index of the dragged column
event.dropIndex: Index of the dropped column
event.columns: Columns array after reorder.
Callback to invoke when a column is reordered.
onRowOrderevent.originalEvent: Browser event.
event.value: New value after reorder
event.dragIndex: Index of the dragged row
event.dropIndex: Index of the drop location
Callback to invoke when a row is reordered.
onValueChangevalue: Value displayed by the table.Callback to invoke after filtering and sorting to pass the rendered value.
rowEditValidatordata: Editing row dataCallback to invoke to validate the editing row when the save icon is clicked on row editing mode.
onRowEditInitevent.originalEvent: Browser event
event.data: Editing row data
Callback to invoke when the editing icon is clicked on row editing mode.
onRowEditSaveevent.originalEvent: Browser event
event.data: Editing row data
Callback to invoke when the save icon is clicked on row editing mode.
onRowEditCancelevent.originalEvent: Browser event
event.data: Editing row data
event.index: Editing row data index
Callback to invoke when the cancel icon is clicked on row editing mode.
onRowEditChangeevent.originalEvent: Browser event
event.data: Editing rows data
event.index: Current editing row data index
Callback to invoke when the row editor is programmatically shown/hidden on row editing mode.
onRowEditCompleteevent.originalEvent: Browser event
event.data: Original rows data
event.newData: Editing rows data
event.field: Column field
event.index: Current editing row data index
Callback to invoke when row edit is completed.
onStateSavestate: Table stateCallback to invoke table state is saved.
onStateRestorestate: Table stateCallback to invoke table state is restored.
Methods
NameParametersDescription
reset-Resets sort, filter, paginator and columnorder state.
exportCSV-Exports the data to CSV format.
filtervalue: the filter value
field: the filter field
mode: filter match mode.
Filters the data.
closeEditingCell-Closes the current editing cell when incell editing is enabled.
resetColumnOrder-Resets column order when reorderableColumns is enabled.
resetScroll-Resets scroll position.
restoreTableStatestateStored states can be loaded at any time using this method if there is a stateStorage property.
Styling

Following is the list of structural style classes, for theming classes visit theming page.

NameElement
p-datatableContainer element.
p-datatable-scrollableContainer element when table is scrollable.
p-datatable-headerHeader section.
p-datatable-footerFooter section.
p-datatable-wrapperWrapper of table element.
p-datatable-tableTable element.
p-datatable-theadTable thead element.
p-datatable-tbodyTable tbody element.
p-datatable-tfootTable tfoot element.
p-column-titleTitle of a column.
p-sortable-columnSortable column header.
p-frozen-columnFrozen column header.
p-rowgroup-headerHeader of a rowgroup.
p-rowgroup-footerFooter of a rowgroup.
p-datatable-row-expansionExpanded row content.
p-row-togglerToggle element for row expansion.
p-datatable-emptymessageCell containing the empty message.
p-row-editor-initPencil button of row editor.
p-row-editor-initSave button of row editor.
p-row-editor-initCancel button of row editor.
Accessibility

This section is under development. After the necessary tests and improvements are made, it will be shared with the users as soon as possible.

Dependencies

None.

Component Scale

Input Style

Ripple Effect

Free Themes

Built-in component themes created by the PrimeReact Theme Designer.

Bootstrap
Blue
Purple
Blue
Purple
Material Design
Indigo
Deep Purple
Indigo
Deep Purple
Material Design Compact
Indigo
Deep Purple
Indigo
Deep Purple
Tailwind
Tailwind Light
Fluent UI
Blue
PrimeOne Design - 2022 NEW
Lara Indigo
Lara Blue
Lara Purple
Lara Teal
Lara Indigo
Lara Blue
Lara Purple
Lara Teal
PrimeOne Design - 2021
Saga Blue
Saga Green
Saga Orange
Saga Purple
Vela Blue
Vela Green
Vela Orange
Vela Purple
Arya Blue
Arya Green
Arya Orange
Arya Purple
Premium Themes

Premium themes are only available exclusively for PrimeReact Theme Designer subscribers and therefore not included in PrimeReact core.

Soho Light
Soho Dark
Viva Light
Viva Dark
Mira
Nano

Legacy Free Themes

Nova
Nova Alt
Nova Accent
Luna Blue
Luna Green
Luna Amber
Luna Pink
Rhea

Premium Create-React-App Templates

Beautifully crafted premium create-react-app application templates by the PrimeTek design team.

Sakai
Atlantis
Freya
Ultima
Diamond
Sapphire
Serenity
Babylon
Avalon
Apollo
Roma