HelpBot Assistant

How can I help you?

Virtualization in MultiSelect Dropdown

21 Feb 202624 minutes to read

MultiSelect Dropdown virtualization is a technique used to efficiently render extensive lists of items while minimizing the impact on performance. This method is particularly advantageous when dealing with large datasets because it ensures that only a fixed number of DOM (Document Object Model) elements are created. When scrolling through the list, existing DOM elements are reused to display relevant data instead of generating new elements for each item. This recycling process is managed internally.

During virtual scrolling, the data retrieved from the data source depends on the popup height and the calculation of the list item height. Enabling the enableVirtualization option in a MultiSelect Dropdown activates this virtualization technique.

When fetching data from the data source, the actionBegin event is triggered before data retrieval begins. Then, the actionComplete event is triggered once the data is successfully fetched.

Furthermore, Incremental Search is supported with virtualization in the MultiSelect component. When a key is typed, the focus is moved to the respective element in the open popup state. In the closed popup state, the popup opens, and focus is moved to the respective element in the popup list based on the typed key. The Incremental Search functionality is well-suited for scenarios involving remote data binding.

Binding local data

The MultiSelect can generate its list items through an array of complex data. For this, the appropriate columns should be mapped to the fields property. When using virtual scrolling, the list updates based on the scroll offset value, triggering a request to fetch more data from the server.

In the following example, id column and text column from complex data have been mapped to the value field and text field, respectively.

[Class-component]

import { MultiSelectComponent, Inject, VirtualScroll } from '@syncfusion/ej2-react-dropdowns';
import * as React from 'react';
import * as ReactDOM from 'react-dom';
export default class App extends React.Component {
    // define the array of string
    constructor(props) {
        super(props);
        this.records = Array.from({ length: 150 }, (_, i) => ({
            id: 'id' + (i + 1),
            text: `Item ${i + 1}`,
        }));
    }
    fields = { text: 'text', value: 'id' };

    render() {
        return (
            // specifies the tag for render the DropDownList component
            <MultiSelectComponent id="datas" dataSource={this.records} placeholder="e.g. Item 1" enableVirtualization={true} allowFiltering={false} fields={this.fields} popupHeight="200px" >
                <Inject services={[VirtualScroll]} />
            </MultiSelectComponent>);
    }
}
ReactDOM.render(<App />, document.getElementById('sample'));
import { MultiSelectComponent, Inject, VirtualScroll } from '@syncfusion/ej2-react-dropdowns';
import * as React from 'react';
import * as ReactDOM from 'react-dom';

export default class App extends React.Component<{}, {}> {
  // maps the appropriate column to fields property
  private fields: object = { text: 'text', value: 'id' };

   // define the array of string
   private records: { [key: string]: Object }[] = [];
   
   // define the array of string
   constructor(props) {
    super(props);
    this.records = Array.from({ length: 150 }, (_, i) => ({
        id: 'id' + (i + 1),
        text: `Item ${i + 1}`,
    }));
  }

  public render() {
    return (
      // specifies the tag for render the DropDownList component
      <MultiSelectComponent id="datas" dataSource={this.records} placeholder="e.g. Item 1" enableVirtualization={true} allowFiltering={false} fields={this.fields} popupHeight="200px" >
      <Inject services={[VirtualScroll]}/>
  </MultiSelectComponent>
    );
  }
}
ReactDOM.render(<App />, document.getElementById('sample'));

Binding remote data

The MultiSelect supports the retrieval of data from remote data services with the help of the DataManager component, triggering the actionBegin and actionComplete events, and then updating the list data. During virtual scrolling, additional data is retrieved from the server, triggering the actionBegin and actionComplete events at that time as well.

The following sample displays the OrderId from the Orders Data Service.

[Class-component]

import { MultiSelectComponent, Inject, VirtualScroll } from '@syncfusion/ej2-react-dropdowns';
import { DataManager, UrlAdaptor , Query } from '@syncfusion/ej2-data';
import * as React from 'react';
import * as ReactDOM from 'react-dom';
export default class App extends React.Component {
    customerData = new DataManager({
        url: 'https://services.syncfusion.com/react/production/api/VirtualDropdownData',
        adaptor: new UrlAdaptor,
        crossDomain: true
    });
    customerField = { text: 'OrderID', value: 'OrderID' };

    render() {
        return (
            // specifies the tag for render the DropDownList component
            <MultiSelectComponent id="datas" dataSource={this.customerData} placeholder="OrderID" enableVirtualization={true} allowFiltering={true} fields={this.customerField} popupHeight="200px" >
                <Inject services={[VirtualScroll]} />
            </MultiSelectComponent>);
    }
}
ReactDOM.render(<App />, document.getElementById('sample'));
import { MultiSelectComponent, Inject, VirtualScroll } from '@syncfusion/ej2-react-dropdowns';
import { DataManager, UrlAdaptor , Query } from '@syncfusion/ej2-data';

import * as React from 'react';
import * as ReactDOM from 'react-dom';

export default class App extends React.Component<{}, {}> {
  // maps the appropriate column to fields property
  private customerField: object = { text: 'OrderID', value: 'OrderID' };
   
  private customerData: DataManager = new DataManager({
    url: 'https://services.syncfusion.com/react/production/api/VirtualDropdownData',
    adaptor: new UrlAdaptor,
    crossDomain: true
  });

  public render() {
    return (
      // specifies the tag for render the DropDownList component
      <MultiSelectComponent id="datas" dataSource={this.customerData} placeholder="OrderID" enableVirtualization={true} allowFiltering={true} fields={this.customerField} popupHeight="200px" >
      <Inject services={[VirtualScroll]}/>
  </MultiSelectComponent>
    );
  }
}
ReactDOM.render(<App />, document.getElementById('sample'));

Customizing items count in virtualization

When the enableVirtualization property is enabled, the take property specified in the Query parameter at initialization or during the actionBegin event determines the number of items loaded. The component internally calculates the optimal item count based on the popup height. If the specified take value is less than the minimum number of items that fit within the popup, the component uses the calculated minimum value instead.

The following sample shows the example for Customizing items count in virtualization.

[Class-component]

import { MultiSelectComponent, Inject, VirtualScroll } from '@syncfusion/ej2-react-dropdowns';
import { Query } from '@syncfusion/ej2-data';

import * as React from 'react';
import * as ReactDOM from 'react-dom';
export default class App extends React.Component {
    // define the array of string
    constructor(props) {
        super(props);
        this.records = Array.from({ length: 150 }, (_, i) => ({
            id: 'id' + (i + 1),
            text: `Item ${i + 1}`,
        }));
    }
    fields = { text: 'text', value: 'id' };
    // bind the Query instance to query property
    query = new Query().take(40);

    Begin(args) {
        args.Query = new Query().take(45);
      }

    render() {
        return (
            // specifies the tag for render the DropDownList component
            <MultiSelectComponent id="datas" dataSource={this.records} placeholder="e.g. Item 1" enableVirtualization={true} query={this.query}  allowFiltering={false} actionBegin={this.Begin} fields={this.fields} popupHeight="200px" >
                <Inject services={[VirtualScroll]} />
            </MultiSelectComponent>);
    }
}
ReactDOM.render(<App />, document.getElementById('sample'));
import { MultiSelectComponent, Inject, VirtualScroll } from '@syncfusion/ej2-react-dropdowns';
import { Query } from '@syncfusion/ej2-data';

import * as React from 'react';
import * as ReactDOM from 'react-dom';

export default class App extends React.Component<{}, {}> {
  // maps the appropriate column to fields property
  private fields: object = { text: 'text', value: 'id' };

   // define the array of string
   private records: { [key: string]: Object }[] = [];
   private query: Query = new Query().take(40);

    public Begin(e: any): void {
      e.query = new Query().take(45);
    }
   // define the array of string
   constructor(props) {
    super(props);
    this.records = Array.from({ length: 150 }, (_, i) => ({
        id: 'id' + (i + 1),
        text: `Item ${i + 1}`,
    }));
  }

  public render() {
    return (
      // specifies the tag for render the DropDownList component
      <MultiSelectComponent id="datas" dataSource={this.records} placeholder="e.g. Item 1" query={this.query} actionBegin={this.Begin} enableVirtualization={true} allowFiltering={false} fields={this.fields} popupHeight="200px" >
      <Inject services={[VirtualScroll]}/>
  </MultiSelectComponent>
    );
  }
}
ReactDOM.render(<App />, document.getElementById('sample'));

Grouping with virtualization

The MultiSelect component combines grouping with virtualization to efficiently organize and render large categorized datasets. Organize items into groups using the groupBy field from your data source. Virtual scrolling works seamlessly with grouped data for local sources. When using remote data, the component retrieves all data initially for grouping purposes, then applies virtualization to the grouped results, delivering optimal performance with organized content.

The following sample shows the example for Grouping with Virtualization.

[Class-component]

import { MultiSelectComponent, Inject, VirtualScroll } from '@syncfusion/ej2-react-dropdowns';
import * as React from 'react';
import * as ReactDOM from 'react-dom';
export default class App extends React.Component {
    // define the array of string
    records = [];
    constructor(props) {
        super(props);
        for (let i = 1; i <= 150; i++) {
            let item = {};
            item.id = 'id' + i;
            item.text = `Item ${i}`;
            // Generate a random number between 1 and 4 to determine the group
            const randomGroup = Math.floor(Math.random() * 4) + 1;
            switch (randomGroup) {
                case 1:
                    item.group = 'Group A';
                    break;
                case 2:
                    item.group = 'Group B';
                    break;
                case 3:
                    item.group = 'Group C';
                    break;
                case 4:
                    item.group = 'Group D';
                    break;
                default:
                    break;
            }
            this.records.push(item);
        }
    }
    fields = { groupBy: 'group', text: 'text', value: 'id' };

    render() {
        return (
            // specifies the tag for render the DropDownList component
            <MultiSelectComponent id="datas" dataSource={this.records} placeholder="e.g. Item 1" enableVirtualization={true} allowFiltering={true} fields={this.fields} popupHeight="200px" >
                <Inject services={[VirtualScroll]} />
            </MultiSelectComponent>);
    }
}
ReactDOM.render(<App />, document.getElementById('sample'));
import { MultiSelectComponent, Inject, VirtualScroll } from '@syncfusion/ej2-react-dropdowns';
import * as React from 'react';
import * as ReactDOM from 'react-dom';

export default class App extends React.Component<{}, {}> {
  // maps the appropriate column to fields property
  private fields: object = {groupBy: 'group', text: 'text', value: 'id' };

   // define the array of string
   private records: { [key: string]: Object }[] = [];
   
   // define the array of string
  constructor(props) {
    super(props);
    for (let i = 1; i <= 150; i++) {
      const item: { id: string, text: string, group: string } = {
        id: 'id' + i,
        text: `Item ${i}`,
        group: ''
      };

      // Generate a random number between 1 and 4 to determine the group
      const randomGroup = Math.floor(Math.random() * 4) + 1;
      switch (randomGroup) {
        case 1:
          item.group = 'Group A';
          break;
        case 2:
          item.group = 'Group B';
          break;
        case 3:
          item.group = 'Group C';
          break;
        case 4:
          item.group = 'Group D';
          break;
        default:
          break;
      }
      this.records.push(item);
    }
  }

  public render() {
    return (
      // specifies the tag for render the DropDownList component
      <MultiSelectComponent id="datas" dataSource={this.records} placeholder="e.g. Item 1" enableVirtualization={true} allowFiltering={true} fields={this.fields} popupHeight="200px" >
      <Inject services={[VirtualScroll]}/>
  </MultiSelectComponent>
    );
  }
}
ReactDOM.render(<App />, document.getElementById('sample'));

Filtering with virtualization

The MultiSelect component supports filtering with virtualization, combining search capabilities with efficient rendering. When allowFiltering is enabled, typing characters filters the data in real time. The component sends filter requests to the server using the complete data source, triggering the action event before the request and the action complete event after data retrieval. Initial data loads when the popup opens. The popup closes after filter selection or when no matches are found.

The following sample shows the example for Filtering with Virtualization.

[Class-component]

import { MultiSelectComponent, Inject, VirtualScroll } from '@syncfusion/ej2-react-dropdowns';
import * as React from 'react';
import * as ReactDOM from 'react-dom';
export default class App extends React.Component {
    // define the array of string
    constructor(props) {
        super(props);
        this.records = Array.from({ length: 150 }, (_, i) => ({
            id: 'id' + (i + 1),
            text: `Item ${i + 1}`,
        }));
    }
    fields = { text: 'text', value: 'id' };

    render() {
        return (
            // specifies the tag for render the DropDownList component
            <MultiSelectComponent id="datas" dataSource={this.records} placeholder="e.g. Item 1" enableVirtualization={true} allowFiltering={true} fields={this.fields} popupHeight="200px" >
                <Inject services={[VirtualScroll]} />
            </MultiSelectComponent>);
    }
}
ReactDOM.render(<App />, document.getElementById('sample'));
import { MultiSelectComponent, Inject, VirtualScroll } from '@syncfusion/ej2-react-dropdowns';
import * as React from 'react';
import * as ReactDOM from 'react-dom';

export default class App extends React.Component<{}, {}> {
  // maps the appropriate column to fields property
  private fields: object = { text: 'text', value: 'id' };

   // define the array of string
   private records: { [key: string]: Object }[] = [];
   
   // define the array of string
   constructor(props) {
    super(props);
    this.records = Array.from({ length: 150 }, (_, i) => ({
        id: 'id' + (i + 1),
        text: `Item ${i + 1}`,
    }));
  }

  public render() {
    return (
      // specifies the tag for render the DropDownList component
      <MultiSelectComponent id="datas" dataSource={this.records} placeholder="e.g. Item 1" enableVirtualization={true} allowFiltering={true} fields={this.fields} popupHeight="200px" >
      <Inject services={[VirtualScroll]}/>
  </MultiSelectComponent>
    );
  }
}
ReactDOM.render(<App />, document.getElementById('sample'));

Checkbox with virtualization

The MultiSelect component supports checkbox selection with virtualization, allowing users to select multiple items efficiently. When the mode property is set to CheckBox, checkboxes appear alongside each item in the virtualized list. The component’s value property updates automatically as items are selected or deselected through their checkboxes.

The following sample shows the example for checkbox with Virtualization.

[Class-component]

import { MultiSelectComponent, Inject, VirtualScroll, CheckBoxSelection } from '@syncfusion/ej2-react-dropdowns';
import * as React from 'react';
import * as ReactDOM from 'react-dom';
export default class App extends React.Component {
    // define the array of string
    constructor(props) {
        super(props);
        this.records = Array.from({ length: 150 }, (_, i) => ({
            id: 'id' + (i + 1),
            text: `Item ${i + 1}`,
        }));
    }
    fields = { text: 'text', value: 'id' };

    render() {
        return (
            // specifies the tag for render the DropDownList component
            <MultiSelectComponent id="datas" dataSource={this.records} mode="CheckBox" placeholder="e.g. Item 1" enableVirtualization={true} allowFiltering={false} fields={this.fields} popupHeight="200px" >
                <Inject services={[VirtualScroll, CheckBoxSelection]} />
            </MultiSelectComponent>);
    }
}
ReactDOM.render(<App />, document.getElementById('sample'));
import { MultiSelectComponent, Inject, VirtualScroll, CheckBoxSelection } from '@syncfusion/ej2-react-dropdowns';
import * as React from 'react';
import * as ReactDOM from 'react-dom';

export default class App extends React.Component<{}, {}> {
  // maps the appropriate column to fields property
  private fields: object = { text: 'text', value: 'id' };

   // define the array of string
   private records: { [key: string]: Object }[] = [];
   
   // define the array of string
   constructor(props) {
    super(props);
    this.records = Array.from({ length: 150 }, (_, i) => ({
        id: 'id' + (i + 1),
        text: `Item ${i + 1}`,
    }));
  }

  public render() {
    return (
      // specifies the tag for render the DropDownList component
      <MultiSelectComponent id="datas" dataSource={this.records} mode="CheckBox" placeholder="e.g. Item 1" enableVirtualization={true} allowFiltering={false} fields={this.fields} popupHeight="200px" >
        <Inject services={[VirtualScroll, CheckBoxSelection]}/>
      </MultiSelectComponent>
    );
  }
}
ReactDOM.render(<App />, document.getElementById('sample'));

Custom value with virtualization

The MultiSelect component supports adding custom values with virtualization. When allowCustomValue is enabled, users can enter and add new options not present in the original dataset. Upon selecting a custom value, the component triggers the customValueSelection event and appends the custom value to the end of the complete list.

The following sample shows the example for custom value with Virtualization.

[Class-component]

import { MultiSelectComponent, Inject, VirtualScroll } from '@syncfusion/ej2-react-dropdowns';
import * as React from 'react';
import * as ReactDOM from 'react-dom';
export default class App extends React.Component {
    // define the array of string
    constructor(props) {
        super(props);
        this.records = Array.from({ length: 150 }, (_, i) => ({
            id: 'id' + (i + 1),
            text: `Item ${i + 1}`,
        }));
    }
    fields = { text: 'text', value: 'id' };

    render() {
        return (
            // specifies the tag for render the DropDownList component
            <MultiSelectComponent id="datas" dataSource={this.records} placeholder="e.g. Item 1" enableVirtualization={true} allowFiltering={false} allowCustomValue={true} fields={this.fields} popupHeight="200px" >
                <Inject services={[VirtualScroll]} />
            </MultiSelectComponent>);
    }
}
ReactDOM.render(<App />, document.getElementById('sample'));
import { MultiSelectComponent, Inject, VirtualScroll } from '@syncfusion/ej2-react-dropdowns';
import * as React from 'react';
import * as ReactDOM from 'react-dom';

export default class App extends React.Component<{}, {}> {
  // maps the appropriate column to fields property
  private fields: object = { text: 'text', value: 'id' };

   // define the array of string
   private records: { [key: string]: Object }[] = [];
   
   // define the array of string
   constructor(props) {
    super(props);
    this.records = Array.from({ length: 150 }, (_, i) => ({
        id: 'id' + (i + 1),
        text: `Item ${i + 1}`,
    }));
  }

  public render() {
    return (
      // specifies the tag for render the DropDownList component
      <MultiSelectComponent id="datas" dataSource={this.records} placeholder="e.g. Item 1" enableVirtualization={true} allowCustomValue={true} allowFiltering={false} fields={this.fields} popupHeight="200px" >
      <Inject services={[VirtualScroll]}/>
  </MultiSelectComponent>
    );
  }
}
ReactDOM.render(<App />, document.getElementById('sample'));

Preselect values with virtualization

The MultiSelect component supports preselecting values with virtualization for both local and remote data sources. When preselected values are bound to the component, the corresponding data is fetched from the server and displayed immediately. For custom values, the component updates its value and appends the custom value to the end of the complete list.

The following sample shows the example for Preselect value with Virtualization.

[Class-component]

import { MultiSelectComponent, Inject, VirtualScroll } from '@syncfusion/ej2-react-dropdowns';
import * as React from 'react';
import * as ReactDOM from 'react-dom';
export default class App extends React.Component {
    // define the array of string
    constructor(props) {
        super(props);
        this.records = Array.from({ length: 150 }, (_, i) => ({
            id: 'id' + (i + 1),
            text: `Item ${i + 1}`,
        }));
    }
    fields = { text: 'text', value: 'id' };
    value = ['id1', 'id2', 'id3'];

    render() {
        return (
            // specifies the tag for render the DropDownList component
            <MultiSelectComponent id="datas" dataSource={this.records} value={this.value} placeholder="e.g. Item 1" enableVirtualization={true} allowFiltering={false} fields={this.fields} popupHeight="200px" >
                <Inject services={[VirtualScroll]} />
            </MultiSelectComponent>);
    }
}
ReactDOM.render(<App />, document.getElementById('sample'));
import { MultiSelectComponent, Inject, VirtualScroll } from '@syncfusion/ej2-react-dropdowns';
import * as React from 'react';
import * as ReactDOM from 'react-dom';

export default class App extends React.Component<{}, {}> {
  // maps the appropriate column to fields property
  private fields: object = { text: 'text', value: 'id' };

   // define the array of string
   private records: { [key: string]: Object }[] = [];

   private value: string[] = ["id1", "id2", "id3"];
   // define the array of string
   constructor(props) {
    super(props);
    this.records = Array.from({ length: 150 }, (_, i) => ({
        id: 'id' + (i + 1),
        text: `Item ${i + 1}`,
    }));
  }

  public render() {
    return (
      // specifies the tag for render the DropDownList component
      <MultiSelectComponent id="datas" dataSource={this.records} value={this.value} placeholder="e.g. Item 1" enableVirtualization={true} allowFiltering={false} fields={this.fields} popupHeight="200px" >
      <Inject services={[VirtualScroll]}/>
  </MultiSelectComponent>
    );
  }
}
ReactDOM.render(<App />, document.getElementById('sample'));