Skip to main content

UI components

Packages: @propeller-commerce/propeller-v2-react-ui and @propeller-commerce/propeller-v2-vue-ui

Ready-made commerce UI for React and Vue: product cards, grids, cart, checkout and account, plus a set of headless hooks (composables in Vue) that hold state and talk to the API but render nothing. Both libraries build on the SDK and the core layer, and ship a precompiled stylesheet, so you do not need Tailwind to use them.

Checked against propeller-v2-react-ui 0.32.0 and propeller-v2-vue-ui 0.31.1 on 28 October 2026.

Install​

The React library needs React 18 or newer, the Vue library Vue 3.4 or newer. Install it together with the SDK:

# React
npm install @propeller-commerce/propeller-sdk-v2 @propeller-commerce/propeller-v2-react-ui

# Vue
npm install @propeller-commerce/propeller-sdk-v2 @propeller-commerce/propeller-v2-vue-ui

Add the SDK to your app walks through a first page, including the GraphQL route on your server.

Mount the providers​

The React library takes its setup through two providers. PropellerDepsProvider holds what is set up once for the whole app: the GraphQL client, the services built on it, the currency symbol, your configuration and, optionally, your image component. PropellerProvider holds the session: the signed-in user, whether a session exists, the active Company, the language, the tax display, the portal mode and the shop mode. Build the client and services once (the SDK seam), then mount both at your app root, with the session provider inside.

// React
import { createClient } from '@propeller-commerce/propeller-sdk-v2';
import {
PropellerDepsProvider,
PropellerProvider,
createServices,
} from '@propeller-commerce/propeller-v2-react-ui';
import '@propeller-commerce/propeller-v2-react-ui/styles.css';

const graphqlClient = createClient({ endpoint: '/api/graphql' });
const services = createServices(graphqlClient);

<PropellerDepsProvider
value={{ graphqlClient, services, currency: '€', configuration: {} }}
>
<PropellerProvider
value={{
user, // Contact | Customer | null
isAuthenticated, // true once you know a session exists (optional)
companyId, // number | undefined (active company, B2B)
language: 'NL',
includeTax: false,
portalMode: 'open',
shopMode: 'hybrid',
}}
>
{children}
</PropellerProvider>
</PropellerDepsProvider>;

Create the client and services outside your components so they are built once. Recompute the session value when that state changes, for example after login or a company switch. Pass isAuthenticated as soon as you know a session exists, before the user profile has loaded, so semi-closed pages do not briefly show their logged-out state. A second PropellerProvider deeper in the tree replaces the session for that part of the page. Inside the providers, useServices() returns the services (it throws when PropellerDepsProvider is missing) and usePropellerContext() returns the client, services and session together (null when either provider is missing).

Vue splits the same two parts differently: install the propellerVue plugin once for the client, services, currency and configuration, then wrap the routed tree in the PropellerProvider component for the session.

// Vue
import { createApp } from 'vue';
import { createClient } from '@propeller-commerce/propeller-sdk-v2';
import { propellerVue, createServices } from '@propeller-commerce/propeller-v2-vue-ui';
import '@propeller-commerce/propeller-v2-vue-ui/styles.css';
import App from './App.vue';

const graphqlClient = createClient({ endpoint: '/api/graphql' });

createApp(App)
.use(propellerVue, {
graphqlClient,
services: createServices(graphqlClient),
currency: '€',
configuration: {},
})
.mount('#app');
<!-- App.vue -->
<script setup lang="ts">
import { PropellerProvider } from '@propeller-commerce/propeller-v2-vue-ui';
// user and companyId come from your own session state.
</script>

<template>
<PropellerProvider :user="user" :company-id="companyId" language="NL" :include-tax="false" portal-mode="open">
<RouterView />
</PropellerProvider>
</template>

Use your framework's image component​

By default the components render product and category images with a plain <img>. To send every image through your framework's image component instead, such as next/image, pass it as imgComponent: on PropellerDepsProvider in React, or in the propellerVue plugin options in Vue. It receives the props of ImgComponentProps from the core layer (src, alt, className, width, height, loading and onClick). Payment provider logos keep their plain <img>.

// next-img.tsx
'use client';
import Image from 'next/image';
import type { ImgComponentProps } from '@propeller-commerce/propeller-v2-core-ui';

export function NextImg({ src, alt, className, width, height, loading, onClick }: ImgComponentProps) {
// next/image needs a size: render a plain image when the component passes none.
if (!width || !height) {
return <img src={src} alt={alt} className={className} loading={loading} onClick={onClick} />;
}
return (
<Image src={src} alt={alt} className={className} width={width} height={height} loading={loading} onClick={onClick} />
);
}

// At your app root:
// <PropellerDepsProvider value={{ graphqlClient, services, currency: '€', configuration: {}, imgComponent: NextImg }}>

next/image only optimizes images from hosts you allow in next.config (images.remotePatterns). Add the hosts your product images come from. In your own components, useImgComponent() returns the injected component, and PropellerImg renders an image with the component you pass as its as prop.

Entry points​

Each library exposes four import paths:

Import pathContentsRuntime
package rootcomponents, hooks, contexts, createServicesclient (carries "use client" in Next.js)
/purepresentational components only, safe in a Server Componentserver and client
/sharedcreateServices, formatters, helpers, types (no framework code)server and client
/styles.cssprecompiled stylesheetimport once at the root

In Next.js App Router, import createServices and the formatters from /shared, and pure components from /pure, when you render on the server, so you do not pull the whole client bundle across the boundary.

Components or hooks​

  • Components render commerce UI. Import one and render it inside the provider. Layout-heavy components such as ProductCard support a compound API (ProductCard.Image, ProductCard.Price and so on) so you control what renders and in what order.
  • Hooks (React) and composables (Vue) are headless: they hold state and call the API but render nothing. Use them to build your own UI, or to drive your own components. The set includes useCart, useAuth, useCheckout, useProductSearch, useProductInfo, useOrders, useFavorites, useMenu, useCompany, useAddress, useClusterConfigurator, usePriceRequest and the purchase authorization hooks. useServices returns the services bundle from the provider.
import { useCart, usePropellerContext } from '@propeller-commerce/propeller-v2-react-ui';

function MiniCart() {
// Hooks take the client and the session as options. Read both from the providers.
const { graphqlClient, user, companyId, language } = usePropellerContext()!;
const cart = useCart({ graphqlClient, user, companyId, language });
// ...
}

Every hook takes an options object and its type is exported as Use<Name>Options, for example UseCartOptions. useCart needs the graphqlClient and the user. The rest is optional.

React and Vue parity​

The two libraries expose the same components and the same hook set (composables in Vue), so a storefront's structure carries across frameworks. Props and composable options are typed, so your editor shows them inline. The component reference lists what is available and links every React component and every hook to its page on the package documentation sites (React, Vue). The Vue site documents the composables. For Vue component props, read the types.

Build your own UI​

If you work in a framework without a Propeller UI library (Angular, Svelte, Web Components), use the SDK services and the formatters in the core layer directly. The component reference works as a checklist of what to build.

See also​