E-commerce blocks for shadcn/ui, organised by the purchase funnel — product cards and grids, URL-driven filters, sort, load more, quick view, empty states and the order summary.
The ecomcn house style — warm paper, near-black ink, a single vermilion signal colour, and a 2px radius so nothing reads as a rounded card. Ships the complete shadcn token set plus --brand, --sale and --success.
A no-match state that names the filters that emptied the grid, offers the single change that brings the most back, and lets shoppers remove filters one at a time. Left-aligned, never centred.
Faceted filters with state that lives in the URL: applied-filter chips, checkbox lists with counts, a dual-handle range with typed inputs, colour swatches and toggles. Compound: one tag for the default layout, or FilterPanelHeader, FilterPanelChips and FilterPanelFacet in any order via useFilterPanel(). Ships a pure parser for server components and a useFilterParams hook.
The filter panel in a slide-over for small screens. Changes are staged and applied once — on Show or on close — with a live result count on the button and focus returned to the trigger. Accepts the same FilterPanel parts as children, so a custom layout stages too.
Progressive loading for a product list: a progress rule and “Showing 24 of 312” count, a Load more button, optional hybrid or infinite auto-loading, focus moved to the first new item, and an announced count. The pattern Baymard found performs best, in one component.
Free-shipping progress, promo code field with applied state, itemised totals and the checkout CTA. Totals logic lives in a separate hook you can test.
Locale-aware price with compare-at strike, computed discount badge and a screen-reader announcement. No dependencies.
The buy box: brand, name, a rating that links to the reviews, the price (the variant's own when it has one), stock-aware variant swatches, a quantity stepper, and an add-to-bag button that carries the live total — "Add to bag — $840". Adding is optimistic and rolls back with an inline message if your handler rejects. A missing size is asked for, not greyed out; a sold-out pick becomes "Notify me". Delivery is a date range from business days and a dispatch cut-off, never "ships soon". Labels, price and notices morph with Motion (a fade under reduced motion). Compound: the selected variant lives in one context, read through useProductBuyBox().
Catalogue tile with badges, compare-at pricing, colour swatches that swap the image, rating, a keyboard-reachable quick-add, and a wishlist heart under the badge (your shadcn Toggle, optimistic). Colours can be hidden for products that don't come in any. Compound: use it as one tag, or recompose it from ProductCardMedia, ProductCardBody and their parts via useProductCard().
Materials, care, shipping and maker sections on your shadcn Accordion, first section open. Each closed row carries a one-line summary, so shoppers get the answer without opening it, and panels open with a Motion height morph. One tag from data, or composed from sections and a specs list; open state is shared through context. Works on Radix and Base UI.
The product's photos: a main image on your shadcn Carousel with a counter, and thumbnails beside it on a desktop and under it on a phone (scrolling without a scrollbar). Swipe on touch; with a mouse the photo zooms where the pointer is. Pressing the photo opens it full screen on your shadcn Dialog — the screen grows out of the photo and folds back into it (Motion; a fade under reduced motion) — which is also the zoom for touch and the keyboard. The active thumbnail's frame slides to the next; the rest sit at 60%. The first photo loads with priority, the second early, the rest when they are next. Images are data rendered as <img>, or through your renderImage (next/image, say). Compound, through useProductGallery().
Responsive catalogue grid with box-matched loading skeletons, appended skeletons while more results load, a density switch, a renderCard slot for composed cards, and a left-aligned empty state.
Quick view with a shared-element morph: the card image grows into the dialog and shrinks back on close. Motion's layoutId drives the morph, a native <dialog> does the modal work, focus returns to the trigger, and reduced-motion users get no movement. The one ecomcn block that depends on motion — opt-in.
A "pairs well with" rail of the exact product card on your shadcn Carousel, with the Carousel's own previous / next buttons in the header and a progress rule under it in place of a scrollbar. Below the fold by nature, so nothing in it loads with the page: the cards and their images mount when the rail comes within a screen of the viewport, fading in over skeletons of exactly their shape (Motion). Compound, through useRelatedProducts().
The reviews section: the average set large, a star histogram whose rows are buttons that filter the list (and read out as "5 stars, 226 reviews, 72%"), a fit scale with its reading in words, and reviews with a Verified buyer badge. The active row's rule slides between rows, bars grow to their share, and the list grows to its measured height as it filters — Motion, a fade under reduced motion. Takes a page of reviews plus overall counts, and reports the filter so you can fetch server-side. Compound, through useReviewSummary().
A measurement table in your shadcn Dialog that morphs out of a quiet link beside the size label, and back into it (Motion; a fade under reduced motion). Centimetres and inches with tape-honest rounding, the shopper's selected size marked and announced, ranges and size equivalents (EU 38) in the same table, and a size column that stays put while the measurements scroll sideways on narrow screens. Composed from parts that share the unit through context.
The rule above a listing: a result count announced to screen readers once results settle, a native sort select, a density switch and a slot for the mobile filter trigger.
Colour swatches and a ruled size grid that work out stock from your variants: with a colour picked, sizes it has none of are struck through with a diagonal rule and read out as sold out, yet stay focusable and choosable — for a notify-me — while a live status line says why they can't be bought. Low stock shows under the size and in the status line. The selection mark slides between options with Motion (a fade under reduced motion). A radio group with roving focus; compound parts share the selection, the resolved variant and whether it can be bought through useVariantSwatches().