Skip to main content

Migrate StyleHelper to container queries

The Style helper was a React utility for authoring conditional styles based on viewport size, hover, and focus states. In Polaris web components, responsive values use native container queries instead. Wrap your content in <s-query-container> and use @container syntax directly in property values.


Anchor to How container queries workHow container queries work

Container queries respond to the width of a parent container rather than the viewport. This makes components more reusable across different layout contexts (main content, sidebar, modals).

To use responsive values:

  1. Wrap content in <s-query-container>
  2. Use @container (inline-size > Npx) valueWhenTrue, valueWhenFalse syntax in any property that supports responsive values
  3. The first value applies when the condition is true (container is wider than the threshold)
  4. The second value (after the comma) applies when the condition is false (container is narrower)

Anchor to Migrating viewport breakpointsMigrating viewport breakpoints

The previous Style helper used named viewport breakpoints (extraSmall, small, medium, large). Container queries don't have named breakpoints — you specify pixel values based on the parent container's width instead. Since container queries respond to the container, not the viewport, there's no direct mapping. Choose breakpoint values based on where your extension renders (for example, a block is narrower than the full viewport).


Migrating responsive grid columns

import '@shopify/ui-extensions/preact';
import {render} from 'preact';

export default function extension() {
render(<Extension />, document.body);
}

function Extension() {
return (
<s-query-container>
<s-grid
gridTemplateColumns="@container (inline-size > 600px) '30% 70%', 1fr"
gap="base"
>
<s-text>Sidebar</s-text>
<s-text>Main content</s-text>
</s-grid>
</s-query-container>
);
}
import {
reactExtension,
Grid,
Text,
Style,
} from '@shopify/ui-extensions-react/checkout';

export default reactExtension(
'purchase.checkout.block.render',
() => <Extension />,
);

function Extension() {
return (
<Grid
columns={Style.default('fill')
.when({viewportInlineSize: {min: 'small'}}, ['30%', '70%'])}
spacing="base"
>
<Text>Sidebar</Text>
<Text>Main content</Text>
</Grid>
);
}

Migrating responsive padding

import '@shopify/ui-extensions/preact';
import {render} from 'preact';

export default function extension() {
render(<Extension />, document.body);
}

function Extension() {
return (
<s-query-container>
<s-box
padding="@container (inline-size > 450px) none, large-100"
background="subdued"
>
<s-text>Content with responsive padding</s-text>
</s-box>
</s-query-container>
);
}
import {
reactExtension,
View,
Text,
Style,
} from '@shopify/ui-extensions-react/checkout';

export default reactExtension(
'purchase.checkout.block.render',
() => <Extension />,
);

function Extension() {
return (
<View
padding={Style.default('base')
.when({viewportInlineSize: {min: 'small'}}, 'large')}
background="subdued"
>
<Text>Content with responsive padding</Text>
</View>
);
}

Anchor to Conditionally hiding contentConditionally hiding content

Migrating conditional display

import '@shopify/ui-extensions/preact';
import {render} from 'preact';

export default function extension() {
render(<Extension />, document.body);
}

function Extension() {
return (
<s-query-container>
<s-box display="@container (inline-size > 450px) none, auto">
<s-text>Only visible on small containers</s-text>
</s-box>
</s-query-container>
);
}
import {
reactExtension,
View,
Text,
Style,
} from '@shopify/ui-extensions-react/checkout';

export default reactExtension(
'purchase.checkout.block.render',
() => <Extension />,
);

function Extension() {
return (
<View
display={Style.default('auto')
.when({viewportInlineSize: {min: 'small'}}, 'none')}
>
<Text>Only visible on small viewports</Text>
</View>
);
}

Use containerName to target a specific ancestor container when nesting multiple query containers.

<s-query-container containerName="outer">
<s-section>
<s-query-container>
<s-box padding="@container outer (inline-size > 450px) large-100, none">
References the "outer" container specifically
</s-box>
</s-query-container>
</s-section>
</s-query-container>

Query a range of container sizes using compound conditions.

<s-query-container>
<s-box
padding="@container (300px < inline-size < 500px) large-100, none"
border="base"
background="subdued"
>
<s-text>Padding only applies when container is between 300px and 500px</s-text>
</s-box>
</s-query-container>

Stack multiple breakpoints in a single value using comma-separated conditions. Conditions cascade mobile-first — later matching conditions override earlier ones, and the final unconditioned value is the base default applied when no condition matches.

<s-query-container>
<s-box padding="@container (inline-size > 300px) base, (inline-size > 350px) large-200, (inline-size > 400px) large-400, none">
<s-text>Padding scales with the container size, starting from none below 300px.</s-text>
</s-box>
</s-query-container>

In the example above, a container narrower than 300px gets padding="none" (the base default). Between 300px and 350px only the first condition matches → base. Between 350px and 400px both early conditions match and the later one wins → large-200. Above 400px all three match and the last one wins → large-400. List conditions from smallest to largest threshold so the cascade resolves the way you expect.

Values with reserved characters like () or , should be wrapped in quotes.

<s-query-container>
<s-grid
gridTemplateColumns="@container (inline-size > 450px) 'repeat(4, 1fr)', 'repeat(2, 1fr)'"
gap="base"
border="base"
>
<s-box background="subdued" padding="base"><s-text>1</s-text></s-box>
<s-box background="subdued" padding="base"><s-text>2</s-text></s-box>
<s-box background="subdued" padding="base"><s-text>3</s-text></s-box>
<s-box background="subdued" padding="base"><s-text>4</s-text></s-box>
</s-grid>
</s-query-container>

Anchor to hover and focus conditionshover and focus conditions

The Style helper's hover and focus conditions don't have a replacement in Polaris web components.


Was this page helpful?