merchify-ui
v0.1.13
Published
React components for merchandise visualization and customization
Downloads
22
Maintainers
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:
- ProductImage REQUIRES ArtSelector:
<ProductImage />by itself shows placeholder - always use<ArtSelector />first! - Context-only architecture:
<ProductImage />does NOT accept artwork as a prop - use<ArtSelector />to set artwork in context <ArtworkCustomizer />reads from context (set by<ArtSelector>), NOT from props<ArtAlignment>and<TileCount>are NOT exported - use<ArtworkCustomizer>instead- Most components require
<Product>wrapper - check JSDoc for context requirements - 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 initThat's it! The CLI will:
- ✅ Install
merchify-uiandzodpackages automatically - ✅ Auto-detect your project structure
- ✅ Configure Tailwind CSS v4
@sourcepaths correctly - ✅ Verify your setup works
Then restart your dev server:
npm run dev # or pnpm dev, yarn dev, bun devManual Installation (Advanced)
If you prefer manual setup:
npm install merchify-ui zod
# or
yarn add merchify-ui zod
# or
pnpm add merchify-ui zodPeer 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:
<ArtSelector>- Sets artwork in Design context (required for ProductImage to work)<ProductImage>- Reads artwork from context and displays product mockup<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 selectionProductPrice- Formatted pricingThemeToggle- Dark/light modeDragHintAnimation- Drag interaction hintFloatingActionGroup- FAB button group
Composed:
ProductCard- Complete product card (multiple variants)ProductImage- Mockup displayProductOptions- Variant selectorsProductList- Product grid/listProductGallery- Image galleryArtworkCustomizer- Smart artwork controls (positioning/tile density)ArtSelector- Artwork picker galleryAddToCart- Cart buttonLightbox- Full-screen image viewerRealtimeMockup- Live mockup generationCurrentSelectionDisplay- Show selected optionsTileCount- Tile density control (internal - use ArtworkCustomizer instead)
Patterns (Context Providers):
Shop- Root shop providerProduct- Product data provider
Hooks:
useProduct- Access product contextuseShop- Access shop contextuseProductGallery- Gallery state managementusePlacementsProcessor- 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:
ArtworkCustomizerreads artwork from Design context (set byArtSelector)- 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:
ArtSelector→ SetsselectedArtworkin Design contextProductImage→ Reads from context, shows mockupArtworkCustomizer→ 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-borderTailwind 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:
- Use as npm package (recommended)
- 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
- Issues: GitHub Issues
- Discussions: GitHub Discussions
- Docs: docs.merchify.com
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.
