npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@shoppexio/ui

v0.1.20

Published

Storefront React primitives and components for building Shoppex storefronts

Readme

@shoppex/ui

Storefront React primitives, components, and blocks for building Shoppex storefronts. Works seamlessly with the Storefront SDK.

Scope check (read this if you are an agent or new contributor):

This package is the storefront-SDK primitive library — it provides hooks (useCart, useStore, useCheckout), composed components (ProductCard, CartDrawer, CheckoutModal, SearchCommand), and blocks (Hero, ProductGrid, FAQ, Footer) for theme authors and external SDK consumers building on top of @shoppexio/storefront.

This is NOT the shared design system for internal apps. The merchant dashboard (apps/dashboard), checkout (apps/checkout), and customer-portal (apps/customer-portal) each maintain their own local components/ui/ directories with shadcn-style primitives. Those apps are not expected to import from @shoppex/ui beyond the already-used @shoppex/ui/utils helpers in the dashboard's code editor.

If you are consolidating internal UI primitives across apps, that is a separate (valid) initiative — track it explicitly instead of moving code into this package.

Installation

bun add @shoppex/ui@npm:@shoppexio/ui @shoppexio/storefront

@shoppex/ui is the internal import name used by first-party themes. The published npm package is @shoppexio/ui; themes install it through the npm alias above so source imports stay stable while release packages use the public scope.

Quick Start

import { shoppex } from '@shoppexio/storefront';
import { useStore, useCart, ProductGrid, CartDrawer, Hero } from '@shoppex/ui';

// Initialize SDK
shoppex.init('your-store-slug');

function App() {
  const { store, products, isLoading } = useStore();
  const { totalQuantity } = useCart();
  const [cartOpen, setCartOpen] = useState(false);

  if (isLoading) return <ProductGridSkeleton count={8} />;

  return (
    <div>
      <Hero store={store} products={products} />
      <ProductGrid products={products} />
      <CartDrawer isOpen={cartOpen} onClose={() => setCartOpen(false)} />
    </div>
  );
}

What's Included

Primitives (Hooks)

| Hook | Description | |------|-------------| | useCart() | Cart state + add/remove/update operations | | useCartItem(productId) | Cart state for a specific product | | useStore() | Fetch store, products, groups | | useStoreSettings() | Store configuration (cart enabled, etc.) | | useSearch() | Product search with debouncing | | useProductFilter() | Filter/sort products by category | | useCheckout() | Checkout flow + coupon validation | | useCartDealQuote() | Server-quoted cart deals, discounts, and bundle suggestions | | usePrice() | Price formatting with store currency | | useProductPricing() | Product pricing details (discount, subscription) |

Components

| Component | Description | |-----------|-------------| | ProductCard | Product card with image, price, add-to-cart | | AddToCartButton | Smart button with quantity controls | | CartDrawer | Slide-over cart sidebar | | CheckoutModal | Email collection modal | | SearchCommand | Cmd+K search palette | | SearchBar | Search trigger button | | PriceDisplay | Formatted price with discount/subscription | | QuantityStepper | +/- quantity input | | CouponInput | Coupon code input with validation |

Blocks (Sections)

| Block | Description | |-------|-------------| | Hero | Store hero with logo, title, stats | | ProductGrid | Responsive product grid with filters | | WhyChooseUs | Features/trust bento grid | | FAQ | Accordion FAQ section | | Footer | Store footer with links |

Styling

Components use CSS variables for theming. Define these in your CSS:

:root {
  --color-background: #09090b;
  --color-foreground: #fafafa;
  --color-card: #18181b;
  --color-primary: #7c3aed;
  --color-primary-foreground: #ffffff;
  --color-muted: #27272a;
  --color-muted-foreground: #a1a1aa;
  --color-border: #27272a;
  --color-destructive: #ef4444;
}

Or use Tailwind CSS with these variables configured.

Using with React Router

Components accept a LinkComponent prop for custom link handling:

import { Link } from 'react-router-dom';

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

<CartDrawer
  isOpen={isOpen}
  onClose={() => setIsOpen(false)}
  checkoutHref="/checkout"
  LinkComponent={({ href, children, className, onClick }) => (
    <Link to={href} className={className} onClick={onClick}>{children}</Link>
  )}
/>

SSR Support

Use InitialDataProvider for SSR/SSG:

import { InitialDataProvider } from '@shoppex/ui';

// Server-side
const { store, products } = await fetchInitialData();

// Client
<InitialDataProvider data={{ store, products }}>
  <App />
</InitialDataProvider>

Examples

Custom Product Card

<ProductCard
  product={product}
  href={`/product/${product.slug}`}
  showRating={false}
  showDescription={false}
  aspectRatio="square"
  renderActions={(p) => (
    <button onClick={() => quickView(p)}>Quick View</button>
  )}
/>

Search with Cmd+K

function App() {
  const [searchOpen, setSearchOpen] = useState(false);

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

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

Custom Checkout Flow

const { checkout, validateCoupon } = useCheckout();

// Validate coupon
const result = await validateCoupon('SAVE10');
if (result.valid) {
  console.log(`${result.discount}% off!`);
}

// Custom checkout (no auto-redirect)
const checkoutResult = await checkout({
  email: '[email protected]',
  coupon: 'SAVE10',
  autoRedirect: false,
});

if (checkoutResult.success) {
  // Track event, then redirect
  analytics.track('checkout_started');
  window.location.href = checkoutResult.redirectUrl;
}

License

MIT