---
title: Using Polaris web components
description: Polaris web components are Shopify's UI toolkit for building interfaces that match the Shopify Checkout design system. This toolkit provides a set of custom HTML elements (web components) that you can use to create consistent, accessible, and performant user interfaces for the Checkout UI Extensions.
api_version: 2025-10
api_name: checkout-ui-extensions
source_url:
html: https://shopify.dev/docs/api/checkout-ui-extensions/latest/using-polaris-components
md: https://shopify.dev/docs/api/checkout-ui-extensions/latest/using-polaris-components.md
---
# Using Polaris web components
Polaris web components are Shopify's UI toolkit for building interfaces that match the Shopify Checkout design system. This toolkit provides a set of custom HTML elements (web components) that you can use to create consistent, accessible, and performant user interfaces for the Checkout UI Extensions.
***
## Styling
Polaris web components come with built-in styling that follows Shopify's design system. The components will automatically apply the correct styling based on the properties you set and the context in which they are used. For example, headings automatically display at progressively less prominent sizes based on how many levels deep they are nested inside of sections. All components inherit a merchant's brand settings and the CSS cannot be altered or overridden.
### Examples
* #### Example
##### JSX
```jsx
Content
```
## Custom layout
When you need to build custom layouts you can use `s-stack`, `s-grid` (coming soon) and `s-box`.
* `s-stack` and `s-grid` (coming soon) do not include spacing between children by default. To apply white space between children use the `gap` property
* When `s-stack` is `direction="inline"` it will automatically wrap children to a new line when space is limited.
* `s-grid` (coming soon) will allow children to overflow unless template rows/columns are properly set.
* Order is important for shorthand properties, e.g. border takes `size-keyword`, `color-keyword`, `style-keyword`
## Scale
Our components use a middle-out scale for multiple properties like `padding`, `size` and `gap`.
Our scale moves from the middle out:
* `small-300` is smaller than `small-100`
* `large-300` is bigger than `large-100`
* `small-100` and `large-100` have aliases of `small` and `large`
* `base` is the default value
### Examples
* #### Example
##### Default
```ts
export type Scale =
| 'small-300'
| 'small-200'
| 'small-100'
| 'small' // alias of small-100
| 'base'
| 'large' // alias of large-100
| 'large-100'
| 'large-200'
| 'large-300';
```
## Responsive values
Some properties accept responsive values, which enables you to change the value of the property depending on a parent inline size.
Syntax
The syntax for a responsive value generally follows the ternary operator syntax. For example, `@container (inline-size > 500px) large, small` means that the value will be `large` if the container is more than 500px wide, and `small` if the container is 500px or less. The syntax rules are:
1. Begin the value with `@container`
2. Optionally add a name to target a specific container
3. Use the `inline-size` keyword inside of parentheses to query the inline-size of the container. This is the condition that will be evaluated to determine which value to use.
4. Set the value if that condition is true
5. Set the value to be used if the condition is false.
For greater compatibility on older browsers, container queries must follow a mobile-first approach. The fallback value (when the condition is false) must always represent your smallest supported design, with the condition value (when the condition is true) providing styles for larger containers. For example, `` ensures that browsers that do not support container styles get a design that works in all container sizes.
Using s-query-container
When using responsive values, you must also place the `` component in the location you want to query the inline-size.
By default, the responsive value will query against the closest parent; to look up a specific parent, this component also accepts a `containername` attribute which adds a name to the container. Then add that name after `@container` in your responsive query to target it.
Values with reserved characters
Some values could contain reserved characters used in the responsive value syntax, such as `()` or `,`. To use these values, escape them by wrapping them in quotes.
Advanced patterns
The syntax is flexible enough to support advanced patterns such as compound conditions, and|or conditions, and nested conditions.
### Examples
* ####
##### HTML
```html
This padding will be "large-400" when the
container is more than 500px. Otherwise it will
be "small".
```
## Interactive elements
`s-button`, `s-link` and `s-clickable` (coming soon) render as anchor elements when they have a `href` and render as a button element when they have an `onClick` without a `href`. The HTML specification states that interactive elements cannot have interactive children.
`s-clickable` is an escape hatch for when `s-link` and `s-button` are not able to implement a specific design. You should always try to use `s-link` and `s-button` first.
Inteactive components with `target="auto"` automatically use `_self` for internal links and `_blank` for external URLs. This behavior ensures a consistent navigation experience for users without requiring developers to manually set the correct target for each link.
## Variant tone and color
The `tone` is used to apply a group of color design tokens to the component such as `critical`, `success` or `info`.
The `color` adjusts the intensity of the `tone` making it more `subdued` or `strong`.
The `variant` is used to change how the component is rendered to match the design language this is different for each component.
### Examples
* #### Example
##### Default
```jsx
Primary Critical Button
Success Strong Badge
```
## Using with Preact
For UI Extensions, Shopify provides Preact as the framework of choice. Using Polaris web components with Preact is very similar to using them with React.
### Examples
* #### Example
##### JSX
```jsx
export function ProductExtension() {
return (
Enable special pricing
console.log('Checkbox toggled')} />
);
}
```
## Properties vs attributes
Polaris web components follow the same property and attribute patterns as standard HTML elements. Understanding this distinction is important for using the components effectively.
Key concepts
1. **Attributes** are HTML attributes that appear in the HTML markup.
2. **Properties** are JavaScript object properties accessed directly on the DOM element.
3. Most attributes in Polaris web components are reflected as properties, with a few exceptions like `value` and `checked` which follow HTML's standard behavior.
How JSX properties are applied
When using Polaris web components in JSX, the framework determines how to apply your props based on whether the element has a matching property name.
If the element has a property with the exact same name as your prop, the value is set as a property. Otherwise, it's applied as an attribute. Here's how this works in pseudocode:
Examples
For Polaris web components, you can generally just use the property names as documented, and everything will work as expected.
### Examples
* ####
## Handling events
Handling events in UI extensions are the same as you would handle them in a web app. You can use the `addEventListener` method to listen for events on the components or use the `on[event]` property to listen for events from the components.
When using Preact, event handlers can be registered by passing props beginning with `on`, and the event handler name is case-insensitive. For example, the JSX `` registers `fn` as a "click" event listener on the button.
### Examples
* #### Handling events
##### JSX
```jsx
export default function HandlingEvents() {
const handleClick = () => {
console.log('s-button clicked');
};
return Click me;
}
// or
export default function HandlingEvents() {
const handleClick = () => {
console.log('s-button clicked');
};
const button = document.createElement('s-button');
button.addEventListener('click', handleClick);
document.body.appendChild(button);
}
```
## Slots
Slots allow you to insert custom content into specific areas of Polaris web components. Use the `slot` attribute to specify where your content should appear within a component.
Key points:
* Named slots (e.g., `slot="title"`) place content in designated areas
* Multiple elements can share the same slot name
* Elements without a slot attribute go into the default (unnamed) slot
### Examples
* #### Examples
##### Banner
```jsx
The order has been created successfully.
View order
Download invoice
```
## Using Forms
The [Form](https://shopify.dev/docs/api/checkout-ui-extensions/latest/components/forms/form) component provides a way to manage form state and submit data to your app's backend or directly to Shopify using Direct API access.
When the form is submitted or reset the relevant callback in the form component will get triggered.
Using this, you can control what defines a component to be dirty by utilizing the input's defaultValue property.
Rules:
* When the defaultValue is set, the component will be considered dirty if the value of the input is different from the defaultValue. You may update the defaultValue when the form is submitted to reset the dirty state of the form.
* When the defaultValue is not set, the component will be considered dirty if the value of the input is different from the initial value or from the last dynamic update to the input's value that wasn't triggered by user input.
Note: In order to trigger the dirty state, each input must have a name attribute.
### Examples
* #### Trigger the Form's dirty state
##### Using \`defaultValue\`
```tsx
import { render } from 'preact';
import { useState } from 'preact/hooks';
export default function extension() {
render(, document.body);
}
const defaultValues = {
text: 'default value',
number: 50,
};
function Extension() {
const [textValue, setTextValue] = useState('');
const [numberValue, setNumberValue] = useState('');
return (
console.log('submit', {textValue, numberValue})}>
setTextValue(e.target.value)}
/>
setNumberValue(e.target.value)}
/>
);
}
```
##### Using implicit default
```tsx
import { render } from 'preact';
import { useState } from 'preact/hooks';
export default function extension() {
render(, document.body);
}
async function Extension() {
const data = await fetch('/data.json');
const {text, number} = await data.json();
return ;
}
function App({text, number}) {
// The initial values set in the form fields will be the default values
const [textValue, setTextValue] = useState(text);
const [numberValue, setNumberValue] = useState(number);
return (
console.log('submit', {textValue, numberValue})}>
setTextValue(e.target.value)}
/>
setNumberValue(e.target.value)}
/>
);
}
```
## Accessibility
Polaris web components are built with accessibility in mind. They:
* Use semantic HTML under the hood
* Support keyboard navigation
* Include proper ARIA attributes
* Manage focus appropriately
* Provide appropriate color contrast
* Log warnings when component properties are missing and required for accessibility
To ensure your application remains accessible, follow these best practices:
1. Always use the `label` and `error` properties for form elements
2. Use appropriate heading levels with `s-heading` or the `heading` property
3. Ensure sufficient color contrast
4. Test keyboard navigation
5. Use `labelAccessibilityVisibility` to hide labels and keep them visible to assistive technologies
6. Use `accessibilityRole` to specify the `aria-role` of the component
### Examples
* #### Example
##### JSX
```jsx
{/* Good - provides a label */}
{/* Bad - missing a label */}
```
## Troubleshooting
Common issues and debugging tips for using Polaris web components.
Common issues
1. **Properties not updating**: Ensure you're using the property name as documented, not a different casing or naming convention.
2. **Event handlers not firing**: Check that you're using the correct event name (e.g., `onClick` for click events).
3. **Form values not being submitted**: Make sure your form elements have `name` attributes.
Debugging tips
1. Inspect the element in your browser's developer tools to see the current property and attribute values.
2. Use `console.log` to verify that event handlers are being called and receiving the expected event objects.
3. Check for any errors in the browser console that might indicate issues with your component usage.
### Examples
* ####