# Product screen

> A shop's product detail: swipeable gallery with page pills, title, rating and a price that rolls with the size, color swatches or chips, size chips, a quantity stepper, specs, a reviews summary and a bottom bar with the rolling total. When Add to cart succeeds the product photo flies in an arc into the cart button and its count pops.

- Type: Component (screens)
- Access: Pro
- Page: https://goodui.dev/native/components/product-screen
- Markdown: https://goodui.dev/native/components/product-screen.md
- Registry item: https://goodui.dev/native/r/product-screen.json
- Dependencies: react-native-reanimated, react-native-svg
- Keywords: react native product page, ecommerce product detail, expo shop template, add to cart animation, fly to cart, variant picker size color, product image carousel, reviews summary

## When to use

- The product page of a shop, marketplace or drop app: one item with photos, variants and a cart.
- Any catalog item people configure before buying (color, size, quantity).

## When not to use

- Browsing many products: build a grid or list and open this screen per item.
- Services booked by time (a haircut, a class): use the booking screens.
- Digital subscriptions: use paywall-screen.

## Installation

With the shadcn CLI (needs a components.json in the Expo app), then `npx expo install --fix` so native dependencies match your Expo SDK. The foundation comes along the first time.

```bash
npx shadcn@latest add @goodui-native-pro/product-screen
npx expo install --fix
```

Pro: add `"@goodui-native-pro": { "url": "https://goodui.dev/native/r/pro/{name}.json", "headers": { "Authorization": "Bearer ${GOODUI_LICENSE_KEY}" } }` to the `registries` of components.json and put your license key in .env.local.

The component lands in `components/goodui-native/product-screen/` and imports the foundation from `components/goodui-native/foundation/` with relative paths; it also installs avatar, badge, button, carousel, chip, color-picker, icon-button, like-button, list-row, locale, quantity-stepper, rating, rolling-number.

### Setup

- Wrap the app in GestureHandlerRootView and ThemeProvider.

Built-in strings follow `<GooduiLocaleProvider>` (one [language pack](https://goodui.dev/native/r/locale-tr.json) translates the whole kit); the `labels` prop overrides them for one instance.

## Usage

```tsx
// app/product/[id].tsx
import { Stack, router, useLocalSearchParams } from "expo-router";
import { ProductScreen } from "@/components/goodui-native/product-screen/product-screen";
import { useCart } from "@/lib/cart";
import { useProduct } from "@/lib/shop";

export default function ProductRoute() {
  const { id } = useLocalSearchParams<{ id: string }>();
  const product = useProduct(id); // { title, price, currency: "EUR", images, colors, sizes, rating, reviewCount, ... }
  const cart = useCart();
  return (
    <>
      <Stack.Screen options={{ headerShown: false }} />
      <ProductScreen
        product={product}
        cartCount={cart.count}
        onAddToCart={(item) => cart.add(item)} // return the promise; the photo flies in when it resolves
        onCartPress={() => router.push("/cart")}
        onBack={() => router.back()}
        onShare={() => share(product)}
        favorite={product.saved}
        onFavoriteChange={(saved) => api.setSaved(id, saved)}
        onImagePress={(index) => router.push(`/product/${id}/photos?index=${index}`)}
        onSeeAllReviews={() => router.push(`/product/${id}/reviews`)}
      />
    </>
  );
}
```

## Examples

### On its own, with a sample product (photo placeholder, color chips)

```tsx
<ProductScreen onBack={() => router.back()} />
```

### Color swatches that switch the gallery photo

```tsx
colors: [
  { value: "forest", label: "Forest", swatch: "#2F4A3A", imageIndex: 0 },
  { value: "sand", label: "Sand", swatch: "#C9B48A", imageIndex: 2 },
]
```

## API reference

### ProductScreen

`import { ProductScreen } from "@/components/goodui-native/product-screen/product-screen";`

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `product` | `Product` | `defaultProduct` | – |
| `cartCount` | `number` | – | Items in the cart, shown on the cart button (controlled). Without it the screen counts its own adds. |
| `onAddToCart` | `(item: AddToCartItem) => unknown` | `() => new Promise((resolve) => setTimeout(resolve, 700))` | Add to cart. Return a promise: the button shows its states and the item flies into the cart when it resolves. |
| `onBack` | `() => void` | – | Back button. Hidden when omitted. |
| `onCartPress` | `() => void` | – | Cart button in the header. |
| `onShare` | `() => void` | – | Share button in the header. Hidden when omitted. |
| `onFavoriteChange` | `(favorite: boolean) => unknown` | – | Wishlist heart. Return a promise for an optimistic toggle with rollback. |
| `favorite` | `boolean` | – | Wishlist state (controlled). |
| `onImagePress` | `(index: number) => void` | – | Tap on the centered gallery image, for example to open an image viewer. |
| `onSeeAllReviews` | `() => void` | – | "See all reviews". Hidden when omitted. |
| `locale` | `string` | – | BCP 47 locale for prices and dates. Defaults to the device locale. |
| `labels` | `Partial<ProductScreenLabels>` | – | Overrides for the built-in strings; a GooduiLocaleProvider pack sets them app-wide. |
| `style` | `StyleProp<ViewStyle>` | – | Style for the outer container. |
| `testID` | `string` | – | Identifier for end-to-end tests. |

## Accessibility

- The gallery is one adjustable element ('<title> photos', 'Photo 2 of 4, Forest'); swipe up or down, or use arrow keys on the web.
- The title is a header; the wishlist heart, share, back and cart are labelled buttons ('Cart, 2 items').
- Colors are a swatch group named 'Color' with each swatch's name, or a chip group; sizes are a chip group where sold out sizes are disabled and say 'sold out' in their label.
- The quantity stepper is adjustable and named 'Quantity, <title>'; 'Only n left' is visible text.
- Each rating bar is read as '5 stars, 70 percent'. A successful add is announced: 'Added 2 to your cart'.
- The flying photo is hidden from screen readers.

## Motion

- Add to cart: the button gathers into a spinner and opens into 'Added'; then the current photo lifts from the button and flies in an arc (x eases out, y eases in, a little upward throw) into the cart button, shrinking and turning, and the cart badge pops with the new count.
- Price and total roll when the size or quantity changes. Picking a color with an imageIndex glides the gallery to that photo.
- The gallery follows the finger, neighbours shrink and the active pill stretches (Carousel).
- Reduced motion: no flight (the count just updates), digits change in place, the carousel settles on a short timing, swatch rings and chip highlights jump.

## Haptics

- tap on Add to cart, success or error when its promise settles (Button)
- select when a swipe or a pill changes the photo (Carousel)
- select on a color or size change (ColorPicker, ChipGroup); warning at the stepper's limits
- pop on the wishlist heart (LikeButton)

## Performance

- The flight is one absolutely positioned view driven by a single shared value on the UI thread; it mounts only while flying.
- Positions are measured with measureInWindow when the add resolves, so the flight lands on the cart wherever the header puts it.

## Notes for AI

- Pass product.images as require() sources or URLs; without images a placeholder shows and nothing flies (the count still updates).
- Colors render as swatches only when every color has a swatch '#RRGGBB'; otherwise as chips. imageIndex ties a color to a gallery photo.
- sizes[].priceDelta is added to price; soldOut sizes are disabled. stock caps the quantity at min(stock, 10) and shows 'Only n left' at 5 or fewer.
- onAddToCart receives { productId, color, size, quantity, unitPrice } and should return a promise. Pass cartCount to show your real cart count; without it the screen counts its own adds.
- Header buttons appear only for the callbacks you pass: onBack, onShare. Render it full-bleed with the navigator header hidden (headerShown: false).

## Related

- [Cart screen](https://goodui.dev/native/components/cart-screen.md): A shopping cart: line items with a quantity stepper whose minus turns into remove, swipe left to remove or save for later, a promo code field, a free delivery meter and totals that roll with every change above a checkout bar. Removing an item offers Undo; an empty cart shows its own empty state.
- [Carousel](https://goodui.dev/native/components/carousel.md): A snapping card carousel that follows the finger 1:1, shrinks and dims the neighbours, stretches its pagination pill with the drag, and turns that pill into a timer when it autoplays.
- [Color picker](https://goodui.dev/native/components/color-picker.md): Swatches with a selection ring that springs from swatch to swatch while the chosen swatch shrinks inside it, plus an optional pad: drag the thumb over saturation and brightness, slide the hue bar, and the before and after preview follows live.
- [Chip](https://goodui.dev/native/components/chip.md): Filter, choice and input chips: selecting one fills it with the accent while a check draws itself in, removing one shrinks it away, and a ChipGroup in single mode moves one elastic highlight from chip to chip.
- [Quantity stepper](https://goodui.dev/native/components/quantity-stepper.md): A − value + stepper whose value rolls in the direction of change. Hold a button to repeat with acceleration; the ends push back with a rubber-band bounce and a warning haptic. In a cart the − turns into a trash button at the minimum.
- [Rating](https://goodui.dev/native/components/rating.md): Stars (or any symbol) you tap or drag across: the fill sweeps from the old rating to the new one and every symbol it crosses pops on its own beat; half steps optional, and a read-only variant shows exact fractions with a value and a count.
- [Like button](https://goodui.dev/native/components/like-button.md): A like toggle with one moment of delight: the heart squashes and springs back filled, a ring expands and particles burst while the count rolls; it updates optimistically and rolls back if the request fails.

## Guidance for AI tools

Product screen: A shop's product detail: swipeable gallery with page pills, title, rating and a price that rolls with the size, color swatches or chips, size chips, a quantity stepper, specs, a reviews summary and a bottom bar with the rolling total. When Add to cart succeeds the product photo flies in an arc into the cart button and its count pops. Use only the documented props. Keep accessibility, reduced motion and both color schemes intact when adapting it.

Full library index: https://goodui.dev/native/llms.txt
