Skip to content
Lockerverse SDK
Esc
↑↓navigate↵open⌘Jpreview
Custom storefront14 examples

Composition / Development

Custom storefront

Keep the product UI in the host and open native checkout.

All examples
Previewlight site theme

Loading storefront…

CodeApp.tsx
import {
  LockerversePayment,
  type LockerversePaymentSelectionItem,
  type LockerversePaymentTheme,
} from "@lockerverse/react/payment";
import "@lockerverse/react/payment.css";
import { useLockerverseWidget } from "@lockerverse/react/widget";
import { useState } from "react";
import { exampleRuntime } from "../example-runtime";

const widgetOptions = {
  apiBaseUrl: exampleRuntime.apiBaseUrl,
  communitySlug: exampleRuntime.communitySlug,
  environment: "development" as const,
  sentryDsn: null,
  widgetSlug: exampleRuntime.widgetSlug,
};

function formatMoney(amountCents: number | null) {
  if (amountCents === null) {
    return "Choose amount";
  }
  return new Intl.NumberFormat("en-US", {
    currency: "USD",
    style: "currency",
  }).format(amountCents / 100);
}

export default function CustomStorefrontExample({
  theme = "light",
}: {
  theme?: LockerversePaymentTheme;
}) {
  const widget = useLockerverseWidget(widgetOptions);
  const [selection, setSelection] =
    useState<LockerversePaymentSelectionItem | null>(null);

  if (selection) {
    return (
      <div className="storefront-checkout">
        <button
          className="example-secondary-button"
          onClick={() => setSelection(null)}
          type="button"
        >
          ← Products
        </button>
        <LockerversePayment
          selection={[selection]}
          style={{ "--lockerverse-accent": "#f1511b" }}
          theme={theme}
          widget={widget}
        />
      </div>
    );
  }
  if (widget.loading) {
    return <p className="example-status">Loading storefront…</p>;
  }
  if (widget.error) {
    return <p className="example-status">{widget.error.message}</p>;
  }
  if (!widget.catalog) {
    return null;
  }

  return (
    <div className="storefront-grid">
      {widget.catalog.products.map((product) => (
        <button
          className="storefront-product"
          key={product.id}
          onClick={() =>
            setSelection(
              product.pricingMode === "custom"
                ? {
                    amountCents: product.minimumAmountCents ?? 100,
                    productSlug: product.slug,
                  }
                : { productSlug: product.slug, quantity: 1 }
            )
          }
          type="button"
        >
          <span>{product.name}</span>
          <strong>{formatMoney(product.amountCents)}</strong>
        </button>
      ))}
    </div>
  );
}