Skip to main content

Components

Pre-built React components with SDK integration. These handle common storefront patterns like product cards, cart drawers, and checkout modals.
These components are for custom React storefronts outside Shoppex hosting. Hosted Shoppex themes use Liquid templates and the platform commerce runtime.

ProductCard

Display a product with image, price, and add-to-cart functionality.
import { ProductCard } from '@shoppex/ui';
import type { Product } from '@shoppexio/storefront';

// @validate
const product = {
  uniqid: 'prod_123',
  title: 'Example Product',
  slug: 'example-product',
  price: '9.99',
  currency: 'USD',
  images: [],
} satisfies Product;

<ProductCard
  product={product}
  href={`/product/${product.slug}`}
/>

Props

product
Product
required
Product object from SDK
href
string
Link URL for the product
showRating
boolean
default:"true"
Show star rating
showBadge
boolean
default:"true"
Show type badge (Subscription, Variants, Group)
showAddToCart
boolean
default:"true"
Show add to cart button
showDescription
boolean
default:"true"
Show product description
centerContent
boolean
default:"false"
Center align title and description
aspectRatio
'square' | '4/3' | '3/4' | '16/9'
default:"'4/3'"
Image aspect ratio
Custom link component (for React Router, Next.js, etc.)
For frameworks like React Router or Next.js:
import { Link } from 'react-router-dom';

<ProductCard
  product={product}
  href={`/product/${product.slug}`}
  LinkComponent={({ href, children, className }) => (
    <Link to={href} className={className}>{children}</Link>
  )}
/>

Custom Rendering

<ProductCard
  product={product}
  renderImage={(p) => <CustomImage src={p.images[0]} />}
  renderBadge={(p) => p.bestseller && <Badge>Bestseller</Badge>}
  renderPrice={(p) => <CustomPrice product={p} />}
  renderActions={(p) => <QuickViewButton product={p} />}
/>

AddToCartButton

Smart button that shows quantity controls when product is in cart.
import { AddToCartButton } from '@shoppex/ui';
import type { Product } from '@shoppexio/storefront';

// @validate
const product = {
  uniqid: 'prod_123',
  title: 'Example Product',
  price: '9.99',
  currency: 'USD',
  images: [],
} satisfies Product;

<AddToCartButton product={product} />

Props

product
Product
required
Product object
variantId
string
Specific variant ID
showQuantityControls
boolean
default:"true"
Show +/- controls when in cart
onAddToCart
() => void
Callback after adding to cart

Behavior

  1. Not in cart: Shows “Add to Cart” button
  2. In cart: Shows quantity controls (- qty +)
  3. Out of stock: Shows disabled “Out of Stock” button

CartDrawer

Slide-over cart sidebar with full cart management.
import { CartDrawer } from '@shoppex/ui';
import { useState } from 'react';

// @validate
function App() {
  const [isOpen, setIsOpen] = useState(false);

  return (
    <>
      <button onClick={() => setIsOpen(true)}>Open Cart</button>
      <CartDrawer
        isOpen={isOpen}
        onClose={() => setIsOpen(false)}
        checkoutHref="/checkout"
      />
    </>
  );
}

Props

isOpen
boolean
required
Whether the drawer is open
onClose
() => void
required
Called when drawer should close
checkoutHref
string
default:"'/checkout'"
Checkout page URL
title
string
default:"'Cart'"
Drawer title
emptyMessage
string
default:"'Your cart is empty'"
Message when cart is empty
checkoutLabel
string
default:"'Checkout'"
Checkout button label
showTaxNote
boolean
default:"true"
Show “Shipping and taxes calculated at checkout” note
Custom link component

Features

  • Escape key closes drawer
  • Body scroll lock when open
  • Real-time cart sync
  • Quantity +/- controls
  • Remove item button
  • Subtotal calculation

CheckoutModal

Email collection modal for starting checkout.
import { CheckoutModal } from '@shoppex/ui';
import { useState } from 'react';

// @validate
function Example() {
  const [showModal, setShowModal] = useState(false);
  const appliedCoupon = 'WELCOME10';

  return (
    <CheckoutModal
      isOpen={showModal}
      onClose={() => setShowModal(false)}
      coupon={appliedCoupon}
    />
  );
}

Props

isOpen
boolean
required
Whether the modal is open
onClose
() => void
required
Called when modal should close
coupon
string
Pre-filled coupon code
autoRedirect
boolean
default:"true"
Automatically redirect to checkout on success
onSuccess
(result) => void
Callback on successful checkout start
onError
(error: string) => void
Callback on checkout error

Custom Redirect

<CheckoutModal
  isOpen={showModal}
  onClose={() => setShowModal(false)}
  autoRedirect={false}
  onSuccess={({ redirectUrl, invoiceId }) => {
    // Track event before redirect
    analytics.track('checkout_started', { invoiceId });
    window.location.href = redirectUrl;
  }}
/>

SearchCommand

Cmd+K style search command palette.
import { SearchCommand, SearchBar } from '@shoppex/ui';
import { useState, useEffect } from 'react';

// @validate
function App() {
  const [isOpen, setIsOpen] = useState(false);

  // Open with Cmd+K
  useEffect(() => {
    const handler = (e: KeyboardEvent) => {
      if ((e.metaKey || e.ctrlKey) && e.key === 'k') {
        e.preventDefault();
        setIsOpen(true);
      }
    };
    window.addEventListener('keydown', handler);
    return () => window.removeEventListener('keydown', handler);
  }, []);

  return (
    <>
      <SearchBar onClick={() => setIsOpen(true)} />
      <SearchCommand
        isOpen={isOpen}
        onClose={() => setIsOpen(false)}
        getProductHref={(p) => `/product/${p.slug ?? p.uniqid}`}
      />
    </>
  );
}

Props

isOpen
boolean
required
Whether the palette is open
onClose
() => void
required
Called when palette should close
onSelect
(product: Product) => void
Called when a product is selected
getProductHref
(product: Product) => string
Generate product link URL
hideOutOfStock
boolean
default:"false"
Hide out of stock products
maxResults
number
default:"10"
Maximum search results

PriceDisplay

Formatted price with discount and subscription support.
import { PriceDisplay } from '@shoppex/ui';
import type { Product } from '@shoppexio/storefront';

// @validate
const product = {
  uniqid: 'prod_123',
  title: 'Example Product',
  price: '9.99',
  currency: 'USD',
  images: [],
} satisfies Product;

<PriceDisplay product={product} />

Props

product
Product
required
Product object
size
'sm' | 'md' | 'lg'
default:"'md'"
Text size
showOriginalPrice
boolean
default:"true"
Show strikethrough original price if discounted
showSubscriptionInterval
boolean
default:"true"
Show “/month” for subscriptions

Example Output

  • Regular: $99.99
  • Discounted: ~~149.99  149.99~~ **99.99** -33%
  • Subscription: $9.99/month
  • Range: 49.9949.99 – 99.99

QuantityStepper

Standalone quantity input with +/- controls.
import { QuantityStepper } from '@shoppex/ui';
import { useState } from 'react';

// @validate
function QuantityInput() {
  const [qty, setQty] = useState(1);

  return (
    <QuantityStepper
      value={qty}
      onChange={setQty}
      min={1}
      max={10}
      size="md"
    />
  );
}

Props

value
number
required
Current quantity
onChange
(value: number) => void
required
Called when quantity changes
min
number
default:"1"
Minimum value
max
number
default:"Infinity"
Maximum value
size
'sm' | 'md' | 'lg'
default:"'md'"
Control size

CouponInput

Coupon code input with validation.
import { CouponInput } from '@shoppex/ui';
import { useCheckout } from '@shoppex/ui';
import { useState } from 'react';

// @validate
function CouponSection() {
  const [appliedCoupon, setAppliedCoupon] = useState<string>();
  const { validateCoupon } = useCheckout();

  return (
    <CouponInput
      appliedCode={appliedCoupon}
      onApply={async (code) => {
        const result = await validateCoupon(code);
        if (result.valid) {
          setAppliedCoupon(code);
          return true;
        }
        return false;
      }}
      onRemove={() => setAppliedCoupon(undefined)}
    />
  );
}

Props

onApply
(code: string) => Promise<boolean>
required
Validate and apply coupon. Return true if valid.
appliedCode
string
Currently applied coupon code
onRemove
() => void
Called when coupon is removed