UNPKG

merchify-ui

Version:

React components for merchandise visualization and customization

664 lines (507 loc) 19.5 kB
# 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:** ```tsx // 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:** ```bash 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:** ```bash npm run dev # or pnpm dev, yarn dev, bun dev ``` --- ### Manual Installation (Advanced) If you prefer manual setup: ```bash 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! ```tsx 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:** ```tsx // 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. ```tsx <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):** ```bash 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. ```tsx <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:** ```tsx // ✅ 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. ```tsx <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! ```tsx // 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 ```tsx <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 ```tsx 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. ```tsx // ❌ WRONG - ProductImage alone doesn't work! <Product productId="BEEB77"> <ProductImage /> {/* Shows placeholder - no artwork! */} </Product> ``` **Solution:** ALWAYS use ArtSelector before ProductImage: ```tsx // ✅ 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. ```tsx // ❌ WRONG - These components are not exported! import { ArtAlignment, TileCount } from 'merchify-ui'; // ❌ Will fail! ``` **Solution:** Use `ArtworkCustomizer` instead - it handles both types automatically: ```tsx // ✅ 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 ```tsx 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:** ```tsx 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) ```tsx // ✅ 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:** ```tsx 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:** ```tsx // ❌ 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:** ```tsx <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:** ```tsx <Product productId="BEEB77"> {/* ProductImage without artwork shows "Select artwork to preview" after 2s */} <ProductImage /> </Product> ``` **Better: Provide artwork upfront via ArtSelector** ```tsx <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**: ```typescript 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: ```tsx // 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: ```css /* 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: ```tsx <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 - [Next.js Full Example](https://github.com/merchify-dev/ui-components/tree/main/examples/next-js) - [Product Card Demo](https://github.com/merchify-dev/ui-components/tree/main/examples/next-js/app/demos/product-card) - [Art Alignment Demo](https://github.com/merchify-dev/ui-components/tree/main/examples/next-js/app/demos/art-alignment) ## API Reference See full component docs at: [https://docs.merchify.com/components](https://docs.merchify.com/components) ## Support - **Issues:** [GitHub Issues](https://github.com/merchify-dev/ui-components/issues) - **Discussions:** [GitHub Discussions](https://github.com/merchify-dev/ui-components/discussions) - **Docs:** [docs.merchify.com](https://docs.merchify.com) ## License MIT © [driuqzy](mailto:driuqzy@gmail.com) --- **⚠️ 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.