Select

Choose from predefined options, search them, or select multiple values.

Live preview
Interact with the component to see results here.
View code
HTML
<sy-select
  placeholder="Choose a team"
  mode="default"
  size="medium"
  maxtagcount="2"
  clearable
  name="team"
  id="sample"
  aria-label="Choose a team"
>
  <sy-option value="1" label="Design"></sy-option>
  <sy-option value="2" label="Development"></sy-option>
  <sy-option value="3" label="Marketing"></sy-option>
</sy-select>

Usage guide

#

Let users choose one or more predefined values. Use sy-option's value and label to distinguish stored values from displayed names.

  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.

Default selection

Use for one choice from a short list. Set the initial selection with defaultValue.

Search and select

mode="searchable" suits large lists that users narrow by name.

Multiple selection

Use mode="multiple" to select several candidates and maxTagCount to control visible tags.

Accept new values too

Use mode="tag" when users need to add values outside the list.

Examples

#

Multiple selection

Select multiple items and remove them individually with each tag's close button.

View code
HTML
<sy-select
  mode="multiple"
  clearable
  maxtagcount="2"
  defaultvalue="1,2,3"
>
  <sy-option value="1" label="Design"></sy-option>
  <sy-option value="2" label="Development"></sy-option>
  <sy-option value="3" label="Marketing"></sy-option>
</sy-select>

Add a new tag

Enter a value that is not in the list and press Enter to add it.

View code
HTML
<sy-select mode="tag" placeholder="Enter a team, then press Enter">
  <sy-option value="1" label="Design"></sy-option>
  <sy-option value="2" label="Development"></sy-option>
  <sy-option value="3" label="Marketing"></sy-option>
</sy-select>

Control selected values

Use setValue to select options and an empty array to deselect them. clearValue keeps the selection and clears only the search text.

View code
HTML
<sy-select
  mode="multiple"
  placeholder="Choose a team"
  id="sample"
>
  <sy-option value="1" label="Design"></sy-option>
  <sy-option value="2" label="Development"></sy-option>
  <sy-option value="3" label="Marketing"></sy-option>
</sy-select>
<div class="demo-actions"
  style="display:flex;flex-wrap:wrap;gap:8px;margin-top:16px"
>
  <sy-button id="action-0" size="small">Select Design and Development
  </sy-button>
  <sy-button id="action-1" size="small">Empty selection</sy-button>
  <sy-button id="action-2" size="small">Clear search</sy-button>
  <sy-button id="action-3" size="small">Close list</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 actionTarget0 = document.querySelector("#sample");
  document.querySelector('#action-0').addEventListener('click', async () => {
    const result = await actionTarget0.setValue([
      "1",
      "2"
    ]);
    document.querySelector('#demo-result').textContent = describeResult(result);
  });
  const actionTarget1 = document.querySelector("#sample");
  document.querySelector('#action-1').addEventListener('click', async () => {
    const result = await actionTarget1.setValue([]);
    document.querySelector('#demo-result').textContent = describeResult(result);
  });
  const actionTarget2 = document.querySelector("#sample");
  document.querySelector('#action-2').addEventListener('click', async () => {
    const result = await actionTarget2.clearValue();
    document.querySelector('#demo-result').textContent = describeResult(result);
  });
  const actionTarget3 = document.querySelector("#sample");
  document.querySelector('#action-3').addEventListener('click', async () => {
    const result = await actionTarget3.closeDropdown();
    document.querySelector('#demo-result').textContent = describeResult(result);
  });
</script>

Change a single selected value

Setting value in single-selection mode updates both the displayed label and the value submitted with the form.

View code
HTML
<sy-select
  name="team"
  clearable
  placeholder="Choose a team"
  id="sample"
>
  <sy-option value="1" label="Design"></sy-option>
  <sy-option value="2" label="Development"></sy-option>
  <sy-option value="3" label="Marketing"></sy-option>
</sy-select>
<div class="demo-actions"
  style="display:flex;flex-wrap:wrap;gap:8px;margin-top:16px"
>
  <sy-button id="action-0" size="small">Select Development</sy-button>
  <sy-button id="action-1" size="small">Empty selection</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 actionTarget0 = document.querySelector("#sample");
  document.querySelector('#action-0').addEventListener('click', async () => {
    const result = await actionTarget0.setValue("2");
    document.querySelector('#demo-result').textContent = describeResult(result);
  });
  const actionTarget1 = document.querySelector("#sample");
  document.querySelector('#action-1').addEventListener('click', async () => {
    const result = await actionTarget1.setValue([]);
    document.querySelector('#demo-result').textContent = describeResult(result);
  });
</script>

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-select
  placeholder="Choose a team"
  mode="default"
  size="medium"
  maxtagcount="2"
  clearable
  name="team"
  id="sample"
  aria-label="Choose a team"
>
  <sy-option value="1" label="Design"></sy-option>
  <sy-option value="2" label="Development"></sy-option>
  <sy-option value="3" label="Marketing"></sy-option>
</sy-select>
<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';
  for (const name of ["opened", "removed", "selected", "focused", "blured", "inputChanged",
      "cleared", "activated"
    ]) {
    document.addEventListener(name, event => {
      document.querySelector('#demo-events').textContent =
        `${event.target.localName} · ${name}: ${describeResult(event.detail)}`;
    });
  }
</script>

Display a read-only selection

readonly preserves the current selection and prevents changes. Unlike disabled, the value can still be submitted with a form.

View code
HTML
<sy-select
  placeholder="Choose a team"
  mode="default"
  size="medium"
  maxtagcount="2"
  clearable
  readonly
  name="team"
  id="sample"
  aria-label="Choose a team"
>
  <sy-option value="1" label="Design"></sy-option>
  <sy-option value="2" label="Development"></sy-option>
  <sy-option value="3" label="Marketing"></sy-option>
</sy-select>

<script type="module">
  const component1 = document.querySelector("#sample");
  Object.assign(component1, {
    "readonly": true
  });
  const component2 = document.querySelector("#sample");
  Object.assign(component2, {
    "selectedOptions": [{
      "value": "1",
      "label": "Design"
    }]
  });
</script>

No search results

Use an empty option to explain an empty list. Distinguish it from regular options the user can select.

View code
HTML
<sy-select placeholder="Open list">
  <sy-option empty label="No results found."></sy-option>
</sy-select>

Loading and empty lists

Use loading while waiting for data. When a response arrives, set loading to false and populate the options.

View code
HTML
<sy-select
  placeholder="Choose a team"
  mode="default"
  size="medium"
  maxtagcount="2"
  clearable
  loading
  name="team"
  id="sample"
  aria-label="Choose a team"
>
  <sy-option value="1" label="Design"></sy-option>
  <sy-option value="2" label="Development"></sy-option>
  <sy-option value="3" label="Marketing"></sy-option>
</sy-select>

<script type="module">
  const component1 = document.querySelector("#sample");
  Object.assign(component1, {
    "loading": true
  });
</script>

Availability of individual options

Mark unavailable items with disabled. Use showTooltip to reveal descriptions of long names.

View code
HTML
<sy-select placeholder="Choose a project">
  <sy-option
    value="design"
    label="Design"
    showtooltip
  ></sy-option>
  <sy-option
    value="archived"
    label="Completed project"
    disabled
  ></sy-option>
</sy-select>

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-select
    name="field"
    required
    placeholder="Choose a team"
    id="sample"
  >
    <sy-option value="1" label="Design"></sy-option>
    <sy-option value="2" label="Development"></sy-option>
    <sy-option value="3" label="Marketing"></sy-option><span slot="error">Choose
      a team.</span>
  </sy-select>
  <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 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-select
    name="field"
    required
    placeholder="Choose a team"
    id="sample"
  >
    <sy-option value="1" label="Design"></sy-option>
    <sy-option value="2" label="Development"></sy-option>
    <sy-option value="3" label="Marketing"></sy-option><span slot="error">Choose
      a team.</span>
  </sy-select>
  <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 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.getStatus();
    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-select
    nonativevalidity
    name="field"
    required
    placeholder="Choose a team"
    id="sample"
  >
    <sy-option value="1" label="Design"></sy-option>
    <sy-option value="2" label="Development"></sy-option>
    <sy-option value="3" label="Marketing"></sy-option><span slot="error">Choose
      a team.</span>
  </sy-select>
  <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';
  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-select>

Properties

Attribute / PropertyTypeDefaultDescription
booleanfalseShow a button to clear the current value.
booleanfalseBlock user interaction and keyboard focus.
booleanfalseDisplay the current value while preventing edits.
booleanfalseShow an empty-list state.
booleanfalseShow an error state.
booleanfalseHide the item from view.
booleanfalseIndicate that processing is in progress.
number0Number of selected tags shown at once. Set to 0 to show all.
string''Initially selected value.
string''Placeholder shown when the value is empty.
'small' | 'medium' | 'large''medium'Component size.
'default' | 'searchable' | 'multiple' | 'tag''default'Standard, searchable, multiple-selection, or tag-input mode.
booleanfalseFail form validation when no value is provided.
string''Field name used in FormData.
booleanfalseUse the app's error UI instead of the browser's built-in error display. checkValidity still performs validation.
booleanfalseUsed internally with Tree Select. Keep the default for a regular Select.
{ value: string; label?: string }[][]Array of selected option objects.

Pass objects and arrays as JavaScript properties.

Events

EventDetailDescription
CustomEvent<void>Emitted when the list opens.
CustomEvent<any>Emitted when a selected item is removed.
CustomEvent<any>Emitted when the user selects an item.
CustomEvent<void>Emitted when focus is received.
CustomEvent<void>Emitted when focus is lost.
CustomEvent<string>Emitted when the search input changes.
CustomEvent<void>Emitted when all selections are cleared.

Methods

MethodReturnsDescription
Promise<void>Select options matching the specified value string or array. An empty array clears the selection.
Promise<void>Clear only the search input, keeping the selected options.
Promise<void>Close the open options list.
Promise<void>Set a custom error state.
Promise<void>Clear the custom error state.
Promise<string>Return the current validation state.
Promise<boolean>Return true if the current constraints are satisfied, otherwise false.
Promise<boolean>Validate the current value and display errors to the user.

Slots

SlotDescription
Component body or child items.
Validation error message.

<sy-option>

Properties

Attribute / PropertyTypeDefaultDescription
booleanfalseBlock user interaction and keyboard focus.
string''Label describing the item.
booleanfalseDisplay the current value while preventing edits.
string''Current value or item identifier.
booleanfalseTooltip behavior for values or helper text.
booleanfalseIndicate whether the option is selected.
booleanfalseHide the item from view.
booleanfalseShow an empty-list state.
booleanfalseIndicate that processing is in progress.
booleanfalseIndicate whether the option was added through tag input.
booleanfalseIndicate whether the option is active during keyboard navigation.

Events

EventDetailDescription
CustomEvent<{ value: string; label: string }>Emitted when keyboard navigation changes the active item.

Slots

SlotDescription
Component body or child items.