Getting started
polaris-vue-elements is a set of typed Vue 3 components that wrap Shopify's Polaris web components (<s-button>, <s-text-field>, <s-modal>, ...). The web components themselves are loaded at runtime from Shopify's CDN — this library just gives you a typed, idiomatic Vue surface (props, events, v-model) around them.
1. Install
npm install polaris-vue-elements2. Load the Polaris runtime
The wrapper components are presentational only — they don't ship the actual custom element definitions. Your host app must load Shopify's Polaris runtime script, which registers <s-*> elements globally:
<!-- index.html -->
<!doctype html>
<html>
<head>
<script src="https://cdn.shopify.com/shopifycloud/polaris-2.js"></script>
</head>
<body>
<div id="app"></div>
</body>
</html>This works in any Vue app, not just inside Shopify Admin — the components are plain custom elements and render wherever the script is loaded.
polaris-2.js is Shopify's stable channel and what we recommend for production. To pin an exact release instead, or to understand how the runtime version relates to this library's version, see Known quirks & gotchas.
Polaris 2.0 and the new admin design
Polaris 2.0 brings the Shopify admin's new visual design (color, typography, spacing and icons). It became stable on October 22, 2026 (announcement). Moving from 1.x is opt-in: swap polaris-1.js for polaris-2.js.
- No code changes needed. 2.0 is a visual update: its components, props, events and slots are the same as 1.1, plus a few additions (such as
SThumbnail'sloadingprop). Apps that stay onpolaris-1.jskeep working with these wrappers; only the additions need 2.x at runtime. - It follows each store's design. Inside the Shopify admin, a store that has the new admin design gets the new look, and a store on the previous design keeps the 1.x look. Outside the admin, the new look is used by default, which makes it easy to preview locally.
- Fixed or sticky content near the bottom of the viewport should reserve space with the
--shopify-safe-area-inset-bottomCSS variable (environment API), so the floating Sidekick bar doesn't cover it. - Built for Shopify apps must match the admin's new visual style by May 1, 2027.
3. Configure Vite
Tell Vue's compiler to treat any s-* tag as a custom element instead of trying to resolve it as a Vue component:
// vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [
vue({
template: {
compilerOptions: {
isCustomElement: (tag) => tag.startsWith('s-'),
},
},
}),
],
})4. Your first component
Import components from polaris-vue-elements and use them like any other Vue component — props and events are fully typed:
<script setup lang="ts">
import { SButton } from 'polaris-vue-elements'
function handleClick(): void {
console.log('clicked')
}
</script>
<template>
<SButton variant="primary" @click="handleClick">Save</SButton>
</template>5. v-model on form components
Form-like components support v-model, mapped to the correct underlying prop/event pair for that element (see src/lib/overrides.ts for the full mapping — text-likes use value/input, SCheckbox/SSwitch use checked/change, SChoiceList uses values/change, and so on):
<script setup lang="ts">
import { ref } from 'vue'
import { STextField } from 'polaris-vue-elements'
const name = ref('')
</script>
<template>
<STextField v-model="name" label="Name" placeholder="Your name" />
</template>6. Controlling overlays (modal, popover, tooltip)
Overlay components like SModal can be controlled two ways:
Declaratively, with the standard HTML commandFor/command attributes on a trigger button — no script required:
<template>
<SButton commandFor="demo-modal" command="--show">Open modal</SButton>
<SModal id="demo-modal" heading="Demo modal">
<SText>Modal content.</SText>
<template #primary-action>
<SButton variant="primary" command="--hide" commandFor="demo-modal">Close</SButton>
</template>
</SModal>
</template>Imperatively, by grabbing a typed template ref and calling the underlying element's method through $el:
<script setup lang="ts">
import { useTemplateRef } from 'vue'
import { SModal } from 'polaris-vue-elements'
const modalRef = useTemplateRef<InstanceType<typeof SModal>>('modal')
function openModal(): void {
modalRef.value?.$el.showOverlay()
}
</script>
<template>
<SModal ref="modal" id="demo-modal" heading="Demo modal">
<SText>Modal content.</SText>
</SModal>
</template>7. Typing raw <s-*> tags (optional)
If you'd rather keep writing Polaris's own tags (<s-button>, <s-text-field>, …) than switch to the wrappers, the package can still type them. Opt in once, in your tsconfig.json:
{
"compilerOptions": {
"types": ["polaris-vue-elements/elements"]
}
}or with a reference in a .d.ts file such as env.d.ts:
/// <reference types="polaris-vue-elements/elements" />This adds every Polaris tag to Vue's GlobalComponents, so Volar and vue-tsc check raw tags the same way they check the wrappers: union-typed props, typed @click / @aftershow listeners, and a typed element for imperative methods.
<template>
<!-- error: Type '"bogus"' is not assignable to type '"auto" | "primary" | … ' -->
<s-button variant="bogus">Save</s-button>
</template>It's types only: nothing is registered at runtime, so keep the isCustomElement setting from step 3. Two differences from the wrappers:
- No
v-modelon raw tags. Bind:valueand listen for@input/@changeyourself, or use the wrapper component, which maps each form field's property and event for you. - Don't combine it with a hand-written shim for the same tags in your project — two declarations of
GlobalComponents['s-button']with different types conflict. Remove the shim when you opt in.
Next steps
Browse the component reference for the full prop/event list of every wrapper, or read Known quirks & gotchas before you run into them yourself.
Building a Shopify app? See Build a Shopify embedded app with Vue 3 for loading App Bridge, authenticated requests and App Bridge APIs from Vue.