Filtering

Filtering allows you to limit the rows available in the table.

Both client-side and server-side filtering are available in Infinite Table - but the way the are configured is pretty similar, so this page documents the common parts, while pointing to the respective pages for the differences.

Configuring Filters for Columns

The most common way to use filtering in Infinite Table is by configuring filters for columns (this works both for client-side and server-side filtering).

You specify an uncontrolled defaultFilterValue on the <DataSource /> component (or the controlled version, filterValue) and the specified value will be used as the initial filter.

Based on the column type, the correct filter editor is displayed in the column header, along with the correct operator icon. In the UI, you can change the operator being used for the filter.

Specifying an initial filter value for the DataSource
<DataSource<Developer>
  data={...}
  defaultFilterValue={[
    {
      field: 'age',
      filter: {
        operator: 'gt',
        value: 30,
        type: 'number'
      }
    }
  ]}
>
  <InfiniteTable<Developer>
    columns={...}
  />
</DataSource>

Note

If you don't need to specify some initial filters, but want the column filter bar to be visible, you need to specify defaultFilterValue = [] (or the controlled filterValue = []).

Specifying any of those props will make the column filter bar visible.

Whenever filters change, onFilterChange will be called with the new filter value - note however, it might not be called immediately, due to the filterDelay prop.

The above snippet will show a number filter for the age column. There are two filter types available at this stage in Infinite Table:

  • string - with the following operators available: contains, eq, startsWith and endsWith
  • number - with the following operators available: eq,neq, gt, gte, lt and lte

Defining Filterable Columns

By default, all columns are filterable.

If you want to make columns by default not filterable, use the columnDefaultFilterable prop and set it to false.

You can specifically configure each column by using the defaultFilterable property - this overrides the global columnDefaultFilterable prop.

Defining a Filter Type for a Column

Besides being filterable, a column can decide what type of filter it will display.

Use the columns.type property to specify the type of filter the column will use. Using the type property also configures the data type of the column, which in turn determines the sort type.

Note

If the type of filter you want to show does not match the column type, you can specify the filter with the column.filterType property. Only use this when the type of the data differs from the type of the filter (eg: you have a numeric column, with a custom filter type).

Understanding Filter Types

A filter type is a concept that defines how a certain type of data is to be filtered. A filter type will have

  • a key - the key used to define the filter in the filterTypes object
  • a label,
  • an array of values considered to be empty values - when any of these values is used in the filter, the filter will not be applied.
  • an array of operators
  • a default operator.

Let's imagine you have a DataSource with developers, each with a salary column, and for that column you want to allow >, >=, < and <= comparisons (operators).

For this, you would define the following filter type:


const filterTypes = {
  income: {
    label: 'Income', 
    emptyValues: ['', null, undefined],
    defaultOperator: 'gt',
    operators: [
      {
        name: 'gt',
        label: 'Greater than',
        fn: ({ currentValue, filterValue, emptyValues }) => {
          if (emptyValues.has(currentValue)) {
            return true;
          }
          return currentValue > filterValue;
        }
      },
      {
        name: 'gte',
        //...
      },
      {
        name: 'lt',
        //...
      },
      {
        name: 'lte',
        //...
      }
    ]
  }
}

Note

Each operator for a certain filter type needs to at least have a name and fn defined. The fn property is a function that will be called when client-side filtering is enabled, with an object that has the following properties:

  • currentValue - the cell value of the current row for the column being filtered
  • filterValue - the value of the filter editor
  • emptyValues - the array of values considered to be empty values for the filter type
  • data - the current row data object - typeof DATA_TYPE
  • index - the index of the current row in the table - number
  • dataArray - the array of all rows originally in the table - typeof DATA_TYPE[]
  • field? - the field the current column is bound to (can be undefined if the column is not bound to a field)
Client-side filtering in action with custom filter type

The salary column has a custom filter type, with the following operators: gt, gte, lt and lte.

View Mode
Fork
import * as React from 'react';

import {
  DataSourceData,
  DataSource,
  InfiniteTable,
  InfiniteTablePropColumns,
} from '@infinite-table/infinite-react';

type Developer = {
  id: number;
  firstName: string;
  lastName: string;

  currency: string;
  preferredLanguage: string;
  stack: string;
  canDesign: 'yes' | 'no';

  salary: number;
};

const data: DataSourceData<Developer> = () => {
  return fetch('https://infinite-table.com/.netlify/functions/json-server' + `/developers1k-sql?`)
    .then((r) => r.json())
    .then((data: Developer[]) => data);
};

const columns: InfiniteTablePropColumns<Developer> = {
  id: {
    field: 'id',
    type: 'number',
    defaultWidth: 100,
  },
  salary: {
    defaultFilterable: true,
    field: 'salary',
    type: 'number',
    filterType: 'salary',
  },

  firstName: {
    field: 'firstName',
  },
  stack: { field: 'stack' },
  currency: { field: 'currency', defaultFilterable: false },
};

function getIcon(icon: string) {
  return () => (
    <div
      style={{
        width: 20,
        display: 'flex',
        justifyContent: 'center',
        flexFlow: 'row',
      }}
    >
      {icon}
    </div>
  );
}

const domProps = {
  style: {
    height: '100%',
  },
};
export default () => {
  return (
    <>
      <React.StrictMode>
        <DataSource<Developer>
          data={data}
          primaryKey="id"
          defaultFilterValue={[]}
          filterDelay={0}
          filterMode="local"
          filterTypes={{
            salary: {
              defaultOperator: 'gt',
              emptyValues: ['', null, undefined],
              operators: [
                {
                  name: 'gt',
                  label: 'Greater Than',
                  components: {
                    Icon: getIcon('>'),
                  },
                  fn: ({ currentValue, filterValue }) => {
                    return currentValue > filterValue;
                  },
                },
                {
                  name: 'gte',
                  components: {
                    Icon: getIcon('>='),
                  },
                  label: 'Greater Than or Equal',
                  fn: ({ currentValue, filterValue }) => {
                    return currentValue >= filterValue;
                  },
                },
                {
                  name: 'lt',
                  components: {
                    Icon: getIcon('<'),
                  },
                  label: 'Less Than',
                  fn: ({ currentValue, filterValue }) => {
                    return currentValue < filterValue;
                  },
                },
                {
                  name: 'lte',
                  components: {
                    Icon: getIcon('<='),
                  },
                  label: 'Less Than or Equal',
                  fn: ({ currentValue, filterValue }) => {
                    return currentValue <= filterValue;
                  },
                },
              ],
            },
          }}
        >
          <InfiniteTable<Developer>
            domProps={domProps}
            columnDefaultWidth={150}
            columnMinWidth={50}
            columns={columns}
          />
        </DataSource>
      </React.StrictMode>
    </>

Specifying the filter mode

As already mentioned, filtering can happen either client-side or server-side. If the DataSource data property is a function (and not an array or a Promise), then the filtering will happen server-side by default.

However, you can explicitly specify where the filtering should happen by setting the filterMode property on the <DataSource /> component - possible values are

  • filterMode="local" - filtering will happen client-side
  • filterMode="remote" - filtering will happen remotely and the filterValue will be passed as a property to the parameter object sent to the data function.

Filter mode ⚠️

Explicitly specify filterMode as either "local" or "remote" if you want to change the default behavior.

Filtering Columns Not Bound to a Field

If a column is not bound to a field, it can still be used for filtering, even client-side filtering, if it is configured with a columns.valueGetter.

Note

If you don't need a default filter value, the filterValue that's set when the user interacts with the column filter will use the column valueGetter to filter values.

If however, you need initial filtering by that column, the filterValue needs to specify a valueGetter itself.

defaultFilterValue={[
  {
    id: 'salary',
    valueGetter: ({ data }) => data.salary,
    filter: {
      operator: 'gt',
      value: '',
      type: 'number',
    }
  },
]}
Filtering a column not bound to a field

The salary column is not bound to a field - however, it can still be used for filtering, as it's configured with a valueGetter.

View Mode
Fork
import * as React from 'react';

import {
  DataSourceData,
  InfiniteTable,
  InfiniteTablePropColumns,
  DataSource,
} from '@infinite-table/infinite-react';

type Developer = {
  id: number;
  firstName: string;
  lastName: string;

  currency: string;
  preferredLanguage: string;
  stack: string;
  canDesign: 'yes' | 'no';

  salary: number;
};

const data: DataSourceData<Developer> = () => {
  return fetch('https://infinite-table.com/.netlify/functions/json-server' + `/developers1k-sql?`)
    .then((r) => r.json())
    .then((data: Developer[]) => data);
};

const columns: InfiniteTablePropColumns<Developer> = {
  id: {
    field: 'id',
    type: 'number',
    defaultWidth: 70,
    defaultFilterable: false,
  },

  salary: {
    // we're intentionally using not binding this column to a `field`
    type: 'number',
    header: 'Salary',
    valueGetter: ({ data }) => data.salary,
  },

  firstName: {
    field: 'firstName',
  },
  stack: { field: 'stack' },
  currency: { field: 'currency' },
};

const domProps = {
  style: {
    height: '100%',
  },
};
export default () => {
  return (
    <>
      <React.StrictMode>
        <DataSource<Developer>
          data={data}
          primaryKey="id"
          defaultFilterValue={[
            // if you want the salary column to be filtered by default
            // you need to pass a valueGetter to the filter value
            // if you don't need a default filter, when you start filtering by
            // the column, the filter value will use the valueGetter of the column
            {
              id: 'salary',
              valueGetter: ({ data }) => data.salary,
              filter: {
                operator: 'gt',
                value: '',
                type: 'number',
              },
            },
          ]}
          filterDelay={0}
          filterMode="local"
        >
          <InfiniteTable<Developer>
            domProps={domProps}
            columnDefaultWidth={150}
            columnMinWidth={50}
            columns={columns}
          />
        </DataSource>
      </React.StrictMode>
    </>

Customizing the Filter Icon for Columns

Columns can customize the filter icon by using the columns.renderFilterIcon property.

Custom filter icons for salary and name columns

The salary column will show a bolded label when filtered.

The firstName column will show a custom filter icon when filtered.

View Mode
Fork
import * as React from 'react';

import {
  DataSourceData,
  InfiniteTable,
  InfiniteTablePropColumns,
  DataSource,
} from '@infinite-table/infinite-react';

type Developer = {
  id: number;
  firstName: string;
  lastName: string;

  currency: string;
  preferredLanguage: string;
  stack: string;
  canDesign: 'yes' | 'no';

  salary: number;
};

const data: DataSourceData<Developer> = () => {
  return fetch('https://infinite-table.com/.netlify/functions/json-server' + `/developers1k-sql?`)
    .then((r) => r.json())
    .then((data: Developer[]) => data);
};

const columns: InfiniteTablePropColumns<Developer> = {
  id: {
    field: 'id',
    type: 'number',
    defaultWidth: 100,
  },
  salary: {
    field: 'salary',
    type: 'number',
    header: ({ filtered }) => {
      return filtered ? <b>Salary</b> : 'Salary';
    },

    renderFilterIcon: () => {
      return null;
    },
  },

  firstName: {
    field: 'firstName',
    renderFilterIcon: ({ filtered }) => {
      return filtered ? '🔥' : '';
    },
  },
  stack: { field: 'stack' },
  currency: { field: 'currency' },
};

const domProps = {
  style: {
    height: '100%',
  },
};
export default () => {
  return (
    <>
      <React.StrictMode>
        <DataSource<Developer>
          data={data}
          primaryKey="id"
          defaultFilterValue={[]}
          filterDelay={0}
          filterMode="local"
        >
          <InfiniteTable<Developer>
            domProps={domProps}
            columnDefaultWidth={150}
            columnMinWidth={50}
            columns={columns}
          />
        </DataSource>
      </React.StrictMode>
    </>

Client-side filtering

Learn how to use filtering in the browser.

Server-side filtering

Figure out how to use filtering with server-side integration.