All skills
medusajs avatar

/storefront-best-practices

@f08f937 official
by Medusamedusajs/medusa-agent-skills225 stars
29

ALWAYS use this skill when working on ecommerce storefronts, online stores, shopping sites. Use for ANY storefront component including checkout pages, cart, payment flows, product pages, product listings, navigation, homepage, or ANY page/component in a storefront. CRITICAL for adding checkout, implementing cart, integrating Medusa backend, or building any ecommerce functionality. Framework-agnostic (Next.js, SvelteKit, TanStack Start, React, Vue). Provides patterns, decision frameworks, backend integration guidance.

Use this Skill: https://skilld.dev/gh/medusajs/medusa-agent-skills/storefront-best-practices

This session only. Nothing lands on disk.

referencelayoutsproduct-listing.md

≈4k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Product Listing Page Layout

Contents

Overview

Primary browsing interface where users compare products, apply filters, and navigate to product details. Critical for product discovery and conversion.

Key Requirements

  • Responsive product grid (3-4 columns desktop, 2 mobile)
  • Filtering (categories, price, attributes)
  • Sorting options (price, popularity, newest)
  • Pagination, infinite scroll, or load more
  • Results count and active filter indicators
  • Clear "no results" state with suggestions
  • Fast loading and filtering (<1s filter updates)
  • Backend integration for dynamic filtering

Reusable Component Architecture (RECOMMENDED)

Build product listing as a reusable component that works across multiple pages:

✅ Use the same product listing component for:

  • "Shop All" page (all products, no category filter)
  • Category pages (filtered by specific category)
  • Search results page (filtered by search query)
  • Sale/Promotion pages (filtered by discount/promotion)
  • Collection pages (curated product sets)
  • Brand pages (filtered by brand)

Benefits of reusable approach:

  • Single source of truth for product browsing UI
  • Consistent filtering, sorting, and pagination behavior across entire site
  • Easier maintenance (fix bugs once, applies everywhere)
  • Better user experience (familiar interface on every product browsing page)
  • Significantly less code duplication

What to make configurable:

  • Initial filter parameters (category ID, search query, promotion ID, brand, etc.)
  • Page title and breadcrumbs
  • Whether to show filters sidebar (some pages may hide certain filters)
  • Default sort order (category: featured, search: relevance, sale: discount %)
  • Number of products per page
  • Filter options available (hide category filter on category pages, etc.)

Common mistake:

  • ❌ Creating separate components/pages for "Shop All", category pages, and search results with duplicated filtering/sorting/pagination logic
  • ✅ Build one reusable ProductListing component that accepts filter parameters and reuse it across all product browsing pages

Routing Pattern

CRITICAL: Always use dynamic routes for category pages, NEVER static pages.

Category/listing pages must use dynamic routes that accept a parameter (handle, slug, or category ID):

Correct examples:

  • Next.js App Router: app/categories/[handle]/page.tsx
  • Next.js Pages Router: pages/categories/[handle].tsx
  • SvelteKit: routes/categories/[handle]/+page.svelte
  • TanStack Start: routes/categories/$handle.tsx
  • Remix: routes/categories.$handle.tsx

Wrong examples:

  • ❌ pages/categories/women.tsx (static file per category)
  • ❌ pages/categories/men.tsx (doesn't scale)

Fetch category products in the dynamic route based on the handle/ID parameter from the URL.

Decision: Pagination vs Infinite Scroll vs Load More

This is a critical ecommerce decision that affects user experience, SEO, and technical implementation.

Use Pagination When:

User needs:

  • Return to specific result pages
  • Precise control over browsing
  • Professional/research shopping (compare systematically)
  • B2B shoppers (procurement, large orders)

Product characteristics:

  • Position matters (rankings, bestsellers)
  • Large catalog with stable ordering
  • Products require careful comparison

Technical benefits:

  • SEO-friendly (unique URL per page)
  • Better for indexing and crawling
  • Easier back button support
  • Lower memory usage

Implementation:

// URL structure: /products?page=2&category=shirts
// Each page has unique URL for SEO

Best for:

  • Desktop-heavy audience
  • B2B ecommerce
  • Product comparison shopping
  • Catalog with 100+ products

Use Infinite Scroll When:

User needs:

  • Exploratory browsing behavior
  • Mobile-first experience
  • Seamless discovery flow
  • Fashion/visual shopping

Product characteristics:

  • Visual-heavy products (fashion, art, photography)
  • Impulse purchases
  • Discovery-focused (Pinterest-style)

Technical considerations:

  • More complex to implement
  • Requires careful SEO handling (pagination URLs still needed)
  • Higher memory usage (all loaded products stay in DOM)
  • Need to handle browser back button carefully

Implementation:

// Load more when user scrolls to bottom
// Keep pagination in URL for SEO: /products?page=2
// Use Intersection Observer API for detection

Best for:

  • Mobile-first stores (>60% mobile traffic)
  • Fashion, home decor, visual products
  • Younger demographic (18-34)
  • Discovery-focused shopping

Use "Load More" Button When:

Benefits of compromise:

  • User controls when to load (not automatic)
  • Footer remains accessible (important for policies, contact)
  • Better for slower connections (international users)
  • Accessibility friendly (no automatic loading)
  • Lower memory usage than infinite scroll

Implementation:

// Button triggers next page load
// Append products to existing grid
// Show count: "Load 24 More Products"

Best for:

  • International audience (varying connection speeds)
  • Footer content is important (legal, policies, contact)
  • Accessibility concerns with infinite scroll
  • Compromise between pagination and infinite scroll

Hybrid Approach (Recommended):

Combine patterns based on context:

  • Pagination for SEO (canonical URLs)
  • Infinite scroll for UX (on user interaction)
  • Load more for control (user-triggered)

Example:

// Desktop: Pagination at bottom + infinite scroll option
// Mobile: Infinite scroll with pagination URLs for SEO
// All: Preserve scroll position on back button

Decision: Filter Pattern Selection

Sidebar Filters (Desktop)

Use when:

  • Many filter options (5+ categories)
  • Complex product attributes
  • Power users (B2B, professional shoppers)
  • Desktop-heavy traffic

Layout:

  • Left sidebar (250-320px wide)
  • Sticky position (scrolls with page)
  • Collapsible sections (accordion)
  • Apply immediately (no "Apply" button)

Top Filters (Desktop)

Use when:

  • Few filter options (2-4 key filters)
  • Maximize grid space (full-width layout)
  • Simple product categories
  • Visual-first products (fashion)

Layout:

  • Horizontal filter bar above grid
  • Dropdowns or button toggles
  • Limited options (price, category, brand)
  • Compact design

Drawer Filters (Mobile - Always)

Pattern:

  • "Filters" button at top (shows active count badge)
  • Slide-out drawer (full-screen or 80% width)
  • Accordion sections
  • "Apply" button at bottom (batch filtering)
  • "Clear All" option

Why batch filtering on mobile:

  • Prevents multiple re-renders on slow connections
  • User can adjust multiple filters before applying
  • Better mobile UX (less disruptive)

Product Grid Layout

Responsive columns:

  • Large desktop (>1440px): 4 columns
  • Desktop (1024-1440px): 3-4 columns
  • Tablet (768-1024px): 3 columns
  • Mobile (< 768px): 2 columns

Adjust based on product type:

  • Fashion/lifestyle: 3-4 columns (more visible at once)
  • Electronics/detailed: 2-3 columns (larger cards, more detail)
  • Furniture/large items: 2-3 columns (showcase details)

Product card essentials:

  • Product image (primary)
  • Title (truncated to 2 lines)
  • Price (Medusa: display as-is, don't divide by 100)
  • Optional: Rating, badges, wishlist
  • See product-card.md for detailed guidelines

Grid spacing:

  • 16-24px gap (desktop)
  • 12-16px gap (mobile)
  • Equal height rows (optional, improves visual consistency)

Filtering Strategy

Filter Types by Purpose

Category filters:

  • Multi-select checkboxes
  • Hierarchical (parent-child categories)
  • Show product count per category
  • Example: "Shirts (24)" "T-Shirts (12)"

Price range filter:

  • Range slider (drag min/max)
  • Or: Predefined ranges ("$0-$50", "$50-$100")
  • Update dynamically as products filtered
  • Show min/max from current results

Attribute filters (Size, Color, Brand):

  • Multi-select checkboxes
  • Visual swatches for colors
  • Show available options based on current filters
  • Gray out unavailable combinations

Availability filters:

  • "In Stock" checkbox
  • "On Sale" checkbox
  • "New Arrivals" checkbox
  • Single purpose, clear value

Filter Behavior

Filter persistence:

  • Save in URL parameters (shareable, bookmarkable)
  • Example: /products?category=shirts&price=0-50&color=blue
  • Restore filters on page reload
  • Clear all filters should reset URL

Active Filters Display

Show active filters:

  • Above product grid
  • Pill/tag format: "Blue ✕" "Under $50 ✕"
  • Click X to remove individual filter
  • "Clear All" link to remove all filters
  • Count: "3 filters active"

Sorting Strategy

Common Sort Options

Essential options:

  • Featured (default): Store's recommended order (bestsellers, promoted)
  • Price: Low to High: Budget-conscious shoppers
  • Price: High to Low: Premium product seekers
  • Newest: Fashion, tech, time-sensitive products
  • Best Selling: Social proof, popular choices
  • Top Rated: Quality-focused shoppers

Advanced options:

  • Name: A-Z (alphabetical)
  • Discount: Highest % off (sale hunters)
  • Reviews: Most reviewed (validation seekers)

Sort Implementation

Display:

  • Dropdown above product grid (right-aligned)
  • Label: "Sort by:" or just dropdown
  • Update products immediately on selection
  • Show current sort in URL: /products?order=-created_at

Backend integration:

  • Pass sort parameter to API (check backend docs for parameter name)
  • Common parameters: order, sort, sort_by
  • Common values: -created_at (desc), +price (asc), -price (desc)

Preserve filters:

  • Sorting doesn't clear filters
  • Maintains all active filters
  • Updates URL with sort parameter

Backend Integration

Fetching Products

Query parameters to include:

  • Category/collection filter (if applicable)
  • Pagination (limit, offset or cursor)
  • Sort order
  • Filter values (price, attributes, etc.)
  • For Medusa: region_id (required for correct pricing)

Check backend API documentation for exact parameter names and formats.

Available Filters

Dynamic filter updates:

  • Show only relevant filters for current category
  • Display product count per filter option
  • Gray out options with 0 products
  • Update available options when filters change

URL State Management

Filter URL structure pattern: /products?category_id=123&order=-created_at&page=2&price=0-50

Benefits:

  • Shareable links
  • Bookmarkable searches
  • Browser back/forward works correctly
  • SEO-friendly (crawlable filter combinations)

Implementation approach:

  • Read filters from URL query parameters on page load
  • Update URL when filters change using URLSearchParams and history.pushState
  • Parse URL parameters to reconstruct filter state

Empty and No Results States

No Products in Category

When category is empty:

  • Message: "No products available yet"
  • Subtext: "Check back soon for new arrivals"
  • CTA: "Browse all products" or "Go to home"
  • Alternative: Show related categories
  • Optional: Newsletter signup for notifications

No Results from Filters

When filters too restrictive:

  • Message: "No products match your filters"
  • Subtext: "Try removing some filters or adjusting your criteria"
  • Prominent "Clear All Filters" button
  • Show which filters might be too restrictive
  • Suggestions: "Try expanding price range" or "Remove brand filter"

Example:

No products found

You filtered by:
- Color: Blue
- Size: XXL
- Price: $0-$20

Try:
• Removing size filter (only 2 XXL products)
• Expanding price range
• [Clear All Filters]

No Results from Search

When search query returns nothing:

  • Message: "No results for '[query]'"
  • Suggestions: Check spelling, try different keywords
  • CTA: Browse popular categories
  • Show search suggestions (similar queries)
  • Display popular or trending products

Performance Optimization

Lazy Loading Images

Implementation:

  • Load images as they come into viewport
  • Use Intersection Observer API
  • Show placeholder or blur-up effect
  • Improves initial page load significantly

Critical for ecommerce:

  • Product listings have 24-100+ images per page
  • Lazy loading reduces initial load by 60-80%
  • Faster perceived performance

Virtual Scrolling (Advanced)

When to use:

  • Very large catalogs (500+ products visible)
  • Infinite scroll with memory concerns
  • Performance issues with many DOM elements

How it works:

  • Only render visible products + buffer
  • Reuse DOM elements as user scrolls
  • Maintains scroll position
  • Libraries: react-window, react-virtuoso

Tradeoff:

  • Complex implementation
  • Better performance for large lists
  • Required for catalogs with 1000+ products loaded

Filter Performance

Optimistic UI:

  • Update grid immediately (predicted results)
  • Show loading overlay briefly
  • Replace with real results
  • Better perceived performance

Mobile Optimization

Critical mobile patterns:

2-column grid:

  • Maximum 2 products per row
  • Larger touch targets
  • Simplified cards (essential info only)
  • Remove hover effects

Filter drawer:

  • Full-screen or 80% width drawer
  • "Filters" button with badge count
  • Batch apply (don't re-fetch on each change)
  • Clear all at top

Sticky filter/sort bar:

  • Fixed at top while scrolling
  • Quick access to filters and sorting
  • Shows active filter count
  • Higher engagement rates

Infinite scroll default:

  • Better mobile UX than pagination
  • Natural scrolling behavior
  • Keep pagination URLs for SEO
  • Handle back button correctly

Performance:

  • Lazy load images (critical on mobile)
  • Limit initial products (12-24)
  • Optimize image sizes for mobile
  • Fast filter updates (<1s)

Checklist

Essential product listing features:

  • RECOMMENDED: Product listing built as reusable component
  • Reusable component works for: shop all, category pages, search results, sale pages
  • Component accepts filter parameters (categoryId, searchQuery, promotionId, etc.)
  • Responsive grid (3-4 columns desktop, 2 mobile)
  • Decision made: Pagination vs infinite scroll vs load more
  • Filter pattern selected: Sidebar (desktop) vs drawer (mobile)
  • Filters fetched from backend dynamically
  • Filter options show product count
  • Active filters displayed above grid (removable pills)
  • "Clear all filters" button prominent
  • Sorting options (featured, price, newest, bestselling)
  • Sort updates products without clearing filters
  • Filters and sort persist in URL (shareable)
  • Results count displayed ("Showing 1-24 of 156 products")
  • Empty state: "No products match filters" with suggestions
  • "Clear all filters" prominent when no results
  • Product prices displayed correctly (Medusa: as-is, not divided)
  • Lazy loading for images (Intersection Observer)
  • Loading state for filter changes (< 1s)
  • Mobile: Filter drawer with batch apply
  • Mobile: 2-column grid maximum
  • Mobile: Sticky filter/sort button
  • Pagination URLs for SEO (even with infinite scroll)
  • Back button support (restore filters, scroll position)
  • Keyboard accessible (tab through filters, enter to apply)
  • ARIA labels on filters (role="group", aria-label)

Source: SKILL.md on GitHub

1 warning16d5 checks · Risk SAFE
  • Gen Agent Trust Hub16d

    This skill consists entirely of comprehensive reference documentation and design patterns for building ecommerce storefronts with a focus on Medusa backend integration. It contains no executable scripts, code, or security risks.

  • Socket16d

    No alerts

  • Snyk16d

    Risk: LOW · No issues

  • Runlayer7mo

    25/28 files flagged

  • ZeroLeaks5mo

    Score: 93/100 · 2 sections analyzed

Signed by skilld at f08f937. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 3 days ago.

Activeupdated 8 months ago

README badge

README badge for medusajs/medusa-agent-skills/storefront-best-practices