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

merchify-ui

v0.1.13

Published

React components for merchandise visualization and customization

Downloads

22

Readme

merchify-ui

React components for merchandise visualization and customization

Drop-in React components for displaying products, generating mockups, and building print-on-demand experiences. Works with the merchify SDK.


🤖 FOR AI ASSISTANTS - READ THIS FIRST!

If you are an AI assistant (Claude, ChatGPT, Copilot, etc.) helping implement merchify-ui:

ALWAYS fetch and read the complete implementation guide:

/api/ai-guide

(Fetch from the same domain as the docs site you're viewing)

This guide contains:

  • ✅ Complete installation (including critical Tailwind CSS setup)
  • ✅ Common patterns and working code examples
  • ✅ Component API reference
  • ✅ Common mistakes and how to avoid them

DO NOT implement merchify-ui without reading AI_GUIDE.md first! Many issues (especially Tailwind CSS configuration) are documented there.


🤖 Quick Reference for LLMs

| Task | Pattern | Components (in order!) | |------|---------|------------------------| | Display product with artwork | Context-based (REQUIRED) | <ArtSelector /><ProductImage /> (ArtSelector MUST come first!) | | Artwork customization | Context-based | <ArtSelector /><ProductImage /> + <ArtworkCustomizer /> | | Variant selection | Wrap in Product | <Product><ProductOptions /></Product> | | Add to cart | Wrap in Product | <Product><AddToCart /></Product> |

🚨 Critical Rules:

  1. ProductImage REQUIRES ArtSelector: <ProductImage /> by itself shows placeholder - always use <ArtSelector /> first!
  2. Context-only architecture: <ProductImage /> does NOT accept artwork as a prop - use <ArtSelector /> to set artwork in context
  3. <ArtworkCustomizer /> reads from context (set by <ArtSelector>), NOT from props
  4. <ArtAlignment> and <TileCount> are NOT exported - use <ArtworkCustomizer> instead
  5. Most components require <Product> wrapper - check JSDoc for context requirements
  6. Seamless patterns REQUIRE tileCount (TypeScript enforces this)

❌ Most common mistake:

// WRONG - ProductImage without ArtSelector shows placeholder!
<Product productId="BEEB77">
  <ProductImage />  {/* ❌ No artwork = shows "Select artwork to preview" */}
</Product>

// CORRECT - ArtSelector provides artwork
<Product productId="BEEB77">
  <ArtSelector artworks={['https://example.com/art.png']} />  {/* ✅ Sets artwork */}
  <ProductImage />  {/* ✅ Reads artwork from context */}
</Product>

Installation

⚡ One-Command Setup (Recommended)

Run this single command to install and configure everything:

npx merchify-cli init

That's it! The CLI will:

  • ✅ Install merchify-ui and zod packages automatically
  • ✅ Auto-detect your project structure
  • ✅ Configure Tailwind CSS v4 @source paths correctly
  • ✅ Verify your setup works

Then restart your dev server:

npm run dev  # or pnpm dev, yarn dev, bun dev

Manual Installation (Advanced)

If you prefer manual setup:

npm install merchify-ui zod
# or
yarn add merchify-ui zod
# or
pnpm add merchify-ui zod

Peer Dependencies:

  • React 18+ or 19+
  • React DOM 18+ or 19+
  • Zod 3+ or 4+

Important: You'll need to manually configure Tailwind CSS @source paths (see "Styling" section below).

Quick Start

⚠️ IMPORTANT: ProductImage REQUIRES artwork via ArtSelector - they work as a pair!

import { Shop, Product, ProductImage, AddToCart, ArtSelector } from 'merchify-ui';

function MyStore() {
  return (
    <Shop mode="mock">
      <Product productId="BEEB77">
        {/* Step 1: ArtSelector provides artwork (REQUIRED!) */}
        <ArtSelector artworks={['https://example.com/art.png']} />

        {/* Step 2: ProductImage reads artwork from context */}
        <ProductImage />

        {/* Step 3: Add to cart */}
        <AddToCart onAddToCart={(item) => console.log(item)} />
      </Product>
    </Shop>
  );
}

What this does:

  1. <ArtSelector> - Sets artwork in Design context (required for ProductImage to work)
  2. <ProductImage> - Reads artwork from context and displays product mockup
  3. <AddToCart> - Handles cart actions

❌ Common mistake:

// This WON'T work - ProductImage needs artwork from ArtSelector!
<Product productId="BEEB77">
  <ProductImage />  {/* Shows "Select artwork to preview" placeholder */}
</Product>

Core Components

<Shop> - Root Provider

Wraps your app and provides configuration for all child components.

<Shop 
  mode="mock"              // 'mock' | 'live' | 'meilisearch'
  endpoint="https://..."   // Optional: API endpoint
  mockupUrl="https://..."  // Optional: Mockup service URL
  accountId="acc_123"      // Optional: Your account ID
>
  {/* Your app */}
</Shop>

Environment Variables (Next.js):

NEXT_PUBLIC_MERCHIFY_ENDPOINT=https://api.merchify.com
NEXT_PUBLIC_MERCHIFY_MOCKUP_URL=https://mockup.merchify.com
NEXT_PUBLIC_MERCHIFY_ACCOUNT_ID=your-account-id

<Product> - Product Context

Provides product data to child components. Fetches product automatically.

<Product productId="BEEB77">
  <ProductImage />
  <ProductOptions />
  <ProductPrice />
</Product>

<ProductImage> - Mockup Visualization

🚨 REQUIRES ARTSELECTOR: ProductImage will NOT work alone - you MUST use <ArtSelector> first!

What happens without ArtSelector:

  • ❌ Shows "Select artwork to preview" placeholder
  • ❌ No product mockup is generated
  • ❌ Component is essentially non-functional

How to use it correctly:

// ✅ CORRECT - ArtSelector first, then ProductImage
<Product productId="BEEB77">
  {/* Step 1: ArtSelector provides artwork (REQUIRED!) */}
  <ArtSelector artworks={[
    { type: 'regular', src: 'https://example.com/art.jpg' },
    { type: 'pattern', src: 'https://example.com/pattern.jpg', tileCount: 2 }
  ]} />

  {/* Step 2: ProductImage reads artwork from context */}
  <ProductImage className="w-full" />
</Product>

// ❌ WRONG - ProductImage alone shows placeholder!
<Product productId="BEEB77">
  <ProductImage />  {/* ❌ No artwork = placeholder */}
</Product>

// ❌ WRONG - Artwork prop doesn't exist!
<ProductImage artwork={artwork} />  {/* ❌ Not supported */}

Why this design? ProductImage uses context-based architecture - ArtSelector sets artwork in Design context, and ProductImage reads from that context. This allows multiple components to share the same artwork state.

<ProductOptions> - Variant Selector

Shows color and size options for the product.

<ProductOptions 
  variant="inline"  // 'inline' | 'dropdown'
  showPrice={true}  // Show price changes
/>

<ArtworkCustomizer> - Smart Artwork Controls

Automatically renders the correct customization UI based on artwork type:

  • Regular artwork → Positioning controls (drag to align)
  • Seamless patterns → Tile density slider

⚠️ Reads artwork from context (set by <ArtSelector>), not from props!

// Use with ArtSelector to manage artwork via context
<Product productId="BEEB77">
  <ArtSelector artworks={[
    { type: 'regular', src: 'https://example.com/photo.jpg' },
    { type: 'pattern', src: 'https://example.com/pattern.jpg', tileCount: 1 }
  ]} />
  <ArtworkCustomizer maxHeight={200} />  {/* Automatically adapts to artwork type */}
</Product>

<AddToCart> - Purchase Button

<AddToCart 
  onAddToCart={(item) => console.log('Added:', item)}
  className="btn-primary"
/>

All 21 Components

Primitives:

  • ColorSwatch - Color selection
  • ProductPrice - Formatted pricing
  • ThemeToggle - Dark/light mode
  • DragHintAnimation - Drag interaction hint
  • FloatingActionGroup - FAB button group

Composed:

  • ProductCard - Complete product card (multiple variants)
  • ProductImage - Mockup display
  • ProductOptions - Variant selectors
  • ProductList - Product grid/list
  • ProductGallery - Image gallery
  • ArtworkCustomizer - Smart artwork controls (positioning/tile density)
  • ArtSelector - Artwork picker gallery
  • AddToCart - Cart button
  • Lightbox - Full-screen image viewer
  • RealtimeMockup - Live mockup generation
  • CurrentSelectionDisplay - Show selected options
  • TileCount - Tile density control (internal - use ArtworkCustomizer instead)

Patterns (Context Providers):

  • Shop - Root shop provider
  • Product - Product data provider

Hooks:

  • useProduct - Access product context
  • useShop - Access shop context
  • useProductGallery - Gallery state management
  • usePlacementsProcessor - Placement data processing

Complete Example

import {
  Shop,
  Product,
  ProductImage,
  ProductOptions,
  ProductPrice,
  AddToCart,
  ArtSelector,
  ArtworkCustomizer
} from 'merchify-ui';

function MerchStore() {
  return (
    <Shop mode="mock">
      <div className="container mx-auto p-4">
        <h1>My Merch Store</h1>

        <Product productId="BEEB77">
          <div className="grid md:grid-cols-2 gap-8">
            {/* Product Image */}
            <div>
              <ProductImage className="w-full rounded-lg" />
            </div>

            {/* Product Details */}
            <div className="space-y-4">
              <h2 className="text-2xl font-bold">Custom T-Shirt</h2>
              <ProductPrice className="text-xl" />

              {/* Artwork Selection */}
              <ArtSelector artworks={[
                { type: 'regular', src: 'https://example.com/photo.jpg' },
                { type: 'regular', src: 'https://example.com/logo.png' },
                { type: 'pattern', src: 'https://example.com/pattern.jpg', tileCount: 1 }
              ]} />

              {/* Artwork Customization - automatically shows right controls! */}
              <ArtworkCustomizer maxHeight={200} />

              <ProductOptions variant="inline" />

              <AddToCart
                onAddToCart={(item) => {
                  console.log('Added to cart:', item);
                }}
              />
            </div>
          </div>
        </Product>
      </div>
    </Shop>
  );
}

Common Mistakes & How to Fix Them

❌ #1 Most Common Mistake: ProductImage shows "Select artwork to preview"

Problem: ProductImage REQUIRES ArtSelector - they are a pair, not optional!

This is the #1 mistake both LLMs and developers make. ProductImage is not a standalone component.

// ❌ WRONG - ProductImage alone doesn't work!
<Product productId="BEEB77">
  <ProductImage />  {/* ❌ Shows placeholder - no artwork! */}
</Product>

Solution: ALWAYS use ArtSelector before ProductImage:

// ✅ CORRECT - ArtSelector + ProductImage together
<Product productId="BEEB77">
  {/* ArtSelector comes FIRST (required!) */}
  <ArtSelector artworks={[
    'https://example.com/art1.jpg',
    'https://example.com/art2.jpg'
  ]} />

  {/* ProductImage comes SECOND (reads from ArtSelector) */}
  <ProductImage />
</Product>

Remember: Think of ArtSelector and ProductImage as a package deal - you need both!

❌ Trying to use ArtAlignment or TileCount directly

Problem: ArtAlignment and TileCount are internal components and NOT exported.

// ❌ WRONG - These components are not exported!
import { ArtAlignment, TileCount } from 'merchify-ui'; // ❌ Will fail!

Solution: Use ArtworkCustomizer instead - it handles both types automatically:

// ✅ CORRECT - Use ArtworkCustomizer (works with context)
import { Product, ArtSelector, ArtworkCustomizer, ProductImage } from 'merchify-ui';

<Product productId="BEEB77">
  <ArtSelector artworks={[
    { type: 'regular', src: 'https://example.com/photo.jpg' },
    { type: 'pattern', src: 'https://example.com/pattern.jpg', tileCount: 1 }
  ]} />
  <ProductImage />
  <ArtworkCustomizer maxHeight={200} />  {/* Automatically shows right controls! */}
</Product>

How it works:

  • ArtworkCustomizer reads artwork from Design context (set by ArtSelector)
  • Automatically renders positioning controls for regular artwork
  • Automatically renders tile density controls for seamless patterns
  • No manual conditionals needed - it switches UI automatically!

🎯 Common Patterns for LLMs & Developers

Pattern 1: Context-Based Artwork Management

The Right Way: Use ArtSelector + ProductImage + ArtworkCustomizer

import { Shop, Product, ArtSelector, ProductImage, ArtworkCustomizer } from 'merchify-ui';

// ✅ CORRECT - Context-based pattern
<Shop>
  <Product productId="BEEB77">
    {/* Step 1: ArtSelector sets artwork in context */}
    <ArtSelector artworks={[
      { type: 'regular', src: 'https://example.com/photo.jpg' },
      { type: 'pattern', src: 'https://example.com/pattern.jpg', tileCount: 1 }
    ]} />

    {/* Step 2: ProductImage reads artwork from context */}
    <ProductImage />

    {/* Step 3: ArtworkCustomizer reads artwork from context and shows right controls */}
    <ArtworkCustomizer maxHeight={200} />
  </Product>
</Shop>

Why this works:

  1. ArtSelector → Sets selectedArtwork in Design context
  2. ProductImage → Reads from context, shows mockup
  3. ArtworkCustomizer → Reads from context, shows positioning (regular) or tile density (pattern) controls

Pattern 2: Multi-Placement Products (Advanced)

For products with multiple placements needing different colors/images:

import { Product, ProductImage } from 'merchify-ui';

// ✅ CORRECT - Use placements prop for multi-placement products
<Product productId="shirt-123">
  <ProductImage placements={{
    Front: '#FF0000',           // Red color on front
    Back: 'https://logo.jpg',   // Image on back
    Sleeve: 'rgb(0, 255, 0)'    // Green on sleeve
  }} />
</Product>

When to use: Products with multiple placements needing different colors or images per placement.

Pattern 3: Product Context Components

Components that require <Product> wrapper:

  • <ProductOptions /> - Reads product options
  • <ProductPrice /> - Can work standalone, but best in context
  • <AddToCart /> - Requires product context
  • <ArtworkCustomizer /> - Requires Design context (from Product)
// ✅ CORRECT - All context components wrapped
<Product productId="BEEB77">
  <ProductImage />
  <ProductOptions />
  <ProductPrice />
  <AddToCart onAddToCart={(item) => console.log(item)} />
</Product>

// ❌ WRONG - Missing Product wrapper
<ProductOptions />  // Will throw error!

Pattern 4: Type-Safe Artwork with TypeScript

Use discriminated unions for type safety:

import type { RegularArtwork, SeamlessPattern, Artwork } from 'merchify-ui';

// Type 1: Regular artwork
const photo: RegularArtwork = {
  type: 'regular',
  src: 'https://example.com/photo.jpg'
};

// Type 2: Seamless pattern (tileCount REQUIRED!)
const pattern: SeamlessPattern = {
  type: 'pattern',
  src: 'https://example.com/pattern.jpg',
  tileCount: 1  // ✅ TypeScript enforces this!
};

// Union type
const artwork: Artwork = Math.random() > 0.5 ? photo : pattern;

TypeScript will catch mistakes:

// ❌ TypeScript error - missing tileCount
const pattern: SeamlessPattern = {
  type: 'pattern',
  src: 'https://...'
  // Error: Property 'tileCount' is missing
};

Pattern 5: Shop-Wide State Sharing

Share artwork across multiple products:

<Shop>
  {/* Artwork selector shared across all products */}
  <ArtSelector artworks={artworkCollection} />

  <div className="grid grid-cols-2 gap-4">
    {/* Both products use the same selected artwork */}
    <Product productId="shirt-123">
      <ProductImage />
    </Product>
    <Product productId="mug-456">
      <ProductImage />
    </Product>
  </div>
</Shop>

Pattern 6: Error Handling & Edge Cases

Handle missing artwork gracefully:

<Product productId="BEEB77">
  {/* ProductImage without artwork shows "Select artwork to preview" after 2s */}
  <ProductImage />
</Product>

Better: Provide artwork upfront via ArtSelector

<Product productId="BEEB77">
  {/* ✅ Always provide artwork for immediate display */}
  <ArtSelector artworks={[{ type: 'regular', src: 'https://...' }]} />
  <ProductImage />
</Product>

TypeScript Support

Full TypeScript definitions included with type-safe discriminated unions:

import type {
  Artwork,           // Union type: RegularArtwork | SeamlessPattern
  RegularArtwork,    // { type: 'regular', src, aspectRatio? }
  SeamlessPattern,   // { type: 'pattern', src, aspectRatio?, tileCount }
  ImageAlignment,
  ProductCardVariant
} from 'merchify-ui';

// TypeScript enforces tileCount for patterns!
const pattern: SeamlessPattern = {
  type: 'pattern',
  src: 'https://...',
  tileCount: 2  // ✅ Required - won't compile without it
};

// Regular artwork doesn't need tileCount
const artwork: RegularArtwork = {
  type: 'regular',
  src: 'https://...',
  aspectRatio: 1
};

Styling

Components use Tailwind CSS with semantic theme tokens:

// Components respect your theme
<ProductImage className="bg-background text-foreground" />

// Theme tokens:
// - bg-background, text-foreground
// - bg-card, text-card-foreground  
// - bg-primary, text-primary-foreground
// - bg-muted, text-muted-foreground
// - border-border

Tailwind v4 Users: Make sure to configure @source directives:

/* app/globals.css */
@import "tailwindcss";
@source "../app/**/*.{js,ts,jsx,tsx}";
@source "../node_modules/merchify-ui/src/**/*.{js,ts,jsx,tsx}";

Development Mode

Use mock mode to develop without API calls:

<Shop mode="mock">
  {/* Returns mock data - no API needed */}
</Shop>

Shadcn-style Usage

Components ship with source code in the src/ directory. You can:

  1. Use as npm package (recommended)
  2. Copy source - Copy components from node_modules/merchify-ui/src/ to your project and customize freely

Examples

API Reference

See full component docs at: https://docs.merchify.com/components

Support

License

MIT © driuqzy


⚠️ Alpha Release

This is an alpha release (v0.1.0). The API may change as we gather feedback and iterate. We're shipping early to learn from real usage.

Breaking changes are expected pre-1.0. Use semver (npm install merchify-ui@latest) to get updates.


Built for developers. Optimized for LLMs. 🤖

This component library is designed to work seamlessly with AI coding assistants. Each component has:

  • Clear, predictable props
  • Explicit TypeScript types
  • Complete usage examples
  • No magic or hidden behavior

LLMs like Claude, GitHub Copilot, and Cursor can reliably generate correct code using these components.