Skip to content
AutoSuggesAutoSugges home
Start free
Menu
Appearance
Appearance: System.

Vehicle makes and models autocomplete in Expo

A make-then-model form in Expo: choosing a make narrows the model field to that make's models with the parent filter.

How do I build dependent make and model fields in Expo?

Use the useAutosuggesCombobox hook from the native entry twice: one field on the makes list, one on the models list. When a make is chosen, pass its value as the parent of the model field. The runtime then returns only that make's models, because the models list is a two-level hierarchy of make and model.

What is different about vehicle makes and models

Two lists, one id space
The make field uses the vehicle makes list. Its item.value is the make id, the same id the vehicle models list uses for each make, so it can be passed straight through as the parent.
The parent filter
A query with a parent returns only that parent’s children. The runtime accepts parent only for a hierarchical list; on a flat list it is an invalid request.
Resetting the model
When the make changes, the model field must start fresh. Remount it, or clear it and set the new parent, so a model from the old make is never kept.
Some makes have no models
The makes list holds every make, and a few have no models under them. The model field then shows an empty state, which is a normal answer and not an error.

Steps

  1. Create two publishable keys: one for the vehicle makes list and one for the vehicle models list.
  2. Create a client for each and call bootstrap() on both, so each list’s own minimum length and debounce apply.
  3. When a make is selected, pass its value as the parent of the model field.
  4. Clear the model whenever the make changes, and disable the model field until a make is chosen.

Code

import { FlatList, Pressable, Text, TextInput } from 'react-native';
import { createAutosuggesClient } from '@autosugges/client';
import {
  getInputAccessibilityProps,
  getListAccessibilityProps,
  getOptionAccessibilityProps,
  handleNativeKeyPress,
  useAutosuggesCombobox,
} from '@autosugges/client/native';
import type { ComboboxController } from '@autosugges/client';

const config = { baseUrl: 'YOUR_RUNTIME_BASE_URL' };
const makes = createAutosuggesClient({ ...config, publishableKey: 'YOUR_MAKES_PUBLISHABLE_KEY' });
const models = createAutosuggesClient({ ...config, publishableKey: 'YOUR_MODELS_PUBLISHABLE_KEY' });
void makes.bootstrap().catch(() => {});
void models.bootstrap().catch(() => {});

function Picker({ combobox, label }: { combobox: ComboboxController; label: string }) {
  const { state } = combobox;
  return (
    <>
      <TextInput
        {...getInputAccessibilityProps(state)}
        accessibilityLabel={label}
        value={state.inputValue}
        onChangeText={combobox.setInputValue}
        onKeyPress={(event) => handleNativeKeyPress(combobox, event.nativeEvent.key)}
      />
      <FlatList
        {...getListAccessibilityProps()}
        data={state.items}
        keyExtractor={(item) => item.itemId}
        renderItem={({ item, index }) => (
          <Pressable
            {...getOptionAccessibilityProps(state, index)}
            accessible
            onPress={() => combobox.selectIndex(index)}
          >
            <Text>{item.displayValue}</Text>
          </Pressable>
        )}
      />
    </>
  );
}

export function VehicleFields({ onChange }: { onChange: (modelId: string) => void }) {
  const modelBox = useAutosuggesCombobox({
    source: models,
    onSelect: (item) => onChange(item.value),
  });
  const makeBox = useAutosuggesCombobox({
    source: makes,
    onSelect: (item) => {
      modelBox.setParentId(item.value); // narrow the model query to this make
      modelBox.setInputValue('');
    },
  });

  return (
    <>
      <Picker combobox={makeBox} label="Make" />
      <Picker combobox={modelBox} label="Model" />
    </>
  );
}

The publishable key is public by design and safe in client code. A secret key never belongs here.

Check that it works

  1. Pick a make, then type in the model field. Only that make’s models appear.
  2. Change the make. The model field is empty again and queries the new make.

Frequently asked questions

How does the model field know which make was chosen?
You pass the chosen make’s value as the parent of the model field. The query then carries a parent parameter and the runtime returns only the children of that make.
Why are there two keys?
A publishable key resolves to one list. The make field queries the makes list and the model field queries the models list, so each has its own key and its own query policy.
What if a make has no models?
The model field shows its empty state. That is a valid answer to the query, not a failure, and the component never presents an error as no results.
Can I pass a parent to any list?
No. The runtime accepts parent only for a hierarchical list. On a flat list such as currencies it returns an invalid request.
What is the minimum number of characters before a query runs?
Two by default. A list owner can change the minimum, but it can never be 1. The client reads the real value from bootstrap and sends no request below it, so do not hardcode it.