ontario-dropdown-list
Only use a dropdown (select) list if you cannot use other form components to capture the user’s information.
Usage Guidance
Please refer to the Ontario Design System for current documentation guidance.
Disabled and read-only states
This component intentionally does not provide readOnly or disabled props.
Disabling form controls can create accessibility and usability barriers, and often does not explain what the user needs to fix.
Instead:
- keep controls and submission actions available
- use validation and error messaging to clearly identify missing or invalid input
For implementation examples, see Error messaging.
Source: https://designsystem.ontario.ca/components/detail/buttons.html#disabled-buttons
Configuration
Once the component package has been installed (see Ontario Design System Component Library for installation instructions), the dropdown list component can be added directly into the project's code, and can be customized by updating the properties outlined here. Additional information on custom types for dropdown list properties are outlined here. Please see the examples below for how to configure the component.
Examples
Example of a dropdown list component with no elementId or isEmptyStartOption props passed. Note that by default, an ID for the elementId will be generated if none is provided. If no isEmptyStartOption prop is provided, it will default to false and display the first option in the options list, unless one of the options has a selected attribute set to true.
- HTML
- React
- Angular
<ontario-dropdown-list
name="streaming-service"
caption='{
"captionText": "Select a streaming service",
"captionType": "default"
}'
required
options='[{
"value": "netflix",
"label": "Netflix"
},
{
"value": "disney-plus",
"label": "Disney Plus"
},
{
"value": "crave",
"label": "Crave"
},
{
"value": "prime",
"label": "Prime Video"
}]'
>
</ontario-dropdown-list>
<OntarioDropdownList
name="streaming-service"
caption={{
captionText: 'Select a streaming service',
captionType: 'default',
}}
required
options={[
{ value: 'netflix', label: 'Netflix' },
{ value: 'disney-plus', label: 'Disney Plus' },
{ value: 'crave', label: 'Crave' },
{ value: 'prime', label: 'Prime Video' },
]}
/>
<ontario-dropdown-list
[name]="'streaming-service'"
[caption]="{ captionText: 'Select a streaming service', captionType: 'default' }"
required
[options]="[
{ value: 'netflix', label: 'Netflix' },
{ value: 'disney-plus', label: 'Disney Plus' },
{ value: 'crave', label: 'Crave' },
{ value: 'prime', label: 'Prime Video' }
]"
></ontario-dropdown-list>
In the following example, the selected option is set using the component's
value. Listen for the component input or change event to read the current
selection.
- HTML
- React
- Angular
<ontario-dropdown-list
id="dropdown-value-example"
name="streaming-service"
caption="Select a streaming service"
value="crave"
options='[
{ "value": "netflix", "label": "Netflix" },
{ "value": "disney-plus", "label": "Disney Plus" },
{ "value": "crave", "label": "Crave" }
]'
></ontario-dropdown-list>
<script>
document.getElementById('dropdown-value-example')?.addEventListener('change', (event) => {
console.log(event.target.value);
console.log(event.detail.value);
});
</script>
<OntarioDropdownList
name="streaming-service"
caption="Select a streaming service"
value="crave"
options={[
{ value: 'netflix', label: 'Netflix' },
{ value: 'disney-plus', label: 'Disney Plus' },
{ value: 'crave', label: 'Crave' },
]}
onInput={(event) => {
console.log((event.target as HTMLOntarioDropdownListElement).value);
}}
onChange={(event) => {
console.log((event.target as HTMLOntarioDropdownListElement).value);
console.log(event.detail.value);
}}
/>
<ontario-dropdown-list
[name]="'streaming-service'"
[caption]="'Select a streaming service'"
[value]="'crave'"
[options]="[
{ value: 'netflix', label: 'Netflix' },
{ value: 'disney-plus', label: 'Disney Plus' },
{ value: 'crave', label: 'Crave' }
]"
(change)="handleDropdownChange($event)"
></ontario-dropdown-list>
handleDropdownChange(event: Event) {
console.log((event.target as HTMLOntarioDropdownListElement).value);
console.log((event as CustomEvent<{ value: string }>).detail.value);
}
In the following example, all available props are passed through.
- HTML
- React
- Angular
<ontario-dropdown-list
name="provinces-territories"
is-empty-start-option="Select"
element-id="provinces-territories"
language="en"
hint-text="Select the province or territory you currently live in."
caption='{
"captionText": "Province/territory",
"captionType": "heading"
}'
required
options='[{
"value": "alberta",
"label": "Alberta"
},
{
"value": "british-columbia",
"label": "British Columbia"
},
{
"value": "manitoba",
"label": "Manitoba"
},
{
"value": "new-brunswick",
"label": "New Brunswick"
},
{
"value": "newfoundland-and-labrador",
"label": "Newfoundland and Labrador"
},
{
"value": "nova-scotia",
"label": "Nova Scotia"
},
{
"value": "ontario",
"label": "Ontario",
"selected": true
},
{
"value": "prince-edward-island",
"label": "Prince Edward Island"
},
{
"value": "quebec",
"label": "Quebec"
},
{
"value": "saskatchewan",
"label": "Saskatchewan"
},
{
"value": "northwest-territories",
"label": "Northwest Territories"
},
{
"value": "nunavut",
"label": "Nunavut"
},
{
"value": "yukon",
"label": "Yukon"
}]'
>
</ontario-dropdown-list>
<OntarioDropdownList
name="provinces-territories"
isEmptyStartOption="Select"
elementId="provinces-territories"
language="en"
hintText="Select the province or territory you currently live in."
caption={{
captionText: 'Province/territory',
captionType: 'heading',
}}
required
options={[
{ value: 'alberta', label: 'Alberta' },
{ value: 'british-columbia', label: 'British Columbia' },
{ value: 'manitoba', label: 'Manitoba' },
{ value: 'new-brunswick', label: 'New Brunswick' },
{ value: 'newfoundland-and-labrador', label: 'Newfoundland and Labrador' },
{ value: 'nova-scotia', label: 'Nova Scotia' },
{ value: 'ontario', label: 'Ontario', selected: true },
{ value: 'prince-edward-island', label: 'Prince Edward Island' },
{ value: 'quebec', label: 'Quebec' },
{ value: 'saskatchewan', label: 'Saskatchewan' },
{ value: 'northwest-territories', label: 'Northwest Territories' },
{ value: 'nunavut', label: 'Nunavut' },
{ value: 'yukon', label: 'Yukon' },
]}
/>
<ontario-dropdown-list
[name]="'provinces-territories'"
[isEmptyStartOption]="'Select'"
[elementId]="'provinces-territories'"
[language]="'en'"
[hintText]="'Select the province or territory you currently live in.'"
[caption]="{ captionText: 'Province/territory', captionType: 'heading' }"
required
[options]="[
{ value: 'alberta', label: 'Alberta' },
{ value: 'british-columbia', label: 'British Columbia' },
{ value: 'manitoba', label: 'Manitoba' },
{ value: 'new-brunswick', label: 'New Brunswick' },
{ value: 'newfoundland-and-labrador', label: 'Newfoundland and Labrador' },
{ value: 'nova-scotia', label: 'Nova Scotia' },
{ value: 'ontario', label: 'Ontario', selected: true },
{ value: 'prince-edward-island', label: 'Prince Edward Island' },
{ value: 'quebec', label: 'Quebec' },
{ value: 'saskatchewan', label: 'Saskatchewan' },
{ value: 'northwest-territories', label: 'Northwest Territories' },
{ value: 'nunavut', label: 'Nunavut' },
{ value: 'yukon', label: 'Yukon' }
]"
></ontario-dropdown-list>
Forms
The ontario-dropdown-list supports integration with native HTML <form> elements. This element integrates with the underlying browser form API and should work the same as an <select>.
The component keeps its value in sync as users interact with the internal
<select>. That same value is used for native form submission and for the
component input and change events. When isEmptyStartOption is enabled,
the start option uses an empty string value until a real option is selected. If
a provided value does not match any option, the component emits a warning and
falls back to the selected option, empty start option, or first available
option.
<form>
<!-- Add an ontario-dropdown-list -->
<ontario-dropdown-list
name="dropdown-1"
is-empty-start-option="Select"
element-id="dropdown-1"
caption="Dropdown"
required
options='[{
"value": "alberta",
"label": "Alberta"
},
{
"value": "ontario",
"label": "Ontario",
"selected": true
},
{
"value": "quebec",
"label": "Quebec"
},
{
"value": "nunavut",
"label": "Nunavut"
}]'
>
</ontario-dropdown-list>
<!-- Submit the form with a submit button -->
<ontario-button type="primary" html-type="submit">Submit</ontario-button>
</form>
Remember to set the name attribute as this is used to identify the field when submitting the form.
Event model
Each event emitted by the component uses the CustomEvent type to emit a custom event to help communicate what the component is doing. To access the data emitted by the component within the CustomEvent type use the CustomEvent.detail property.
For most integrations, prefer the component input and change events and
read the selected option from event.target.value. Those events also include
event.detail.value as a convenience. Use dropdownOnChange when you
specifically want the component's custom event payload.
Eg. To access the value of any change made to this component from the dropdownOnChange event, use the following code to wire up to listen for the the dropdownOnChange event.
<ontario-dropdown-list
name="dropdown-1"
id="dropdown-1"
is-empty-start-option="Select"
element-id="dropdown-1"
caption="Dropdown"
required
options='[{
"value": "ontario",
"label": "Ontario"
},
{
"value": "quebec",
"label": "Quebec"
}]'
></ontario-dropdown-list>
<script>
// Note: this waits for the page and components to load before
// locating the component.
window.onload = () => {
const dropdown1 = document.getElementById('dropdown-1');
dropdown1.addEventListener('dropdownOnChange', (event) => {
console.log('OnChange detail:', event.detail);
});
};
</script>
If an option is selected from dropdown-1, the value of event.detail is the object emitted along with the dropdownOnChange event.
{ id: "dropdown-1", value: "ontario" }
See the Events table to learn more about the available custom events from the component and what the type of CustomEvent.detail will be.
Native input and change events
The component uses a ShadowDOM to maintain encapsulation, however, this changes how the events flow from the inside of the component to the outside in the DOM.
The component handles the internal <select> events and re-emits input and
change events so consumers can listen on the component instead of the
internal control. The current selection is available through
event.target.value, and a convenience copy is also included in
event.detail.value.
When using libraries that listen for events, this process may not work with them and a workaround might be required depending on the framework or library in use.
Error messaging
Use validation and error messaging to help users understand what needs to be corrected.
Setting an error message
Set errorMessage when selection is missing or invalid, and keep the list enabled so users can correct their choice.
Static vs live validation
Use a static errorMessage when validation happens on submit (for example, after a form post or submit handler check).
Use live validation when you want real-time feedback as users interact (for example, on change or blur).
For more guidance, visit the Error messaging guidance page.
- HTML
- React
- Angular
<ontario-dropdown-list
id="service-selection"
name="service-selection"
caption="Select a service"
required
options='[
{ "label": "Health card", "value": "health-card" },
{ "label": "Driver licence", "value": "driver-licence" }
]'
></ontario-dropdown-list>
<script>
window.addEventListener('load', () => {
const dropdown = document.getElementById('service-selection');
dropdown.addEventListener('inputOnBlur', () => {
dropdown.errorMessage = dropdown.value ? '' : 'Select a service to continue.';
});
});
</script>
<OntarioDropdownList
elementId="service-selection"
name="service-selection"
caption="Select a service"
required
options={[
{ label: 'Health card', value: 'health-card' },
{ label: 'Driver licence', value: 'driver-licence' },
]}
errorMessage="Select a service to continue."
/>
<ontario-dropdown-list
[elementId]="'service-selection'"
[name]="'service-selection'"
[caption]="'Select a service'"
[required]="true"
[options]="serviceOptions"
[errorMessage]="'Select a service to continue.'"
></ontario-dropdown-list>
Live validation
Keep the control available and validate selection on interaction (for example, on blur or submit). When validation fails, set a contextual error message that explains how to fix the issue.
Custom property types
Caption
caption='{ "captionText": "Province/territory", "captionType": "heading" }'
| Property name | Type | Description |
|---|---|---|
captionText | string | The text to display as the caption (label) for the dropdown-list. This must be implemented. |
captionType | "default" | "large"| "heading" | The type of caption to render. This is optional - if no type is passed, the "default" type will render. |
Options
options='[ { "value": "netflix", "label": "Netflix" }, { "value": "disney-plus", "label": "Disney Plus" }, { "value":
"crave", "label": "Crave" }, { "value": "prime", "label": "Prime Video" } ]'
| Property name | Type | Description |
|---|---|---|
value | string | The dropdown option content value. Each value must be unique to the option. |
label | string | The text to display as the dropdown option label. |
selected | boolean | A boolean value to determine whether or not the dropdown list option is pre-selected. If no selected value is set, it will be set to false. |
Accessibility
- An
idattribute is necessary to allow the dropdown list to be associated with a label element. If none is provided through theelementIdprop, one will be generated. - A
nameattribute needs to be set to be submitted to the server when the form is submitted. - Do not add any customized styles to dropdown lists - the browser's default is the most accessible.
Technical Note: SSR (Server-Side Rendering) Considerations
The Ontario Dropdown List component supports server-side rendering, with a few considerations:
- Language prop: Language change events only fire in the browser after hydration. To ensure the correct language is rendered during SSR, pass the desired
languageexplicitly as a prop. - Dynamic ID generation: If
elementIdis not passed, a UUID is generated at runtime. To prevent hydration mismatches between server and client, explicitly pass a stableelementId. - Hint text and accessibility IDs: If using
ontario-hint-text, note that thearia-describedbyreference is resolved after hydration. Make sure this does not impact critical accessibility paths in your application. - Form participation: This component uses the Form-Associated Custom Elements API (
@AttachInternals) to participate in native form submission. During SSR, it renders with the expected select structure and can support straightforward form submission whenname,language, andelementIdare stable. Required-state messaging and the component's event model become available after hydration. - Hydrated-only behaviour: If your application depends on emitted events or hydrated validation behaviour, use the event examples below and verify the full submit flow in the consuming application.
- Framework guidance: Use the HTML
<form>example above for native submit or Next.js server-action style flows. For App Router setup details, follow the Next.js integration guide.
SSR-safe example:
<OntarioDropdownList
name="province"
elementId="province"
language="en"
caption={{ captionText: 'Select your province', captionType: 'heading' }}
options={[
{ label: 'Ontario', value: 'on' },
{ label: 'Quebec', value: 'qc' },
]}
isEmptyStartOption="Select a province"
/>
Overview
Ontario Dropdown List presents a selectable list of predefined options.
This component intentionally does not expose readOnly or disabled props.
To support accessible and understandable form completion:
- keep form fields and submission actions available
- use validation and error messaging to guide corrections
For component guidance, see:
- https://designsystem.ontario.ca/components/detail/dropdown-lists.html
- https://designsystem.ontario.ca/developer-docs/components/ontario-dropdown-list/
Disabled/read-only policy source:
Properties
| Property | Attribute | Description | Type | Default |
|---|---|---|---|---|
caption | caption | The text to display for the dropdown list label. | Caption | string | undefined |
customOnBlur | custom-on-blur | Used to add a custom function to the dropdown onBlur event. | ((event: Event) => void) | undefined | undefined |
customOnChange | custom-on-change | Used to add a custom function to the dropdown onChange event. | ((event: Event) => void) | undefined | undefined |
customOnFocus | custom-on-focus | Used to add a custom function to the dropdown onFocus event. | ((event: Event) => void) | undefined | undefined |
elementId | element-id | The ID for the dropdown list. If no ID is provided, one will be generated. | string | undefined | undefined |
errorMessage | error-message | Set this to display an error message | string | undefined | undefined |
hintExpander | hint-expander | Used to include the ontario-hint-expander component for the dropdown list component. This is passed in as an object with key-value pairs. This is optional. | HintExpander | string | undefined | undefined |
hintText | hint-text | Used to include the ontario-hint-text component for the dropdown list. This is optional. | Hint | string | undefined | undefined |
isEmptyStartOption | is-empty-start-option | This prop is used to determine whether or not the initial option displayed is empty. If set to true, it will render the default “select” text. If set to a string, it will render the string value. | boolean | string | undefined | false |
language | language | The language of the component. This is used for translations, and is by default set through event listeners checking for a language property from the header. If no language is passed, it will default to English. | "en" | "fr" | undefined | undefined |
name | name | The name for the dropdown list. The name value is used to reference form data after a form is submitted. | string | undefined |
options | options | The options for dropdown list. Each option will be passed in through an object in the options array. This can either be passed in as an object directly (if using react), or as a string in HTML. In the example below, the options are being passed in as a string and there are three dropdown options displayed. | DropdownOption[] | string | undefined |
required | required | This is used to determine whether the dropdown list is required or not. This prop also gets passed to the InputCaption utility to display either an optional or required flag in the label. If no prop is set, it will default to false (optional). | boolean | undefined | false |
value | value | The currently selected dropdown value. The component keeps the host value in sync as users interact with the dropdown. If value is provided, it takes precedence over any selected flags passed through options. | string | undefined | undefined |
Events
| Event | Description | Type |
|---|---|---|
dropdownOnBlur | Emitted when a keyboard input event occurs when a dropdown list has lost focus. | CustomEvent<InputInteractionEvent & { focused: boolean; }> |
dropdownOnChange | Emitted when a keyboard input or mouse event occurs when a dropdown list has been changed. | CustomEvent<{ id?: string | undefined; value?: string | undefined; }> |
dropdownOnFocus | Emitted when a keyboard input event occurs when a dropdown list has gained focus. | CustomEvent<InputInteractionEvent & { focused: boolean; }> |
inputErrorOccurred | Emitted when an error message is reported to the component. | CustomEvent<{ errorMessage: string; }> |
Dependencies
Depends on
Graph
Built with StencilJS