Skip to main content

Using Polaris web components

Polaris web components are Shopify's UI toolkit for building interfaces that match the Shopify Admin 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 Shopify App Home and UI Extensions.


You can start using Polaris web components in any framework right away. Simply add the script tag to your app's HTML head.

Example

<script src="https://cdn.shopify.com/shopifycloud/app-bridge-ui-experimental.js"></script>

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.

In general, you should not need to use custom CSS to style Polaris web components. By building with only the components, you can ensure that your app will look and feel consistent with the Shopify Admin and its style will automatically adjust to match future updates.

Example

JSX

<s-box
padding="base"
background="subdued"
border="base"
borderRadius="base"
>
Content
</s-box>

To ensure the white space between components stays aligned with the Admin Design Language use components with opinionated spacing like s-page and s-section.

s-page component

The s-page adds the global padding, background color, and internal layout for the Admin. It includes opinionated spacing between s-section and s-banner.

Two sizes:

  • large which is full width used for tables and data visualization
  • base which is narrow and used for forms with a sidebar

Can add a sidebar with the aside slot

s-section component

The s-section component provides structured content areas with proper spacing. It provides default vertical white space to children

  • s-stack and s-grid are not necessary as children unless building a complex layout
  • Nesting s-section elements changes the heading level and the appearance of the section.
  • The top level s-section renders a card appearance

Example

<s-page>
<s-banner tone="critical" heading="Media upload failed">
File extension doesn't match the format of the file.
</s-banner>
<s-section>
<s-text-field label="Title"></s-text-field>
<s-text-area label="Description"> </s-text-area>
</s-section>

<s-section heading="Status" slot="aside">
<s-select>
<s-option value="active">Active</s-option>
<s-option value="draft">Draft</s-option>
</s-select>
</s-section>
</s-page>

When you need to build custom layouts you can use s-stack, s-grid and s-box. You should always try to use s-section and s-page components first to ensure your white space stays aligned with the Admin Design Language.

  • s-stack and s-grid do not include padding between children by default. To apply the default white space between children use padding="base"
  • Try to avoid adding vertical spacing with custom layouts. Use the s-section and s-page default white space instead.
  • When s-stack is display="inline" it will automatically wrap children to a new line when space is limited.
  • s-grid 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

Our components use a middle-out scale for mulitple 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

Example

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';

Some properties accept responsive values, which enables you to change the value of the property depending on a parent block size.

The syntax for a responsive value generally follows the ternary operator syntax. For example, @container (inline-size < 500px) small, large means that the value will be small if the container is less than 500px wide, and large if the container is 500px or wider. 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.

When using responsive values, you must also place the <s-query-container> 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 queryname attribute which adds a name to the container. Then add that name after @container in your responsive value to target it.

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.

The syntax is flexible enough to support advanced patterns such as compound conditions, and|or conditions, and nested conditions.

HTML

<s-box padding="@container (inline-size < 500px) small, large">Hello</s-box>

Anchor to interactive-elementsInteractive elements

s-clickable s-button and s-link 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 stats 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.

Anchor to variant-tone-and-colorVariant 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.

Example

<s-button tone="critical" variant="primary"> Primary Critical Button </s-button>

<s-badge tone="success" color="strong"> Success Strong Badge </s-badge>

Anchor to using-with-reactUsing with React (App Home)

When building in the App Home with the Shopify Remix template, you'll be using React. Here's how to use Polaris web components in your React components:

Example

JSX


import { useState } from 'react';

function ProductForm() {
const [productName, setProductName] = useState('');

const handleSubmit = (event) => {
event.preventDefault();
console.log('Product name:', productName);
};

return (
<s-section heading="Add Product">
<form onSubmit={handleSubmit}>
<s-stack gap="base">
<s-text-field
label="Product name"
value={productName}
onChange={(e) => setProductName(e.target.value)}
required
/>
<s-button variant="primary" type="submit">
Save product
</s-button>
</s-stack>
</form>
</s-section>
);
}

Anchor to using-with-preactUsing with Preact (UI Extensions)

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:

Example

JSX

export function ProductExtension() {
return (
<s-box padding="base">
<s-stack gap="base">
<s-text>Enable special pricing</s-text>
<s-checkbox
onChange={() => console.log('Checkbox toggled')}
/>
<s-number-field
label="Discount percentage"
suffix="%"
min="0"
max="100"
/>
</s-stack>
</s-box>
);
}

Anchor to properties-vs-attributesProperties 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.

  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.

When using Polaris web components in JSX (React or Preact), 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:

For Polaris web components, you can generally just use the property names as documented, and everything will work as expected.

Polaris web components use standard DOM events, making them work seamlessly with your preferred framework. You can attach event handlers using the same patterns as with native HTML elements.

Event handlers in Polaris components work just like standard HTML elements. In frameworks, use the familiar camelCase syntax (like onClick in React). In plain HTML, use lowercase attributes or addEventListener.

Polaris form components support two primary event types for tracking input changes:

  • onInput: Fires immediately on every keystroke or value change
  • onChange: Fires when the field loses focus or Enter is pressed

Choose the appropriate event based on your needs:

  • Use onInput for real-time validation or character counting
  • Use onChange for validation after a user completes their input

Track when users interact with form elements using these events:

  • onFocus: Fires when an element receives focus
  • onBlur: Fires when an element loses focus

Important details about form values in Polaris web components:

  • All form elements return string values in their events, even numeric inputs
  • Multi-select components (like s-choice-list) use a values prop (array of strings)
  • Access values in event handlers via event.currentTarget.value

Polaris components can be used in two ways:

Uncontrolled (simpler): Component manages its own internal state - use defaultValue prop

Controlled (more powerful): Your code manages the component's state - use value prop

Use controlled components when you need to:

  • Validate input as the user types
  • Format or transform input values
  • Synchronize multiple inputs

Under the hood, Polaris web components handle event registration consistently across frameworks:

  • In React 18+, Polaris components properly register events via addEventListener instead of setting attributes
  • Event names are automatically converted to lowercase (onClick becomes click)
  • All event handlers receive standard DOM events as their first argument

For example, when you write <s-button onClick={handleClick}>, the component:

  1. Sees that "onclick" in element is true
  2. Registers your handler via addEventListener('click', handler)
  3. Passes the event object to your handler when clicked

Basic Event Handling Examples

<s-button onClick={() => console.log('Clicked!')}>
Inline Handler
</s-button>

<s-button onClick={(event) => {
console.log('Event details:', event.type);
console.log('Target:', event.currentTarget);
}}>
With Event Object
</s-button>

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

<s-banner heading="Order created" status="success">
The order has been created successfully.
<s-button slot="secondary-actions">
View order
</s-button>
<s-button slot="secondary-actions">
Download invoice
</s-button>
</s-banner>

Polaris web components work seamlessly with standard HTML forms:

The form components will automatically participate in form submission and validation.

Example

JSX

<form onSubmit={handleSubmit}>
<s-stack gap="base">
<s-text-field
name="email"
label="Email"
type="email"
required
/>
<s-password-field
name="password"
label="Password"
required
/>
<s-button type="submit" variant="primary">
Sign in
</s-button>
</s-stack>
</form>

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

Example

JSX

// Good - provides a label
<s-text-field label="Email address" />

// Bad - missing a label
<s-text-field placeholder="Enter email" />

Common issues and debugging tips for using Polaris web components.

  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.

  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.