Getting Started with the KendoReact Data Grid

Welcome to the KendoReact Data Grid!

After completing this tutorial, you will have learned the basic concepts and configuration of the Grid, and you will have developed the following sample app:

Example
View Source
Change Theme:

tip If you prefer video, watch the React Data Grid Video Tutorial.

Before You Begin

npx create-react-app my-app --template typescript
npm start

This guide requires that you have basic knowledge of React and TypeScript, and that you have already created [a blank React project]({% slug getting_started %}). If you are starting from a Next.js project, you need to use the KendoReact Server Data Grid.

For the best results, follow the guide step-by-step.

tip You can speed up the development of your KendoReact Data Grid application with the Kendo UI Template Wizard for Visual Studio Code.

Install the Component

npm i @progress/kendo-react-grid @progress/kendo-data-query @progress/kendo-react-data-tools @progress/kendo-react-inputs @progress/kendo-react-intl @progress/kendo-react-dropdowns @progress/kendo-react-dateinputs @progress/kendo-drawing @progress/kendo-react-animation @progress/kendo-licensing @progress/kendo-react-buttons @progress/kendo-react-treeview @progress/kendo-react-popup @progress/kendo-svg-icons

npm i @progress/kendo-theme-default

Run these commands in the root of your React project to install the KendoReact Data Grid and its dependencies, and the Kendo UI Default theme.

Import the Component

import { Grid, GridColumn as Column } from "@progress/kendo-react-grid";
import "@progress/kendo-theme-default/dist/all.css";

Place the import statements in the App component file (for example: src/App.tsx) for your project.

Note that you are also importing the GridColumn component, but under the Column alias.

Use the Component

Using the KendoReact Data Grid requires either a commercial license key or an active trial license key. Follow the instructions on the KendoReact My License page to activate your license.

The KendoReact Data Grid is a powerful tool for creating responsive, accessible, and customizable applications that require the displaying and management of large datasets. This section will take you through a basic Data Grid setup workflow, starting with the column definition and ending with some basic styling.

Load and Show Data

import products from "./shared-gd-products.json";

const App = () => {
  return (
    <Grid data={products}>

    </Grid>
  );
}

export default App;
  1. Use the dataset from the demo source files at the top of the guide to create a shared-gd-products.json file locally in your project.
  2. Use an import statement to reference the data file.
  3. Add a <Grid> definition.
  4. Use the data prop to load the data in your Data Grid.

You now have a simple grid that shows all the data from shared-gd-products.json.

Define Columns

    <Grid data={products}>
        <Column field="ProductID" title="ID" />
        <Column field="ProductName" title="Name"/>
        <Column field="Category.CategoryName" title="Category" />
        <Column field="UnitPrice" title="Price" />
        <Column field="UnitsInStock" title="In stock" />
        <Column field="Discontinued" title="Discontinued" />
    </Grid>
  1. For every column that you want to show, add a <Column> definition. Note that Column is an alias for GridColumn.
  2. Use the field prop to bind the column to the respective data field in your data set. For nested fields, use a parent.child notation (for example: Category.CategoryName).
  3. Use the title prop to set a custom title for the column. If not set, the title defaults to the name of the field.

You now have a grid that shows a sub-set of the data and has custom column names.

Add Pagination

import React, { useState } from 'react';
import { Grid, GridColumn as Column, GridDataStateChangeEvent } from "@progress/kendo-react-grid";
import { process, State, DataResult } from "@progress/kendo-data-query";
import "@progress/kendo-theme-default/dist/all.css";

import products from "./shared-gd-products.json";

const App = () => {
  const initialDataState: State = {
  take: 10,
  skip: 0
};

  const [dataState, setDataState] = useState<State>(initialDataState);

  const handleDataStateChange = (event: GridDataStateChangeEvent) => {
    setDataState(event.dataState);
  };

  return (
    <Grid
      data={process(products, dataState) as DataResult}
      pageable={true}
      skip={dataState.skip}
      take={dataState.take}
      total={products.length}
      {...dataState}
      onDataStateChange={handleDataStateChange}
    >
      <Column field="ProductID" title="ID" />
      <Column field="ProductName" title="Name"/>
      <Column field="Category.CategoryName" title="Category" />
      <Column field="UnitPrice" title="Price" />
      <Column field="UnitsInStock" title="In stock" />
      <Column field="Discontinued" title="Discontinued" />
    </Grid>
  );
}

export default App;
  1. Add the import statements required for data handling. You now also need the [Data Query(https://www.telerik.com/kendo-react-ui/components/dataquery/)] package to handle your data operations.

    import React, { useState } from 'react';
    import { Grid, GridColumn as Column, GridDataStateChangeEvent } from "@progress/kendo-react-grid";
    import { process, State, DataResult } from "@progress/kendo-data-query";
  2. Inside your App, define the initial state of your data. For example, rendering only the first 10 items of the data set. Note the use of the skip and take props of the <Grid> which are responsible for calculating the current page and the number of items shown.

    const initialDataState: State = {
        take: 10,
        skip: 0
    };
  3. Add the logic that handles your data operations affecting the state of the Grid.
    Because this guide will introduce you to multiple data operations, the example uses the process method and the onDataStateChange event. Both are the recommended practice when implementing multiple data operations within the same Data Grid.

    const [dataState, setDataState] = useState<State>(initialDataState);
    
    const handleDataStateChange = (event: GridDataStateChangeEvent) => {
    setDataState(event.dataState);
    };
  4. Re-define your data source, pass the Grid state as a prop, and configure some additional <Grid> props to enable pagination.

    • pageable enables the built-in pager.
    • skip helps calculate the current page.
    • take determines how many items are rendered on the page.
    • total defines the total number of records in the dataset.
    • onDataStateChange fires when the data state of the Grid changes.
    <Grid
      data={process(products, dataState) as DataResult}
      pageable={true}
      skip={dataState.skip}
      take={dataState.take}
      total={products.length}
      {...dataState}
      onDataStateChange={onDataStateChange}
    >

Note that after the initial definition of your Grid state change handling, you no longer need to make any changes.

You now have a grid with enabled pagination.

Enable Filtering

...
const initialDataState: State = {
        take: 10,
        skip: 0,
        filter: undefined
    };
...

    <Grid
      ...
      filterable={true}
      filter={dataState.filter}
      ...
    >
      ...
    </Grid>
  1. Update your initial data state to include the filter prop. This example sets it as undefined to show the data unfiltered, but you can pre-define your own filter logic instead.
  2. Configure the <Grid> props to enable filtering.
    • filterable enables the built-in filter row, rendered right below the column titles.
    • filter is the descriptor by which the data is filtered.

Enable Sorting

...
const initialDataState: State = {
  take: 10,
  skip: 0,
  filter: undefined,
  sort: undefined
};
...
<Grid
      ...
      sortable={true}
      sort={dataState.sort}
      ...
    >
    </Grid>
  1. Update your initial data state to include the sort prop. This example sets it as undefined to show the data unsorted, but you can pre-define your own sorting logic instead.
  2. Configure the <Grid> props to enable sorting.
    • sortable enables the built-in sorting which triggers when you click the column title. When the column is sorted, an arrow indicating the sorting direction shows next to the column title.
    • sort is the descriptor by which the data is sorted.

You now have a grid with enabled filtering.

Show Custom Content

import { ..., GridCustomCellProps } from "@progress/kendo-react-grid";
...
const categoryNameEmoticons: { [key: string]: string } = {
    'Beverages': '🍹',
    'Condiments': '🥫',
    'Confections': '🍬',
    'Dairy Products': '🥛',
    'Grains/Cereals': '🌾',
    'Meat/Poultry': '🍗',
    'Produce': '🥕',
    'Seafood': '🐟',
  };

const EmoticonCell = (props: GridCustomCellProps) => {
    const emoticon = categoryNameEmoticons[props.dataItem.Category.CategoryName] || '❓';
    return (
      <td>
        <span style={{ fontSize: '16px', marginRight: '8px' }}>{emoticon}</span>
        {props.dataItem.Category.CategoryName}
      </td>
    );
  };
...

<Column field="Category.CategoryName" title="Category" cell={EmoticonCell} />
  1. Add the import statement for the GridCustomCellProps component.

  2. Add the logic that maps your product categories to a visual representation (via emoticons). Note that you need to explicitly set the types for the mappings.

    const categoryNameEmoticons: { [key: string]: string } = {
        'Beverages': '🍹',
        'Condiments': '🥫',
        'Confections': '🍬',
        'Dairy Products': '🥛',
        'Grains/Cereals': '🌾',
        'Meat/Poultry': '🍗',
        'Produce': '🥕',
        'Seafood': '🐟',
    };
  3. Add the logic that creates your custom cell. Note that you need to replace the entire cell (<td> block) and not just its contents. This example also sets a default emoticon to use if there's no mapping for the category.

    const EmoticonCell = (props: GridCustomCellProps) => {
        const categoryEmoticon = categoryNameEmoticons[props.dataItem.Category.CategoryName] || '❓';
        return (
            <td>
                <span style={{ fontSize: '16px', marginRight: '8px' }}>{categoryEmoticon}</span>
                {props.dataItem.Category.CategoryName}
            </td>
        );
    };
  4. Update your <Column> definition and set the cell prop to render your custom cells.

    <Grid
      data={dataResult}
      pageable={true}
      skip={dataState.skip}
      take={dataState.take}
      total={products.length}
      filterable={true}
      filter={dataState.filter}
      sortable={true}
      sort={dataState.sort}
      onDataStateChange={onDataStateChange}
    >
      <Column field="ProductID" title="ID" />
      <Column field="ProductName" title="Name" />
      <Column field="Category.CategoryName" title="Category" cell={EmoticonCell} />
      <Column field="UnitPrice" title="Price" />
      <Column field="UnitsInStock" title="In stock" />
    </Grid>

You now have a grid with a column that renders additional content in its cells.

Enable Inline Editing

import React, { useState } from 'react';
import {
  Grid,
  GridColumn as Column,
  GridCustomCellProps,
  GridItemChangeEvent,
  GridRowClickEvent,
  GridDataStateChangeEvent,
 } from "@progress/kendo-react-grid";
import {
  process,
  State,
  DataResult
} from "@progress/kendo-data-query";
import "@progress/kendo-theme-default/dist/all.css";

import products from "./shared-gd-products.json";

interface ProductCategory {
  CategoryID?: number;
  CategoryName?: string;
  Description?: string;
}

interface Product {
  ProductID: number;
  ProductName?: string;
  SupplierID?: number;
  CategoryID?: number;
  QuantityPerUnit?: string;
  UnitPrice?: number;
  UnitsInStock?: number;
  UnitsOnOrder?: number;
  ReorderLevel?: number;
  Discontinued?: boolean;
  Category?: ProductCategory;
  inEdit?: boolean | string;
}

const categoryNameEmoticons: { [key: string]: string } = {
  'Beverages': '🍹',
  'Condiments': '🥫',
  'Confections': '🍬',
  'Dairy Products': '🥛',
  'Grains/Cereals': '🌾',
  'Meat/Poultry': '🍗',
  'Produce': '🥕',
  'Seafood': '🐟',
};

const EmoticonCell = (props: GridCustomCellProps) => {
  const categoryEmoticon = categoryNameEmoticons[props.dataItem.Category.CategoryName] || '❓';
  return (
    <td>
      <span style={{ fontSize: '16px', marginRight: '8px' }}>{categoryEmoticon}</span>
      {props.dataItem.Category.CategoryName}
    </td>
  );
};

const App = () => {
  const initialDataState: State = {
    take: 10,
    skip: 0,
    filter: undefined,
    sort: undefined
  };

  const [dataState, setDataState] = useState<State>(initialDataState);
  const [data, setData] = useState<Array<Product>>(products);
  const [editID, setEditID] = useState<number | null>(null);

  const handleDataStateChange = (event: GridDataStateChangeEvent) => {
    setDataState(event.dataState);
  };

  const handleRowClick = (event: GridRowClickEvent) => {
    setEditID(event.dataItem.ProductID);
  };

  const handleItemChange = (event: GridItemChangeEvent) => {
    const inEditID = event.dataItem.ProductID;
    const field = event.field || "";
    const newData = data.map((item) =>
      item.ProductID === inEditID ? { ...item, [field]: event.value } : item
    );
    setData(newData);
  };

  return (
    <Grid
      data={{
        ...process(data, dataState) as DataResult,
        data: process(data, dataState).data.map((item: Product) => ({
          ...item,
          inEdit: item.ProductID === editID,
        })),
      }}
      pageable={true}
      skip={dataState.skip}
      take={dataState.take}
      total={products.length}
      filterable={true}
      filter={dataState.filter}
      sortable={true}
      sort={dataState.sort}
      editField="inEdit"
      onItemChange={handleItemChange}
      onDataStateChange={handleDataStateChange}
      onRowClick={handleRowClick}
      {...dataState}
    >
      <Column field="ProductID" title="ID" editable={false} />
      <Column field="ProductName" title="Name" editor="text"/>
      <Column field="Category.CategoryName" title="Category" cell={EmoticonCell} editable={false} />
      <Column field="UnitPrice" title="Price" editor="numeric" />
      <Column field="UnitsInStock" title="In stock" editor="numeric" />
      <Column field="Discontinued" title="Discontinued" editor="boolean" />
    </Grid>
  );
}

export default App;
  1. Add the import statements for GridItemChangeEvent and GridRowClickEvent.

  2. Define interfaces for the data in your shared-gd-products.json file. Define the type for every data field from the dataset and add the boolean inEdit property. You will later use it to indicate if the item is being edited.

    interface ProductCategory {
      CategoryID?: number;
      CategoryName?: string;
      Description?: string;
    }
    
    interface Product {
      ProductID: number;
      ProductName?: string;
      SupplierID?: number;
      CategoryID?: number;
      QuantityPerUnit?: string;
      UnitPrice?: number;
      UnitsInStock?: number;
      UnitsOnOrder?: number;
      ReorderLevel?: number;
      Discontinued?: boolean;
      Category?: ProductCategory;
      inEdit?: boolean | string;
    }
  3. Add logic that handles row clicks. Set your editID to the ProductID of the current data item.

    const [editID, setEditID] = useState<number | null>(null);
    
    const handleRowClick = (event: GridRowClickEvent) => {
        setEditID(event.dataItem.ProductID);
    };
  4. Add logic that handles the item editing.

    const [data, setData] = useState<Array<Product>>(products);
    
    const handleItemChange = (event: GridItemChangeEvent) => {
        const inEditID = event.dataItem.ProductID;
        const field = event.field || "";
        const newData = data.map((item) =>
        item.ProductID === inEditID ? { ...item, [field]: event.value } : item
        );
        setData(newData);
    };
  5. Configure the <Grid> props to enable editing.

    • Update your data binding.
    • Set the editField prop to enable editing.
    • Set the onItemChange and onRowClick props to finish your event handling.
    <Grid
      data={{
        ...process(data, dataState) as DataResult,
        data: process(data, dataState).data.map((item: Product) => ({
          ...item,
          inEdit: item.ProductID === editID,
        })),
      }}
      pageable={true}
      skip={dataState.skip}
      take={dataState.take}
      total={products.length}
      filterable={true}
      filter={dataState.filter}
      sortable={true}
      sort={dataState.sort}
      editField="inEdit"
      onItemChange={handleItemChange}
      onDataStateChange={handleDataStateChange}
      onRowClick={handleRowClick}
      {...dataState}
    >
  6. Update your <Column> definitions.

    • Set editable={false} for the ID and Category columns. This disables editing for the cells in the respective columns.
    • Set the editor prop for the other <Column>s.
    <Column field="ProductID" title="ID" editable={false} />
    <Column field="ProductName" title="Name" editor="text"/>
    <Column field="Category.CategoryName" title="Category" cell={EmoticonCell} editable={false} />
    <Column field="UnitPrice" title="Price" editor="numeric" />
    <Column field="UnitsInStock" title="In stock" editor="numeric" />
    <Column field="Discontinued" title="Discontinued" editor="boolean" />

You now have a grid with enabled editing.

Style the Component

tip Are you looking for guidance around how to create visually appealing and consistent user interfaces with Telerik UI components? Check out the Progress Design System.

With the import "@progress/kendo-theme-default/dist/all.css"; statement present in your code, you already have professionally designed styling applied to your app out-of-box. In this section, you will learn how to customize the look and feel of your KendoReact Data Grid.

This guide shows you how you can apply inline styling directly to the component or by using the KendoReact Data Grid or the Kendo Default theme styling variables.

For more information and additional approaches to styling, see KendoReact Data Grid Styling Basics.

Enable Responsive Design

Currently, the KendoReact Data Grid does not provide an explicit setting for responsive design. However, out-of-the-box the Data Grid columns will resize to accommodate any changes in your screen size. You can also configure the separate Grid components to be responsive (for example, the pager). Additionally, you can implement your own customizations to make various elements of the Data Grid more responsive.

If you are looking to create a more compact KendoReact Grid, check the size prop.

Customize the Theme

tip ThemeBuilder helps you achieve a unique appearance for your React apps and delivers full control over the visual elements of the KendoReact UI components.

This project already relies on the Kendo Default theme. It provides neutral styling which you can customize to fit your design needs.

To customize the theme:

  1. In the root of the project, install the @progress/kendo-theme-core package. It provides a collection of functions and mixins that will help you customize your theme.
npm i @progress/kendo-theme-core
  1. In the root of the project, install the Sass package.
npm i sass
  1. Inside your project, create a custom-theme.scss file and replace the Kendo theme import with it.
// In your custom-theme.scss

@import '@progress/kendo-theme-core/scss/functions/index.import.scss';

// All custom SCSS will between the @import statements

@import '@progress/kendo-theme-default/dist/all.scss';
// In your app code
import './custom-theme.scss';

Change the Theme Colors

@import '@progress/kendo-theme-core/scss/functions/index.import.scss';

$kendo-colors: ();
$kendo-colors: k-map-merge(
    $kendo-colors,
    k-generate-color-variations('primary', #2B2BB2, 'default'),
    k-map-set($kendo-colors, on-app-surface, #00216B)
);

@import '@progress/kendo-theme-default/dist/all.scss';
  1. Create an empty $kendo-colors map. All your theme colors are defined through the variables in the $kendo-colors map.
  2. Merge your new map with the existing definition for the Default theme. In your new map, re-generate the primary palette of the default theme based on the #2B2BB2 color. Manually set the $kendo-colors-on-app-surface variable to the #00216B.

You now have a restyled Data Grid that uses a set of custom blue hues. The colors apply to all major elements and interactions.

Style the Fonts

$kendo-font-size: 1rem;
$kendo-font-family: Helvetica;

Set the font size in your custom-theme.scss.

Your grid now uses a larger font size. For a complete list of the available typography variables, see Customizing Typography.

Style the Header

.k-column-title {
    font-weight: 700;
    font-size: 18px;
}

Instead of the built-in variables, you can also customize your code using CSS selectors in custom-theme.scss. Apply a new size and font weight for your column titles via the .k-column-title class.

You now have a more prominent header.

Style Columns

<Column field="ProductID" title="ID" editable={false} filterable={false} width="75px" />
<Column field="ProductName" title="Name" editor="text" />
<Column field="Category.CategoryName" title="Category" cell={EmoticonCell} editable={false} width="300px"/>
<Column field="UnitPrice" title="Price" editor="numeric" width="150px"/>
<Column field="UnitsInStock" title="In stock" editor="numeric" width="150px"/>
<Column field="Discontinued" title="Discontinued" editor="boolean" width="150px" />

Set your column widths inline in their <Column> definitions. Note that this example also disables filtering by ID by setting fitlerable={false} for the ID column.

You now have a Data Grid with fixed column widths. You can also use the className prop to apply a styling class to the respective column.

Style Rows

$kendo-grid-alt-bg: #DCECFF;

In custom-theme.scss, change the background color of your alternating rows. You can use one of the built-in Grid styling variables. For a complete list of the styling variables for the React grid, see Customizing Grid.

You now have your alternating rows show a different background color. You can also use the rowRender function to modify the appearance of the rows.

KendoReact Data Grid APIs

Grid API

KendoReact Data Grid Dependencies

The Grid package requires you to install the following peer dependencies in your application:

Package NameDescription
react 16.8.2*Contains the functionality necessary to define React components.
react-domContains the React renderer for the web.
@progress/kendo-licensingContains the internal infrastructure related to licensing.
@progress/kendo-react-intlContains the KendoReact Internationalization package that applies the desired cultures by providing services and pipes for the parsing and formatting of dates and numbers.
@progress/kendo-data-queryApplies sorting, filtering, grouping, and aggregate data operations.
@progress/kendo-react-animationEnables the animations in the KendoReact components.
@progress/kendo-react-data-toolsDelivers components required to manage and control the data in the application.
@progress/kendo-react-dateinputsContains the KendoReact Date Inputs components that are used to select the date and time for an appointment.
@progress/kendo-react-dropdownsContains the KendoReact Dropdowns, which allows users to choose from a predefined list of options.
@progress/kendo-react-inputsContains the KendoReact Inputs, which the input of data, based on a specific and predefined format.
@progress/kendo-drawingContains the Drawing library, which provides interactive vector graphics.
@progress/kendo-react-buttonsContains the KendoReact Buttons library, which provides buttons.
@progress/kendo-react-treeviewContains the KendoReact TreeView package that is used in the DropDowns.
@progress/kendo-react-popupContains the KendoReact Popup components.
@progress/kendo-svg-iconsContains the KendoReact SVG icons.