DevExtreme v24.1 is now available.

Explore our newest features/capabilities and share your thoughts with us.

Your search did not match any results.


Autocomplete is a text box that displays suggestions while a user types. This demo shows how to customize the Autocomplete component and handle value changes.

DevExtreme Accessibility Compliance
DevExtreme component libraries meet a variety of WCAG and Section 508 compliance standards. To assess this demo’s accessibility level, click the Run AXE® Validation button to launch the AXE® web accessibility evaluation tool.
All trademarks or registered trademarks are property of their respective owners. AXE® Terms of Use
The overall accessibility level of your application depends on the Autocomplete features used.
Backend API
<template> <div class="form"> <div class="dx-fieldset"> <div class="dx-fieldset-header">Default Mode</div> <div class="dx-field"> <div class="dx-field-label">First Name</div> <div class="dx-field-value"> <DxAutocomplete :data-source="names" v-model:value="firstName" placeholder="Type first name..." @value-changed="updateEmployeeInfo" /> </div> </div> </div> <div class="dx-fieldset"> <div class="dx-fieldset-header">With Clear Button</div> <div class="dx-field"> <div class="dx-field-label">Last Name</div> <div class="dx-field-value"> <DxAutocomplete :data-source="surnames" v-model:value="lastName" :show-clear-button="true" placeholder="Type last name..." @value-changed="updateEmployeeInfo" /> </div> </div> </div> <div class="dx-fieldset"> <div class="dx-fieldset-header">Disabled</div> <div class="dx-field"> <div class="dx-field-label">Position</div> <div class="dx-field-value"> <DxAutocomplete :data-source="positions" :value="position" :disabled="true" :input-attr="{ 'aria-label': 'Position' }" /> </div> </div> </div> <div class="dx-fieldset"> <div class="dx-fieldset-header">Custom Item Template and Data Source Usage</div> <div class="dx-field"> <div class="dx-field-label">State</div> <div class="dx-field-value"> <DxAutocomplete :data-source="states" v-model:value="state" value-expr="State_Long" placeholder="Type state name..." item-template="stateTemplate" @value-changed="updateEmployeeInfo" > <template #stateTemplate="{ data }"> <span>{{ data.State_Long }} ({{ data.State_Short }})</span> </template> </DxAutocomplete> </div> </div> </div> <div class="dx-fieldset"> <div class="dx-fieldset-header">Custom Store and Search Options</div> <div class="dx-field"> <div class="dx-field-label">Current Client</div> <div class="dx-field-value"> <DxAutocomplete :data-source="clientsStore" v-model:value="currentClient" :min-search-length="2" :search-timeout="500" placeholder="Type client name..." value-expr="Text" @value-changed="updateEmployeeInfo" /> </div> </div> </div> <div class="dx-fieldset"> <div class="dx-fieldset-header">Event Handling</div> <div class="employees-data"> Employee data: <span>{{ fullInfo }}</span> </div> </div> </div> </template> <script setup lang="ts"> import { ref } from 'vue'; import ODataStore from 'devextreme/data/odata/store'; import { DxAutocomplete } from 'devextreme-vue/autocomplete'; import CustomStore from 'devextreme/data/custom_store'; import 'whatwg-fetch'; import { names, surnames, positions } from './data.ts'; const statesStore = new ODataStore({ version: 2, url: '$select=Sate_ID,State_Long,State_Short', key: 'Sate_ID', keyType: 'Int32', }); const clientsCustomStore = new CustomStore({ key: 'Value', useDefaultSearch: true, load(loadOptions) { let params = '?'; [ 'skip', 'take', 'filter', ].forEach((option) => { if (option in loadOptions && isNotEmpty(loadOptions[option])) { params += `${option}=${JSON.stringify(loadOptions[option])}&`; } }); params = params.slice(0, -1); return fetch(`${params}`) .then((response) => response.json()) .then((data) => ({ data:, })) .catch(() => { throw new Error('Data Loading Error'); }); }, }); const firstName = ref(''); const lastName = ref(''); const position = ref(positions[0]); const state = ref(''); const currentClient = ref(''); const fullInfo = ref(''); const states = ref(statesStore); const clientsStore = ref(clientsCustomStore); function updateEmployeeInfo() { let employeeInfo = ''; employeeInfo += `${firstName.value || ''} ${lastName.value || ''}`.trim(); employeeInfo += (employeeInfo && position) ? `, ${position.value}` : position.value || ''; employeeInfo += (employeeInfo && state.value) ? `, ${state.value}` : state.value || ''; employeeInfo += (employeeInfo && currentClient.value) ? `, ${currentClient.value}` : currentClient.value || ''; fullInfo.value = employeeInfo; } function isNotEmpty(value) { return value !== undefined && value !== null && value !== ''; } </script> <style> .employees-data { padding-top: 16px; padding-bottom: 10px; } </style>
window.exports = window.exports || {}; window.config = { transpiler: 'plugin-babel', meta: { '*.vue': { loader: 'vue-loader', }, '*.ts': { loader: 'demo-ts-loader', }, '*.svg': { loader: 'svg-loader', }, 'devextreme/time_zone_utils.js': { 'esModule': true, }, 'devextreme/localization.js': { 'esModule': true, }, 'devextreme/viz/palette.js': { 'esModule': true, }, }, paths: { 'root:': '../../../../', 'npm:': '', }, map: { 'vue': 'npm:vue@3.2.47/dist/vue.esm-browser.js', 'vue-loader': 'npm:dx-systemjs-vue-browser@1.1.1/index.js', 'demo-ts-loader': 'root:utils/demo-ts-loader.js', 'svg-loader': 'root:utils/svg-loader.js', 'whatwg-fetch': 'npm:whatwg-fetch@2.0.4/fetch.js', 'mitt': 'npm:mitt/dist/mitt.umd.js', 'rrule': 'npm:rrule@2.6.4/dist/es5/rrule.js', 'luxon': 'npm:luxon@1.28.1/build/global/luxon.min.js', 'es6-object-assign': 'npm:es6-object-assign@1.1.0', 'devextreme': 'npm:devextreme@24.1.4/cjs', 'devextreme-vue': 'npm:devextreme-vue@24.1.4/cjs', 'jszip': 'npm:jszip@3.10.1/dist/jszip.min.js', 'devextreme-quill': 'npm:devextreme-quill@1.7.1/dist/dx-quill.min.js', 'devexpress-diagram': 'npm:devexpress-diagram@2.2.10/dist/dx-diagram.js', 'devexpress-gantt': 'npm:devexpress-gantt@4.1.56/dist/dx-gantt.js', '@devextreme/runtime': 'npm:@devextreme/runtime@3.0.13', 'inferno': 'npm:inferno@7.4.11/dist/inferno.min.js', 'inferno-compat': 'npm:inferno-compat/dist/inferno-compat.min.js', 'inferno-create-element': 'npm:inferno-create-element@7.4.11/dist/inferno-create-element.min.js', 'inferno-dom': 'npm:inferno-dom/dist/inferno-dom.min.js', 'inferno-hydrate': 'npm:inferno-hydrate@7.4.11/dist/inferno-hydrate.min.js', 'inferno-clone-vnode': 'npm:inferno-clone-vnode/dist/inferno-clone-vnode.min.js', 'inferno-create-class': 'npm:inferno-create-class/dist/inferno-create-class.min.js', 'inferno-extras': 'npm:inferno-extras/dist/inferno-extras.min.js', 'plugin-babel': 'npm:systemjs-plugin-babel@0.0.25/plugin-babel.js', 'systemjs-babel-build': 'npm:systemjs-plugin-babel@0.0.25/systemjs-babel-browser.js', // Prettier 'prettier/standalone': 'npm:prettier@2.8.8/standalone.js', 'prettier/parser-html': 'npm:prettier@2.8.8/parser-html.js', }, packages: { 'devextreme-vue': { main: 'index.js', }, 'devextreme': { defaultExtension: 'js', }, 'devextreme/events/utils': { main: 'index', }, 'devextreme/events': { main: 'index', }, 'es6-object-assign': { main: './index.js', defaultExtension: 'js', }, }, packageConfigPaths: [ 'npm:@devextreme/*/package.json', 'npm:@devextreme/runtime@3.0.13/inferno/package.json', ], babelOptions: { sourceMaps: false, stage0: true, }, }; System.config(window.config);
export const names = ['James', 'John', 'Robert', 'Michael', 'William', 'David', 'Richard', 'Charles', 'Joseph', 'Thomas', 'Christopher', 'Daniel', 'Paul', 'Mark', 'Donald', 'George', 'Kenneth', 'Steven', 'Edward', 'Brian', 'Ronald', 'Anthony', 'Kevin', 'Jason', 'Jeff', 'Mary', 'Patricia', 'Linda', 'Barbara', 'Elizabeth', 'Jennifer', 'Maria', 'Susan', 'Margaret', 'Dorothy', 'Lisa', 'Nancy', 'Karen', 'Betty', 'Helen', 'Sandra', 'Donna', 'Carol', 'Ruth', 'Sharon', 'Michelle', 'Laura', 'Sarah', 'Kimberly', 'Deborah']; export const surnames = ['Anderson', 'Smith', 'Johnson', 'Williams', 'Jones', 'Brown', 'Davis', 'Miller', 'Wilson', 'Moore', 'Taylor', 'Thomas', 'Jackson', 'White', 'Harris', 'Martin', 'Thompson', 'Garcia', 'Martinez', 'Robinson', 'Clark', 'Rodriguez', 'Lewis', 'Lee', 'Walker', 'Hall', 'Allen', 'Young', 'Hernandez', 'King', 'Wright', 'Lopez', 'Hill', 'Scott', 'Green', 'Adams', 'Baker', 'Gonzalez', 'Nelson', 'Carter', 'Mitchell', 'Perez', 'Roberts', 'Turner', 'Phillips', 'Campbell', 'Parker', 'Evans', 'Edwards', 'Collins', ]; export const positions = ['CEO', 'COO', 'CTO', 'CMO', 'HR Manager', 'IT Manager', 'Controller', 'Sales Manager', 'Support Manager'];
import { createApp } from 'vue'; import App from './App.vue'; createApp(App).mount('#app');
<!DOCTYPE html> <html lang="en"> <head> <title>DevExtreme Demo</title> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=5.0" /> <link rel="stylesheet" type="text/css" href="" /> <script type="module"> import * as vueCompilerSFC from ""; window.vueCompilerSFC = vueCompilerSFC; </script> <script src=""></script> <script src=""></script> <script src=""></script> <script type="text/javascript" src="config.js"></script> <script type="text/javascript"> System.import("./index.ts"); </script> </head> <body class="dx-viewport"> <div class="demo-container"> <div id="app"> </div> </div> </body> </html>

Bind Autocomplete to Data

Use one of the following properties to supply data to the component:

  • items[]
    Accepts a local data array.

  • dataSource
    Accepts a local data array, DataSource object, or DevExtreme data store. In this demo, every Autocomplete component is bound to a local array, except the following components:

    • "Custom Item Template and Data Source Usage"
      Uses an ODataStore.

    • "Custom Store and Search Options"
      Uses a CustomStore.

The value property stores the selected item. You can use the same property to select an item programmatically, as shown in the "Disabled" Autocomplete component.

If the data source contains objects, you should specify the valueExpr property. It accepts a data field name that uniquely identifies each data object. In this demo, valueExpr is specified only for the "Custom Item Template and Data Source Usage" and "Custom Store and Search Options" components because other components are bound to arrays of primitive values.

Configure Search Parameters

When a user types the first character, Autocomplete displays suggestions. To increase the number of characters that triggers suggestions, use the minSearchLength property. You can also specify the time interval Autocomplete should wait before it displays suggestions. Assign this time interval in milliseconds to the searchTimeout property. See the "Custom Store and Search Options" Autocomplete component for an example.

In most cases, the data field that supplies Autocomplete with suggestions is the same data field that is used to search for the entered text. If you use two different fields, assign the field that supplies Autocomplete with suggestions to the valueExpr property and the field used to search to the searchExpr property. Note that searchExpr also accepts arrays if you want to search multiple fields.

Handle Value Change

To handle value changes, implement the onValueChanged function. In this demo, we use onValueChanged to display the values of all Autocomplete components.

Appearance Customization

You can specify the following properties to customize the Autocomplete component's appearance:

  • placeholder
    The text that is displayed when Autocomplete is empty.

  • showClearButton
    Adds a Clear button that empties the Autocomplete value as shown in the "With Clear Button" Autocomplete component.

  • disabled
    Disables the component.

  • itemTemplate
    Customizes the appearance of the Autocomplete suggestions. See the "Custom Item Template and Data Source Usage" component for an example.