skip to content

Autocomplete

The Autocomplete component filters dropdown options based on what the user enters into the input.

Demo

<form class="eds-c-form" action="#" method="get">
{{> eds-c-autocomplete input=autocomplete.input}}
<input type="hidden" name="selected-item-id" value="" data-eds-c-autocomplete-hidden>
<div class="u-mt-24">
{{> eds-c-button template=button.template variant=button.variant type=button.type text=button.text}}
</div>
</form>

When to use this component

Use the Autocomplete component when users need to search and select from a large list of options.

If you need users to select specific options, for example institution names that must be in a particular format, the Autocomplete component may help to reduce errors.

Autocomplete can also help in cases where the list is too large for a standard select dropdown.

When not to use this component

Do not use the Autocomplete component when:

  • the list has fewer than 10 options - use a select dropdown or radio buttons instead
  • users need to see all options before selecting - use a select dropdown
  • the search requires complex filtering - consider a dedicated search page

How it works

You must use the Autocomplete component with the Form component.

The Autocomplete component enhances a standard text input with a dropdown of matching results.

As users enter what they are looking for in the input field, the component:

  1. Waits for a minimum number of characters (default: 2).
  2. Debounces input to avoid excessive API calls (default: 300ms).
  3. Fetches matching results from your API endpoint.
  4. Displays results in a dropdown below the input.
  5. Allows selection with mouse or keyboard.
  6. Dispatches a custom event with the selected item's data.

Installation

To install the components, go to the get started guide for developers.

Styles

Import the styles of the Autocomplete component into your scss file:

// enhanced.scss
@import '../path/to/@springernature/elements/components/eds-c-autocomplete';

Configuration

To view all the properties of this component, go to the schema.

Template

The component template accepts the following options (with Handlebars data):

Option Type Description Required
input Object Input field configuration (see eds-c-input schema). Yes
dataAttributes Object Custom data attributes for the wrapper element. No

JavaScript configuration

When initializing the component with JavaScript, you can pass these options:

Option Type Description Default
apiUrl String URL endpoint for fetching autocomplete results. null
apiQueryParam String Query parameter name for the search term. 'q'
apiMethod String HTTP method for API requests ('GET' or 'POST'). 'GET'
apiCredentials String Credentials mode: 'same-origin', 'include', or 'omit'. 'same-origin'
apiBodyFormat String POST body format: 'json' or 'urlencoded'. 'json'
apiHeaders Object Custom headers (e.g., CSRF tokens, authentication). {}
transformResponse Function Transform API response to expected format. null
minChars Number Minimum characters before showing results. 2
debounceDelay Number Delay in milliseconds before triggering search. 300
maxResults Number Maximum number of results to display in the dropdown. 20
maxQueryLength Number Maximum query string length accepted (reads from input's maxlength attribute first, then this option, then defaults to 100). 100
noResultMsg String Message displayed when search returns no results. 'No results found for'
loadingMsg String Message displayed while fetching results from API. 'Loading...'
errorMsg String Message displayed when API request fails. 'Unable to load results. Please try again.'

API response format

The component expects an array of objects.

The minimum required data are id and name. Additional properties like city and country are optional.

[
    {
        "id": "99",             // Unique identifier
        "bpid": "12345",           // Business partner ID (used as form value)
        "name": "University Name", // Display text
        "city": "London",          // City name
        "country": "GB"            // Country code
    }
]

If your API returns a different structure, use the transformResponse option to convert it:

// API returns: { results: [...], metadata: {...} }
new EdsCAutocomplete(wrapper, {
    apiUrl: '/api/search',
    transformResponse: (data) => {
        // Transform API response to expected format
        return data.results.map(item => ({
            id: item.id,
            bpid: item.id,
            name: item.institution_name,
            city: item.location.city,
            country: item.location.country_code
        }));
    }
});

States

The component displays different states:

  • Loading: Shows the text label "Loading..." while fetching results
  • No results: Shows "No results found" when query returns empty
  • Error: Shows "Unable to load results. Please try again." on API failure

Keyboard support

Key Action
ArrowDown Move focus to next result.
ArrowUp Move focus to previous result.
Enter Select the focused result.
Escape Close the dropdown.
Tab Close the dropdown and move to next element.

Accessibility

The component includes a persistent ARIA live region (aria-live="assertive") that announces state changes to screen reader users:

  • announces "Loading..." when fetching results
  • announces "No results found for [query]" when the search returns no matches
  • announces "Unable to load results. Please try again." on API errors

The live region is visually hidden but remains in the DOM, ensuring reliable announcements without interfering with the visual interface.

Progressive enhancement

Without JavaScript, the component functions as a standard form input that submits to your server. With JavaScript enabled, it provides enhanced autocomplete functionality while maintaining the same form structure.

Example: Form integration with hidden input

To capture the selected item's ID for form submission, add a data-autocomplete-hidden attribute to the hidden input field:


<form class="eds-c-form" action="/your-search-endpoint" method="get">
    <div data-eds-c-autocomplete>
        {{#with autocomplete}}
            {{> eds-c-autocomplete}}
        {{/with}}
    </div>
    <input type="hidden" name="selected-item-id" value="" data-eds-c-autocomplete-hidden>
    <div class="u-mt-24">
        {{#with button}}
            {{> eds-c-button}}
        {{/with}}
    </div>
</form>

Javascript

const autocompleteWrappers = document.querySelectorAll('[data-eds-c-autocomplete]');
const autocompleteInstances = [];
for (const wrapper of autocompleteWrappers) {
	const autocomplete = new EdsCAutocomplete(wrapper, { apiUrl: '/api/autocomplete' });
	// Listen for selection event
	wrapper.addEventListener('autocomplete:select', (e) => {
		console.log('autocomplete selected', e.detail);
		// Find hidden input in parent form (scoped to form)
		const form = wrapper.closest('form');
		if (form) {
			const hiddenInput = form.querySelector('[data-eds-c-autocomplete-hidden]');
			if (hiddenInput) {
				hiddenInput.value = e.detail.value;
			}
		}
	});
	autocompleteInstances.push(autocomplete);
}

Events

The component dispatches a custom event when a user selects an item:

wrapper.addEventListener('autocomplete:select', (event) => {
    console.log('Selected institution:', event.detail);
});

Security (CSRF) for logged in usage

If sensitive data exposure could be a concern, protect the endpoint with a CSRF (Cross‑Site Request Forgery) token. A synchronizer CSRF token is a secret value the server puts into pages (meta tag or hidden field) and the client must send back with requests so the server can verify they come from your site.

This helps to prevent malicious third‑party pages from making authenticated requests using the user’s cookies. If you do not implement this, attackers could cause unwanted authenticated requests. Even if search is read‑only, follow your app security policy. Include a token in the page and send it in request headers for POST/credentialed fetches.

Client side

Once the user is logged in, add a meta tag in your page template:

<meta name="csrf-token" content="">

The token must be per session per user.

Then, in your JavaScript, read the token and include it in requests:

const csrfToken = document.querySelector('meta[name="csrf-token"]')?.content || '';

const ac = new EdsCAutocomplete(wrapper, {
  apiUrl: '/api/autocomplete',
  apiMethod: 'POST',
  apiCredentials: 'same-origin',
  apiHeaders: {
    'Content-Type': 'application/json',
    'X-CSRF-Token': csrfToken
  }
});

Server side

Node.js/Express example:

router.post('/api/autocomplete', (req, res) => {
  const tokenFromClient = req.get('X-CSRF-Token') || req.body._csrf;
  const tokenInSession = req.session && req.session.csrfToken;

  if (!tokenFromClient || tokenFromClient !== tokenInSession) {
    return res.status(403).json({ error: 'Invalid CSRF token' });
  }

  // handle search...
});

Help improve this page

If you have a question, idea, or suggestion to improve this component or guidance, post in the #ask-elements Slack channel or the #ask-elements Teams channel.