# Storefront Page

> A React ecommerce store template: free shipping bar, hero, collections, a filterable product grid with quick add, a cart drawer and reviews.

- Type: React template (Templates)
- Access: Pro (the source needs a Wingo UI Pro plan, see https://wingo-ui.com/pricing)
- Version: 1.0.0
- Install: `npx wingo-ui@latest add storefront-page`
- Docs: https://wingo-ui.com/templates/storefront-page

## Interaction

A small brand's store home page. The strip on top states the free shipping threshold and can be closed. Shop the collection glides down to the grid; a collection tile filters the grid to that collection. In the grid, the collection chips (a swipeable row on phones) filter live and the count is announced; from 1024px On sale, In stock, a price select and a sort select sit beside them, below 1024px Filters opens a sheet (a bottom sheet on phones) with sort, price and availability chips and Show products. Products are two per row on phones. Every card is one link that lifts on hover; its swatches (arrow keys inside the group) recolor the drawn photo, the heart saves it, and Add to cart puts that color in the cart and opens the cart drawer, a side panel on desktop and a bottom drawer on phones, with quantity steppers, swipe or undo to remove, a free shipping progress bar and checkout. The featured product band has a gallery (thumbnails, swipe, a full-screen viewer), color swatches that redraw the photos, a size select, a quantity stepper and Add to cart; on phones a bar with Add to cart slides up from the bottom while that button is off screen. Reviews filter by star and sort; the newsletter form validates the email and confirms.

## Installation

```bash
npx wingo-ui@latest add storefront-page
```

Pro item: run `npx wingo-ui@latest login` once with a Pro account. The CLI adds the files, their dependencies and a version record for updates.

With shadcn: `npx shadcn@latest add @wingo-ui/storefront-page` (needs the @wingo-ui registry with your API key in components.json)

Files:

- `components/templates/storefront-page.tsx`
- `components/templates/storefront-page-data.ts`

npm dependencies: `npm i lucide-react motion`

Wingo UI dependencies (installed automatically):

- [Marketing Section](https://wingo-ui.com/components/marketing-section) ([Markdown](https://wingo-ui.com/components/marketing-section.md))
- [Marketing Header](https://wingo-ui.com/components/marketing-header) ([Markdown](https://wingo-ui.com/components/marketing-header.md))
- [Marketing Footer](https://wingo-ui.com/components/marketing-footer) ([Markdown](https://wingo-ui.com/components/marketing-footer.md))
- [Newsletter Section](https://wingo-ui.com/components/newsletter-section) ([Markdown](https://wingo-ui.com/components/newsletter-section.md))
- [Banner](https://wingo-ui.com/components/banner) ([Markdown](https://wingo-ui.com/components/banner.md))
- [Button](https://wingo-ui.com/components/button) ([Markdown](https://wingo-ui.com/components/button.md))
- [Cart drawer](https://wingo-ui.com/components/cart-drawer) ([Markdown](https://wingo-ui.com/components/cart-drawer.md))
- [Favorite button](https://wingo-ui.com/components/favorite-button) ([Markdown](https://wingo-ui.com/components/favorite-button.md))
- [Price](https://wingo-ui.com/components/price) ([Markdown](https://wingo-ui.com/components/price.md))
- [Product gallery](https://wingo-ui.com/components/product-gallery) ([Markdown](https://wingo-ui.com/components/product-gallery.md))
- [Quantity stepper](https://wingo-ui.com/components/quantity-stepper) ([Markdown](https://wingo-ui.com/components/quantity-stepper.md))
- [Rating](https://wingo-ui.com/components/rating) ([Markdown](https://wingo-ui.com/components/rating.md))
- [Reviews](https://wingo-ui.com/components/reviews) ([Markdown](https://wingo-ui.com/components/reviews.md))
- [Select](https://wingo-ui.com/components/select) ([Markdown](https://wingo-ui.com/components/select.md))
- [Sheet](https://wingo-ui.com/components/sheet) ([Markdown](https://wingo-ui.com/components/sheet.md))
- [useControllableState](https://wingo-ui.com/components/use-controllable-state) ([Markdown](https://wingo-ui.com/components/use-controllable-state.md))
- [Navigation](https://wingo-ui.com/components/navigation) ([Markdown](https://wingo-ui.com/components/navigation.md))
- [Motion presets](https://wingo-ui.com/components/motion) ([Markdown](https://wingo-ui.com/components/motion.md))
- [Tones](https://wingo-ui.com/components/tones) ([Markdown](https://wingo-ui.com/components/tones.md))
- [UIProvider](https://wingo-ui.com/components/ui-config) ([Markdown](https://wingo-ui.com/components/ui-config.md))

## Usage

```tsx
// app/page.tsx
"use client"

import Link from "next/link"
import { StorefrontPage, type StorefrontAddToCart } from "@/components/templates/storefront-page"
import type { CartItem } from "@/components/ui/cart-drawer"

async function addToCart(item: StorefrontAddToCart) {
  await fetch("/api/cart", { method: "POST", body: JSON.stringify({ line: item.lineId, quantity: item.quantity }) })
}

async function checkout(items: CartItem[]) {
  const response = await fetch("/api/checkout", { method: "POST", body: JSON.stringify(items) })
  const { url } = await response.json()
  window.location.assign(url)
}

export default function HomeRoute() {
  return <StorefrontPage linkComponent={Link} freeShippingThreshold={50} onAddToCart={addToCart} onCheckout={checkout} />
}
```

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `products?` | `StorefrontProduct[]` | `"STOREFRONT_PRODUCTS"` | { id, title, collection, price, compareAtPrice?, colors: { name, value }[], kind, images?, sizes?, rating?, reviewCount?, badge?, addedAt?, rank?, description?, maxQuantity?, soldOut?, href?, favorite? }. kind picks the drawn photo (notebook, pocket, planner, pen, mug, pouch) in the chosen color; images (one per color) replace it. Quick add uses the first size. |
| `collections?` | `StorefrontCollection[]` | `"STOREFRONT_COLLECTIONS"` | { id, title, description?, kind, color, image?, href? } tiles and grid chips; a product joins one by its collection id. An empty array hides the tiles. |
| `featured?` | `StorefrontFeatured \| null` | `"STOREFRONT_FEATURED"` | A product (the StorefrontProduct shape) plus eyebrow and highlights, shown in the band with the gallery, swatches, size select and Add to cart. null hides it. |
| `perks?` | `StorefrontPerk[]` | `"STOREFRONT_PERKS"` | { icon?, title, description } tiles: shipping, returns, guarantee. An empty array hides them. |
| `reviews?` | `ReviewData[]` | `"STOREFRONT_REVIEWS"` | The reviews of the featured product, in the Reviews shape. An empty array hides the section. |
| `reviewsSummary?` | `ReviewsSummaryData` | `"STOREFRONT_REVIEWS_SUMMARY"` | { average, count, distribution, aspects? } when reviews is one page of many. |
| `priceRanges?` | `StorefrontPriceRange[]` | `"STOREFRONT_PRICE_RANGES"` | The price filter choices as [min, max) pairs in major units; null is an open end. Any price is always first. |
| `labels?` | `Partial<StorefrontPageLabels>` |  | Every string on the page: brand, announcement, hero, section headings, filters, card and cart copy, newsletter, footer. Some are functions (announcement, productCount, quickAddLabel, featuredNote, photo). |
| `currency?` | `string` | `"USD"` | ISO currency of every price, the threshold and the cart. |
| `locale?` | `string` | `""` | Intl locale for prices and counts; empty uses the UIProvider's, else en-US. |
| `freeShippingThreshold?` | `number` | `50` | The order total for free shipping: the announcement strip, the note under Add to cart and the cart's progress bar. 0 hides all three. |
| `shipping?` | `number \| null` | `6` | The flat delivery price under the threshold, in the note under Add to cart and the cart summary; null prints Calculated at checkout. |
| `defaultCollection?` | `string` | `""` | The collection id the grid starts on; empty is All. |
| `defaultSort?` | `"featured" \| "newest" \| "price-asc" \| "price-desc" \| "rating"` | `"featured"` | The grid's starting order. featured follows rank with sold out items last. |
| `cartItems?` | `CartItem[]` |  | The cart, controlled. Quick add and Add to cart append or bump a line (id product:color:size). |
| `defaultCartItems?` | `CartItem[]` |  | The starting cart when it is not controlled; empty by default. |
| `onCartItemsChange?` | `(items: CartItem[]) => void` |  | Every cart change: adds, quantity steps and removals. |
| `cartOpen?` | `boolean` |  | The cart drawer, controlled. |
| `defaultCartOpen?` | `boolean` | `false` | Starts with the cart drawer open. |
| `onCartOpenChange?` | `(open: boolean) => void` |  | The drawer opened or closed. |
| `onAddToCart?` | `(item: StorefrontAddToCart) => void \| Promise<unknown>` |  | { product, color?, size?, quantity, lineId }. A returned promise keeps the button loading; a rejection adds nothing and announces labels.addFailed. |
| `onCheckout?` | `(items: CartItem[]) => void \| Promise<unknown>` |  | The cart's checkout button; a promise keeps it loading. Without it the button links to cartProps.checkoutHref (default /checkout). |
| `onFavoriteChange?` | `(product: StorefrontProduct, favorite: boolean) => void \| Promise<unknown>` |  | A heart was toggled; a rejection rolls it back. |
| `onSubscribe?` | `(email: string) => void \| Promise<unknown>` |  | The newsletter form; without it a 900 ms pretend, then the success line. |
| `columns?` | `3 \| 4` | `4` | Products per row from 1024px; three from 768px, two on phones. |
| `tone?` | `"neutral" \| "brand" \| "success" \| "warning" \| "danger" \| "info"` | `"neutral"` | Accents: the rating stars, the review bars and filters. |
| `ctaTone?` | `"neutral" \| "brand" \| "success" \| "warning" \| "danger" \| "info"` | `"neutral"` | Shop the collection, Add to cart, the sticky bar, checkout and the newsletter button. |
| `favoriteTone?` | `"neutral" \| "brand" \| "success" \| "warning" \| "danger" \| "info"` | `"neutral"` | The heart on a saved product; neutral is white on the photo. |
| `saleTone?` | `"neutral" \| "brand" \| "success" \| "warning" \| "danger" \| "info"` | `"danger"` | Sale prices, the discount chip and the Sale tag on the photo (a markdown is a status). |
| `radius?` | `"none" \| "sm" \| "md" \| "lg"` | `"lg"` | Corners of the product, collection, buy box and perk cards: 0 / 12 / 16 / 24px; photos inside follow. |
| `background?` | `"plain" \| "soft" \| "glow" \| "aurora" \| "grid" \| "dots"` | `"soft"` | The main background: plain is the bare page color; the others tint the page and add a wash, a glow, an aurora, a grid or dots over the first screen. |
| `backgroundColor?` | `string` | `"oklch(0.72 0.07 70)"` | The tint of the main background, a warm sand by default. |
| `bandBackground?` | `"muted" \| "tinted" \| "gradient" \| "dots"` | `"muted"` | The secondary background of every second section (collections, the featured product, perks). Cards inside are lifted above it. |
| `bandColor?` | `string` | `"oklch(0.72 0.07 70)"` | The tint of tinted and gradient bands; left equal to backgroundColor it follows it. |
| `showAnnouncement?` | `boolean` | `true` | The free shipping strip above the header (needs freeShippingThreshold above 0). |
| `showCollections?` | `boolean` | `true` | The collection tiles. |
| `showFeatured?` | `boolean` | `true` | The featured product band. |
| `showReviews?` | `boolean` | `true` | The reviews with the rating summary. |
| `showPerks?` | `boolean` | `true` | The shipping, returns and guarantee tiles. |
| `showNewsletter?` | `boolean` | `true` | The newsletter signup. |
| `stickyAddToCart?` | `boolean` | `true` | Below 768px, a bar with Add to cart slides up while the featured product is on screen and its own button is not. |
| `header?` | `boolean` | `true` | The sticky header with the brand, section links, account and the cart button. Off, the cart button moves to the grid's toolbar. |
| `footer?` | `boolean` | `true` | The footer with Shop, Help and Company links. |
| `headerProps?` | `Partial<MarketingHeaderProps>` |  | Anything for MarketingHeader (layout, variant, items...); wins over the page's own values. |
| `footerProps?` | `Partial<MarketingFooterProps>` |  | Anything for MarketingFooter (variant, columns, socials, newsletter...). |
| `bannerProps?` | `Partial<BannerProps>` |  | Anything for the announcement Banner (tone, variant, persistKey, action...). |
| `galleryProps?` | `Partial<ProductGalleryProps>` |  | Anything for the featured ProductGallery (thumbnails, zoom, aspect...). |
| `reviewsProps?` | `Partial<ReviewsProps>` |  | Anything for Reviews (layout, onWriteReview, hasMore, onLoadMore...). |
| `newsletterProps?` | `Partial<NewsletterSectionProps>` |  | Anything for the NewsletterSection (layout, bullets, subscribers...). |
| `cartProps?` | `Partial<CartDrawerProps>` |  | Anything for the CartDrawer (showCoupon, couponProps, taxRate, note, size, checkoutHref...); items and open stay with the page, and onCheckout wins over checkoutHref. |
| `animateIn?` | `boolean` | `true` | The hero rises in once when the page mounts on the client; never during hydration, under reduced motion or in thumbnails. |
| `linkComponent?` | `(props: LinkComponentProps) => ReactNode` |  | The router link for the product, header and footer links (next/link in an app). |
| `className?` | `string` |  | Classes for the root. |
| `classNames?` | `Partial<Record<StorefrontPageSlot, string>>` |  | Classes for announcement, header, hero, collections, collection, shop, toolbar, filters, grid, product, photo, featured, gallery, buyBox, stickyBar, reviews, perks, newsletter, cart and footer. |

## Parts

### STOREFRONT_PAGE_SAMPLE (constant)

All the sample content in one object: products, collections, featured, perks, reviews and the summary.

### STOREFRONT_PRODUCTS (constant)

Eight Fieldnote products: notebooks, a pocket set, a planner, pens, a mug and a pouch, two on sale and one sold out.

### STOREFRONT_COLLECTIONS (constant)

Notebooks, Planners, Writing and Desk goods.

### STOREFRONT_FEATURED (constant)

The Field Notebook with three sizes, four colors and three highlights.

### STOREFRONT_PERKS (constant)

Free shipping, 60-day returns, a binding guarantee and plastic-free packaging.

### STOREFRONT_REVIEWS (constant)

Six Field Notebook reviews, one with a reply from the shop.

### STOREFRONT_REVIEWS_SUMMARY (constant)

4.8 from 1,284 reviews with the star counts and three topics.

### STOREFRONT_PRICE_RANGES (constant)

Under $20, $20 to $40 and $40 and up.

### DEFAULT_STOREFRONT_PAGE_LABELS (constant)

The English strings.

### drawStorefrontProduct (function)

(kind, color, view?) returns an svg data url of a drawn product shot (views front, open, angle, detail), for an img, a gallery or a cart line image.

### StorefrontPageProps (type)

Props of StorefrontPage, including the attributes of the root element.

### StorefrontPageLabels (type)

Every string of the page.

### StorefrontPageSlot (type)

The keys of classNames.

### StorefrontPageRadius (type)

"none" | "sm" | "md" | "lg".

### StorefrontPageSort (type)

"featured" | "newest" | "price-asc" | "price-desc" | "rating".

### StorefrontPageColumns (type)

3 | 4.

### StorefrontPriceRange (type)

[min, max) in major units; null is an open end.

### StorefrontArtView (type)

"front" | "open" | "angle" | "detail".

### StorefrontAddToCart (type)

What onAddToCart receives: { product, color?, size?, quantity, lineId }.

### StorefrontProduct (type)

One product of the grid.

### StorefrontProductKind (type)

"notebook" | "pocket" | "planner" | "pen" | "mug" | "pouch": the drawn shots.

### StorefrontColor (type)

{ name, value }: a swatch and the color of the drawn shot.

### StorefrontSize (type)

{ value, label, description?, price? }: a size option, its own price if it differs.

### StorefrontCollection (type)

One collection tile.

### StorefrontFeatured (type)

A StorefrontProduct plus eyebrow and highlights.

### StorefrontPerk (type)

{ icon?, title, description }.

## Examples

- **Brand accents**: ctaTone and favoriteTone on an aurora page with tinted bands: orange Add to cart, checkout and newsletter buttons, red hearts.
- **A returning shopper**: defaultCartItems with two lines and the drawer open: the free shipping bar, steppers and the summary.
- **A lean catalog**: Three columns, square-ish cards, starting on Notebooks sorted by price, without collections, reviews or perks.
- **When the cart fails**: onAddToCart rejects: the button spins, nothing lands in the cart and the failure is announced.
- **Inside a site layout**: No header, footer or announcement, on a plain page with dotted bands: the cart button moves to the grid's toolbar.

## See also

- [Cart drawer](https://wingo-ui.com/components/cart-drawer) ([Markdown](https://wingo-ui.com/components/cart-drawer.md))
- [Product gallery](https://wingo-ui.com/components/product-gallery) ([Markdown](https://wingo-ui.com/components/product-gallery.md))
- [Reviews](https://wingo-ui.com/components/reviews) ([Markdown](https://wingo-ui.com/components/reviews.md))
- [Checkout Page](https://wingo-ui.com/components/checkout-page) ([Markdown](https://wingo-ui.com/components/checkout-page.md))
- [Landing Page](https://wingo-ui.com/templates/landing-page) ([Markdown](https://wingo-ui.com/templates/landing-page.md))

---

Canonical: https://wingo-ui.com/templates/storefront-page
More components: https://wingo-ui.com/components · Index for agents: https://wingo-ui.com/llms.txt
