Autocomplete

Show suggestions as users type and let them quickly select a match.

Live preview
Interact with the component to see results here.
View code
HTML
<sy-autocomplete
  placeholder="Enter a fruit name"
  size="medium"
  trigger="focus"
  min="0"
  highlight-matches
  id="sample"
  aria-label="Search fruit"
></sy-autocomplete>

<script type="module">
  const component1 = document.querySelector("#sample");
  Object.assign(component1, {
    "source": [
      "Apple",
      "Apricot",
      "Banana",
      "Cherry",
      "Grape",
      "Orange",
      "Peach"
    ]
  });
</script>

Usage guide

#

Use for search or input with many candidates when users know the name. Select is simpler for a small fixed set of values.

  1. Write the error message in the error slot.

    Inside the component, add <span slot="error">Error message</span>. You can include links and other guidance as well as text. For inputs supporting noNativeValidity, enable it to show this guidance instead of a browser popup. Switch always displays errors below the component.

  2. Report business rule failures with setCustomError().

    For example, if the server rejects a duplicate value, update the error slot and call setCustomError(). This method does not take a message argument. A custom error also blocks form submission.

  3. Clear the error after resolving it and check whether submission is allowed.

    Use clearCustomError() to clear a business rule error. Required-field and format validation remain active. await checkValidity() returns true or false; reportValidity() validates and displays errors. Read valid named values through FormData on submission.

  1. Prepare suggestions

    Pass a string array to source.

  2. Control search timing

    Set the minimum character count with min and the delay after typing in milliseconds with debounceTime.

  3. Distinguish input from selection

    changed reports input changes; selected reports candidate selection. Connect app logic to the appropriate event.

Examples

#

Debounce and minimum length

Suggestions appear after at least two characters have been entered and typing has stopped for 300ms.

View code
HTML
<sy-autocomplete
  placeholder="Enter at least two characters"
  debounce-time="300"
  min="2"
  trigger="input"
  id="sample"
  aria-label="Debounced search"
></sy-autocomplete>

<script type="module">
  const component1 = document.querySelector("#sample");
  Object.assign(component1, {
    "source": [
      "Apple",
      "Apricot",
      "Banana",
      "Cherry",
      "Grape",
      "Orange",
      "Peach"
    ]
  });
</script>

Disabled and read only

From top to bottom: default, disabled, and read only. Disabled fields cannot receive focus or input. Read-only fields keep their value but prevent editing.

View code
HTML
<div style="display:grid;gap:16px;width:100%">
  <sy-autocomplete value="Apple"></sy-autocomplete>
  <sy-autocomplete value="Apple" disabled></sy-autocomplete>
  <sy-autocomplete value="Apple" readonly></sy-autocomplete>
</div>

Events and payloads

Interact with the component and inspect the latest event name and detail. The code includes subscriptions to events emitted by this component and its children.

View code
HTML
<sy-autocomplete
  placeholder="Enter a fruit name"
  size="medium"
  trigger="focus"
  min="0"
  highlight-matches
  id="sample"
  aria-label="Search fruit"
></sy-autocomplete>
<output id="demo-events" aria-live="polite">Interact with the component to see
  its latest event and payload.</output>

<script type="module">
  const describeResult = value => JSON.stringify(value, (_key, item) => {
    if (item instanceof File) return {
      name: item.name,
      size: item.size,
      type: item.type
    };
    if (item instanceof Element) return `<${item.localName}>`;
    return item;
  }) ?? 'Done';
  const component1 = document.querySelector("#sample");
  Object.assign(component1, {
    "source": [
      "Apple",
      "Apricot",
      "Banana",
      "Cherry",
      "Grape",
      "Orange",
      "Peach"
    ]
  });
  for (const name of ["changed", "selected", "activeChanged"]) {
    document.addEventListener(name, event => {
      document.querySelector('#demo-events').textContent =
        `${event.target.localName} · ${name}: ${describeResult(event.detail)}`;
    });
  }
</script>

Case-sensitive search

Match the capitalization of the fruit names, such as Apple, as you type.

View code
HTML
<sy-autocomplete
  placeholder="Enter a fruit name"
  size="medium"
  trigger="input"
  min="1"
  highlight-matches
  case-sensitive
  id="sample"
  aria-label="Search fruit"
></sy-autocomplete>

<script type="module">
  const component1 = document.querySelector("#sample");
  Object.assign(component1, {
    "source": [
      "Apple",
      "Apricot",
      "Banana",
      "Cherry",
      "Grape",
      "Orange",
      "Peach"
    ]
  });
  const component2 = document.querySelector("#sample");
  Object.assign(component2, {
    "caseSensitive": true,
    "trigger": "input",
    "min": 1
  });
</script>

Forms and validation

Submit an empty value to trigger required-field validation. Enter a value and submit to inspect the form data, then try enabling and clearing a custom error.

View code
HTML
<form style="display:grid;gap:16px">
  <sy-autocomplete
    name="field"
    required
    id="sample"
  ><span slot="error">Enter a fruit name.</span></sy-autocomplete>
  <div style="display:flex;flex-wrap:wrap;gap:8px">
    <sy-button type="submit" variant="primary">Submit</sy-button>
    <sy-button type="reset">Reset</sy-button>
  </div>
</form>
<div class="demo-actions"
  style="display:flex;flex-wrap:wrap;gap:8px;margin-top:16px"
>
  <sy-button id="action-0" size="small">Check validity</sy-button>
  <sy-button id="action-1" size="small">Show error</sy-button>
  <sy-button id="action-2" size="small">Clear error</sy-button>
</div>
<output id="demo-result" aria-live="polite"></output>

<script type="module">
  const describeResult = value => JSON.stringify(value, (_key, item) => {
    if (item instanceof File) return {
      name: item.name,
      size: item.size,
      type: item.type
    };
    if (item instanceof Element) return `<${item.localName}>`;
    return item;
  }) ?? 'Done';
  const component1 = document.querySelector("#sample");
  Object.assign(component1, {
    "source": [
      "Apple",
      "Apricot",
      "Banana",
      "Cherry",
      "Grape",
      "Orange",
      "Peach"
    ]
  });
  const actionTarget0 = document.querySelector("#sample");
  document.querySelector('#action-0').addEventListener('click', async () => {
    const result = await actionTarget0.checkValidity();
    document.querySelector('#demo-result').textContent = describeResult(result);
  });
  const actionTarget1 = document.querySelector("#sample");
  document.querySelector('#action-1').addEventListener('click', async () => {
    const result = await actionTarget1.setCustomError();
    document.querySelector('#demo-result').textContent = describeResult(result);
  });
  const actionTarget2 = document.querySelector("#sample");
  document.querySelector('#action-2').addEventListener('click', async () => {
    const result = await actionTarget2.clearCustomError();
    document.querySelector('#demo-result').textContent = describeResult(result);
  });
  document.querySelector('form').addEventListener('submit', event => {
    event.preventDefault();
    document.querySelector('#demo-result').textContent = describeResult([...new FormData(event
      .currentTarget)]);
  });
</script>

Read validation results and error states

Validate before submitting or display errors on screen. Check an empty required field, then fill it to see how the return value changes. Querying the state distinguishes custom errors from required-field errors.

View code
HTML
<form style="display:grid;gap:16px">
  <sy-autocomplete
    name="field"
    required
    id="sample"
  ><span slot="error">Enter a fruit name.</span></sy-autocomplete>
  <div style="display:flex;flex-wrap:wrap;gap:8px">
    <sy-button type="submit" variant="primary">Submit</sy-button>
    <sy-button type="reset">Reset</sy-button>
  </div>
</form>
<div class="demo-actions"
  style="display:flex;flex-wrap:wrap;gap:8px;margin-top:16px"
>
  <sy-button id="action-0" size="small">Check validity</sy-button>
  <sy-button id="action-1" size="small">Show error</sy-button>
  <sy-button id="action-2" size="small">Clear error</sy-button>
  <sy-button id="action-3" size="small">Validate with errors</sy-button>
  <sy-button id="action-4" size="small">Query validation state</sy-button>
</div>
<output id="demo-result" aria-live="polite"></output>

<script type="module">
  const describeResult = value => JSON.stringify(value, (_key, item) => {
    if (item instanceof File) return {
      name: item.name,
      size: item.size,
      type: item.type
    };
    if (item instanceof Element) return `<${item.localName}>`;
    return item;
  }) ?? 'Done';
  const component1 = document.querySelector("#sample");
  Object.assign(component1, {
    "source": [
      "Apple",
      "Apricot",
      "Banana",
      "Cherry",
      "Grape",
      "Orange",
      "Peach"
    ]
  });
  const actionTarget0 = document.querySelector("#sample");
  document.querySelector('#action-0').addEventListener('click', async () => {
    const result = await actionTarget0.checkValidity();
    document.querySelector('#demo-result').textContent = describeResult(result);
  });
  const actionTarget1 = document.querySelector("#sample");
  document.querySelector('#action-1').addEventListener('click', async () => {
    const result = await actionTarget1.setCustomError();
    document.querySelector('#demo-result').textContent = describeResult(result);
  });
  const actionTarget2 = document.querySelector("#sample");
  document.querySelector('#action-2').addEventListener('click', async () => {
    const result = await actionTarget2.clearCustomError();
    document.querySelector('#demo-result').textContent = describeResult(result);
  });
  const actionTarget3 = document.querySelector("#sample");
  document.querySelector('#action-3').addEventListener('click', async () => {
    const result = await actionTarget3.reportValidity();
    document.querySelector('#demo-result').textContent = describeResult(result);
  });
  const actionTarget4 = document.querySelector("#sample");
  document.querySelector('#action-4').addEventListener('click', async () => {
    const result = await actionTarget4.getValidStatus();
    document.querySelector('#demo-result').textContent = describeResult(result);
  });
  document.querySelector('form').addEventListener('submit', event => {
    event.preventDefault();
    document.querySelector('#demo-result').textContent = describeResult([...new FormData(event
      .currentTarget)]);
  });
</script>

Custom error messages

Display a custom error for server-side duplicate checks or business rules. Change the message below and submit to verify that it is blocked. After clearing the error, required-field constraints must still be satisfied.

View code
HTML
<label for="error-message">Error message to display</label><input
  id="error-message"
  value="The server did not accept this value. Please review it."
  type="text"
>
<form style="display:grid;gap:16px">
  <sy-autocomplete
    no-native-validity
    name="field"
    required
    id="sample"
  ><span slot="error">Enter a fruit name.</span></sy-autocomplete>
  <div style="display:flex;flex-wrap:wrap;gap:8px">
    <sy-button type="submit" variant="primary">Submit</sy-button>
    <sy-button type="reset">Reset</sy-button>
  </div>
</form>
<div class="demo-actions">
  <sy-button id="show-custom-error" variant="primary">Show error</sy-button>
  <sy-button id="clear-custom-error">Clear error</sy-button>
</div>
<output id="custom-error-result" aria-live="polite"></output>
<output id="demo-result" aria-live="polite"></output>

<script type="module">
  const describeResult = value => JSON.stringify(value, (_key, item) => {
    if (item instanceof File) return {
      name: item.name,
      size: item.size,
      type: item.type
    };
    if (item instanceof Element) return `<${item.localName}>`;
    return item;
  }) ?? 'Done';
  const component1 = document.querySelector("#sample");
  Object.assign(component1, {
    "source": [
      "Apple",
      "Apricot",
      "Banana",
      "Cherry",
      "Grape",
      "Orange",
      "Peach"
    ]
  });
  await (async function customErrorSetup(root) {
    const field = root.querySelector('#sample');
    const message = root.querySelector('[slot="error"]');
    const input = root.querySelector('#error-message');
    const output = root.querySelector('#custom-error-result');
    const update = async () => {
      message.textContent = input.value.trim() || _msg("m_4eea8cef81c6");
      await field.setCustomError();
      output.textContent = _msg("m_e00bd46236d3", [message.textContent]);
    };
    input.addEventListener('input', update);
    root.querySelector('#show-custom-error').addEventListener('click', update);
    root.querySelector('#clear-custom-error').addEventListener('click', async () => {
      await field.clearCustomError();
      output.textContent = await field.checkValidity() ? _msg("m_c454251167b2") : _msg(
        "m_5b8cb48627e5");
    });
    root.querySelector('form').addEventListener('invalid', () => {
      output.textContent = _msg("m_c702c7778166");
    }, true);
    root.querySelector('form').addEventListener('reset', () => {
      output.textContent = _msg("m_b90c2b4dc30f");
    });
    await update();
  })(document);
  document.querySelector('form').addEventListener('submit', event => {
    event.preventDefault();
    document.querySelector('#demo-result').textContent = describeResult([...new FormData(event
      .currentTarget)]);
  });
</script>

API reference

#

Hover over a name or focus it with the keyboard to see the code. On touch screens, tap the name.

<sy-autocomplete>

Properties

Attribute / PropertyTypeDefaultDescription
booleanfalseMatch suggestions case-sensitively.
number0Input debounce delay in milliseconds.
booleanfalseBlock user interaction and keyboard focus.
booleanfalseHighlight matching text in suggestions.
booleanfalseIndicate that processing is in progress.
number0Minimum character count before suggestions appear.
string''Field name used in FormData.
string''Placeholder shown when the value is empty.
booleanfalseDisplay the current value while preventing edits.
booleanfalseFail form validation when no value is provided.
'small' | 'medium' | 'large''medium'Component size.
'default' | 'warning' | 'error' | 'success''default'Visually represent default, success, warning, or error states.
string''Current value or item identifier.
string[][]Array of suggestion strings, passed as a JavaScript property.

Pass objects and arrays as JavaScript properties.

'focus' | 'input''focus'User action that opens the list or popup.
booleanfalseUse the app's error UI instead of the browser's built-in error display. checkValidity still performs validation.

Events

EventDetailDescription
CustomEvent<{ value: string; isValid: boolean; status: string }>Emitted when the value or selection state changes.
CustomEvent<{ value: string; isValid: boolean; status: string }>Emitted when the user selects an item.

Methods

MethodReturnsDescription
Promise<void>Move keyboard focus to the input or action element.
Promise<void>Remove focus from the input element.
Promise<boolean>Return true if the current constraints are satisfied, otherwise false.
Promise<boolean>Validate the current value and display errors to the user.
Promise<string>Return the current validation state.
Promise<void>Set a custom error state.
Promise<void>Clear the custom error state.

Slots

SlotDescription
Validation error message.

<sy-autocomplete-option>

Properties

Attribute / PropertyTypeDefaultDescription
string[][]Array of suggestion strings, passed as a JavaScript property.

Pass objects and arrays as JavaScript properties.

booleanfalseIndicate that processing is in progress.
number-1Index of the candidate being navigated with the keyboard, managed by Autocomplete.
string''Search query used to filter and highlight candidates.
booleanfalseMatch suggestions case-sensitively.
booleanfalseHighlight matching text in suggestions.

Events

EventDetailDescription
CustomEvent<number>Read the result from the event's detail.
CustomEvent<string>Emitted when the user selects an item.

Methods

MethodReturnsDescription
Promise<void>Initialize event bindings for suggestion options. Normally managed by Autocomplete.
Promise<void>Render the component again using its current properties. Normally, property changes or the parent component manage updates.