figma guide

Designing product recommendations UI in Figma: rails, bundles, and handoff

Design you-may-also-like carousels, frequently bought together bundles, and personalized recommendation rails in Figma with component variants and Dev Mode specs.

Published
Updated
Jun 23, 2026
Read time
7 min
Level
Beginner

Quick answer

Recommendation UI is a reusable ProductRail with a typed header and a horizontal row of ProductCard instances—never one-off frames per page. Model rail types (related, fbt, recently_viewed, trending) as component properties so engineering can map slots to recommendation APIs. On PDP, stack Related items below reviews and Frequently bought together above the buy box only when the bundle adds a clear ATC shortcut. Reuse product cards and PDP variants; cap rails at 4–8 visible cards with scroll or peek. Pair with cart upsell drawers and empty states when a rail has no results. Start from the Figma guides hub.


Who this is for

  • Product designers shipping DTC stores, marketplaces, and subscription catalogs where discovery drives average order value.
  • Design system teams standardizing cross-sell rails across PDP, cart, checkout, and post-purchase surfaces.
  • Engineers wiring recommendation engines, bundle pricing, carousel analytics, and lazy-loaded shelves.

Recommendation surfaces

SurfaceGoalKey components
PDP — relatedContinue browsing similar itemsProductRail type=related
PDP — FBTIncrease basket sizeBundleRail with checkboxes
Cart drawerLast-minute add-onsProductRail type=cart_upsell
Empty cartRecovery merchandisingProductRail type=trending
Order confirmationPost-purchase cross-sellProductRail type=replenishment
Home / category heroMerchandised shelvesProductRail type=editorial
Search zero resultsAlternative discoveryProductRail type=popular

Verdict: one ProductCard component everywhere—recommendation rails only change header copy, card count, and optional bundle chrome. Do not fork card layouts per rail type.


ProductRail anatomy

PartPurposeSpec tip
HeaderContext + optional link”You may also like” / “View all”
Subhead (optional)Algorithm transparency”Based on items in your cart”
Scroll containerHorizontal shelfPeek next card 16–24px
ProductCard × nMerchandise unitSame as PLP grid
Nav arrows (desktop)Carousel controlDisabled at start/end
Pagination dots (mobile)Position indicatorOptional if swipe-only
ProductRail
├── Variant: type=related | fbt | trending | recently_viewed | cart_upsell
├── Variant: density=compact | standard
├── Variant: breakpoint=desktop | mobile
├── Property: maxVisible (number)
├── Property: showViewAll (boolean)
└── Layers:
    ├── RailHeader (title + optional ViewAllLink)
    ├── RailSubhead (optional)
    ├── ScrollTrack
    │   └── ProductCard × n
    └── CarouselControls (arrows | dots)

Hide the entire rail when itemCount=0—do not render an empty carousel shell. Document fallback: show type=trending or omit section.


Frequently bought together (FBT)

FBT is not a standard product rail—it is a bundle selector with combined pricing:

PartPurposeSpec tip
Primary productAnchor SKUThumbnail + title (read-only)
Checkbox rowsOptional add-onsPre-checked defaults from merchandising
Plus separatorsVisual grouping”+” between items
Bundle priceSum with discountShow savings vs individual
Primary CTAAdd selected to cart”Add 3 items to cart — $89”
Individual linksEscape hatch”View” per item → PDP
BundleRail
├── Variant: layout=horizontal | stacked
├── Property: anchorProductId (string)
├── Property: selectedIds (string[])
└── Layers:
    ├── BundleItems (checkbox + ProductMini × n)
    ├── PriceSummary (total + savings)
    └── AddBundleButton

When only one add-on exists, collapse to a simple “Complete the look” pair row—avoid a heavy bundle UI for two items.


Rail types and copy

TypeHeader exampleWhen to show
related”You may also like”Always on PDP if API returns ≥3
fbt”Frequently bought together”PDP when bundle rules exist
recently_viewed”Recently viewed”Signed-in or cookie-backed
trending”Popular right now”Fallback + homepage
cart_upsell”Customers also bought”Cart with ≥1 line item
replenishment”Buy again”Order history / confirmation
editorialCampaign titleMerch-curated; optional hero link

Keep headers honest—“Picked for you” implies personalization; “Similar items” is safer for rule-based related products.


ProductCard in rails vs PLP

AttributePLP grid cardRail card
WidthFluid columnFixed (160–220px)
Wishlist toggleYesOptional (sm)
Quick addSometimesRare—tap opens PDP
Compare checkboxSometimesUsually hidden
RatingFullCompact stars
PriceFullSingle line

Use ProductCard variant context=rail so image ratio, title lines, and badge placement stay aligned with grid cards.


Cart and checkout upsell

PlacementPatternCaution
Cart below line itemsSingle ProductRailMax 6 cards; do not push totals below fold
Mini cart drawerCompact rail3 cards + horizontal scroll
Checkout (pre-pay)One-line add-onSingle SKU checkbox only—avoid carousel distraction
Post-ATC modalOptional upsellDismissible; respect focus trap

Checkout upsell should never block payment—use optional checkbox add-on, not a full carousel on the payment step.


Loading, empty, and error states

StateVisualBehavior
LoadingSkeleton cards in railReserve rail height to prevent layout shift
Empty APIHide rail entirelyFallback to trending or nothing
Partial (1–2 items)Show rail without carouselLeft-align cards; no arrows
ErrorOmit railSilent fail—log server-side
Stale recently viewedGray “Unavailable” cardRemove slot or replace

Document minimum item threshold (usually 3) before rendering a carousel—two cards look broken with arrow chrome.


Personalization and privacy

ScenarioUXSpec note
Signed-in personalizationSubhead: “Based on your browsing”Link to privacy / ad prefs in footer
Guest cookie railSame copy without nameCookie banner compliance (regional)
Sensitive categoriesSuppress railsHealth, adult—engineering flag
Price in railLive price APIStale price max age annotation

Do not design recommendation UI that implies reading private data (“Because you bought X yesterday”) unless product/legal approves copy.


Accessibility

RequirementImplementation
Carouselaria-roledescription="carousel" on track
ScrollKeyboard arrows move focus between cards
Arrowsaria-label="Next products" / disabled state
Live regionOptional count: “Showing 4 of 12”
MotionRespect prefers-reduced-motion—disable auto-play
FocusVisible focus ring on card links

Cross-check contrast on card scrims with accessibility plugins.


Handoff checklist

ItemDev Mode annotation
Rail type enumMap to API slot names
Min/max itemsRender thresholds
Card componentProductCard variant=rail
Bundle pricingDiscount rule ID + currency
Lazy loadIntersection observer trigger
Analyticsselect_item, view_item_list with item_list_name
Carousel swipeTouch vs arrow-only on desktop
View all URLCategory or dedicated shelf page
Exclude OOSFilter rule for recommendation SKUs

Use Dev Mode checklist and reuse Auto Layout for scroll tracks.


Common mistakes

MistakeWhy it hurtsFix
Unique card per railInconsistent PLP ↔ PDPSingle ProductCard variants
FBT without bundle CTAExtra clicksOne “Add all” with selected checkboxes
Carousel with 2 itemsAwkward arrowsStatic row below threshold
Rails above buy box on mobilePushes ATC downRelated below fold; FBT only if high lift
Fake “personalized” copyTrust issuesMatch copy to actual algorithm
Infinite auto-playAccessibility + annoyanceManual scroll only
OOS items in railDead-end tapsFilter unavailable SKUs

  1. Extend ProductCard with context=rail width and compact rating.
  2. Build ProductRail with type property and header variants.
  3. Add BundleRail for FBT with checkbox rows and bundle CTA.
  4. Place rails on PDP (below reviews), cart, and empty cart.
  5. Design loading skeleton matching card dimensions.
  6. Document thresholds (min items, hide rules, fallbacks).
  7. Prototype carousel scroll and bundle checkbox → price update.
  8. Mobile pass with swipe, peek, and sticky buy box unaffected.

FAQ

Related = similar catalog items. Cross-sell = complementary category (case + screen protector). Upsell = higher-tier version of same product. Use distinct rail types so analytics and APIs stay separate.

How many rails on one PDP?

Cap at two merchandising blocks below the fold—related + FBT or related + reviews-adjacent shelf. More rails feel like ads and hurt conversion.

Add subtle “Sponsored” label on card or rail subhead; design isSponsored boolean on ProductCard. Do not mix sponsored and organic without disclosure.

Same rail in email?

Export card aspect ratio notes for ESP templates—design system should document 2:3 image ratio for rail cards reused in email grids.


Next steps

Share on X

§ Keep reading

Related guides.