> ## Documentation Index
> Fetch the complete documentation index at: https://docs.platform.decart.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# E-commerce Virtual Try-On

> Add a 'Try it on' button to product pages with realtime virtual try-on

Let shoppers see how clothing looks on them in realtime, using just a webcam. This walkthrough covers the key integration points for adding virtual try-on to an e-commerce product page — from creating secure client tokens to sending garment images with descriptive prompts.

<Info>
  This example uses Next.js, but the same `@decartai/sdk` works with any JavaScript framework.
</Info>

**Source code:** [github.com/DecartAI/tryon-examples](https://github.com/DecartAI/tryon-examples) — includes six production-ready examples (e-commerce, standalone, digital mirror, outfit builder, and more).

## What you'll build

<CardGroup cols={3}>
  <Card title="Try-on button" icon="shirt">
    A "Try it on" button on product pages that opens a realtime try-on modal with the user's webcam.
  </Card>

  <Card title="Instant garment swap" icon="arrows-rotate">
    Switch between products without reconnecting — each click sends a new garment image and prompt.
  </Card>

  <Card title="Secure tokens" icon="lock">
    Your permanent API key stays on the server. The browser only receives a short-lived ephemeral token.
  </Card>
</CardGroup>

## Prerequisites

* Node.js 18+
* A Decart API key from [platform.decart.ai](https://platform.decart.ai)

## How it works

The entire integration is three steps:

1. **Server** creates a short-lived client token from your permanent API key
2. **Browser** opens the camera and establishes a WebRTC connection to `lucy-vton-latest`
3. **Browser** sends a garment image + descriptive prompt via `set()` — the model dresses the person in realtime

```mermaid theme={null}
sequenceDiagram
    participant S as Your Server
    participant D as Decart
    participant B as Browser<br/>(Decart SDK)

    S->>D: POST /v1/client/tokens (API key)
    D-->>S: { apiKey: "ek_...", expiresAt }
    S->>B: Pass client token

    B->>D: WebRTC connect (camera stream)
    D->>B: WebRTC (transformed video)

    Note over B,D: set({ image, prompt }) to change outfits
```

## Step 1: Create a client token (server-side)

Your backend creates a short-lived token using your permanent API key. The browser never sees your real key.

```typescript theme={null}
// app/api/tokens/route.ts
import { createDecartClient } from "@decartai/sdk";
import { NextResponse } from "next/server";

export async function POST() {
  const client = createDecartClient({
    apiKey: process.env.DECART_API_KEY,
  });
  const token = await client.tokens.create();
  return NextResponse.json(token);
}
```

<Note>
  Client tokens have a 10-minute TTL. Create a new token each time a user opens a try-on session. Active WebRTC sessions continue working even after the token expires. See the [Client Tokens guide](/getting-started/client-tokens) for details.
</Note>

## Step 2: Connect camera to the realtime model

The frontend fetches a token, opens the camera, and establishes a WebRTC connection to `lucy-vton-latest`.

```typescript theme={null}
import { createDecartClient, models } from "@decartai/sdk";

// 1. Get a client token from your backend
const res = await fetch("/api/tokens", { method: "POST" });
const { apiKey } = await res.json();

// 2. Open the user's camera
const stream = await navigator.mediaDevices.getUserMedia({
  video: { facingMode: "user" },
});

// 3. Connect to the realtime model
const client = createDecartClient({ apiKey });

const rtClient = await client.realtime.connect(stream, {
  model: models.realtime("lucy-vton-latest"),
  onRemoteStream: (remoteStream) => {
    document.getElementById("output").srcObject = remoteStream;
  },
});
```

## Step 3: Send a garment image + prompt

Call `set()` to send a reference garment image with a descriptive prompt. The model applies the garment to the person in realtime.

```typescript theme={null}
await rtClient.set({
  prompt: "Substitute the current top with a black bomber jacket with a blue logo on the chest and a zip front",
  image: garmentBlob,
  enhance: false,
});
```

Call `set()` again at any time to switch garments — no need to reconnect.

## Product catalog pattern

For e-commerce, pre-write prompts for each product in your catalog. This gives the best results since you can craft specific, detailed descriptions.

```typescript theme={null}
interface Product {
  name: string;
  image: string;
  prompt: string;
  price: number;
}

const products: Product[] = [
  {
    name: "Bomber Jacket",
    image: "/products/bomber.png",
    prompt: "Substitute the current top with a black bomber jacket with a blue logo on the chest and a zip front",
    price: 149,
  },
  {
    name: "Knit Beanie",
    image: "/products/beanie.png",
    prompt: "Add a navy blue knit beanie with a white cross logo on the fold to the person's head",
    price: 35,
  },
  {
    name: "Polo Shirt",
    image: "/products/polo.png",
    prompt: "Substitute the current top with a navy blue polo shirt with a white logo on the chest",
    price: 69,
  },
];

// When user clicks "Try it on"
async function tryOn(product: Product) {
  const garmentBlob = await fetch(product.image).then((r) => r.blob());
  await rtClient.set({
    prompt: product.prompt,
    image: garmentBlob,
    enhance: false, // use your pre-written prompt as-is
  });
}
```

### Prompt patterns

Use the **substitute** pattern when replacing an existing garment, and the **add** pattern when adding something new:

| Pattern        | When to use                               | Example                                                                          |
| -------------- | ----------------------------------------- | -------------------------------------------------------------------------------- |
| **Substitute** | Replacing an existing garment             | `"Substitute the current top with a red plaid flannel shirt with a relaxed fit"` |
| **Add**        | Adding something the person isn't wearing | `"Add a wide-brimmed straw hat to the person's head"`                            |

Be specific — include color, material, texture, pattern, and fit. Aim for **20–30 words**.

```
✅ "Substitute the current top with a bright red hoodie with an oversized casual fit"
✅ "Add a navy baseball cap with a blue logo to the person's head"

❌ "Put a jacket on the person"   (too vague)
❌ "Red hoodie"                    (missing action and context)
```

## Generating prompts for user uploads

For user-uploaded garment images where you don't have a pre-written prompt, use any vision LLM to auto-generate one from the garment image:

```typescript theme={null}
// app/api/enhance-prompt/route.ts
import OpenAI from "openai";

const openai = new OpenAI();

export async function POST(req: Request) {
  const formData = await req.formData();
  const image = formData.get("image") as File;
  const personFrame = formData.get("personFrame") as File | null; // optional

  const messages = [
    {
      role: "system" as const,
      content: `You write prompts for a virtual try-on model. Examine the garment image and write a prompt using this pattern:
"Substitute the current top with [detailed garment description]" or "Add [item] to the person's [body part]".
Include color, material, texture, pattern, and fit. 20-30 words. Return only the prompt.`,
    },
    {
      role: "user" as const,
      content: [
        { type: "image_url" as const, image_url: { url: await fileToDataUrl(image) } },
        ...(personFrame
          ? [{ type: "image_url" as const, image_url: { url: await fileToDataUrl(personFrame) } }]
          : []),
      ],
    },
  ];

  const completion = await openai.chat.completions.create({
    model: "gpt-4o-mini",
    messages,
    max_tokens: 100,
  });

  return Response.json({ prompt: completion.choices[0].message.content });
}
```

<Tip>
  Sending a camera frame of the person alongside the garment image gives the LLM context about what they're currently wearing, producing more accurate prompts (e.g. "Substitute the grey sweater" instead of generic "Substitute the current top").
</Tip>

## Reference image tips

* **Clean garment images work best** — just the clothing item, no person wearing it
* **White or plain backgrounds** are ideal
* **At least 512×512 pixels** — the model reproduces what it sees, so a clear image produces better results
* If your product images show a model wearing the garment, consider using an image editing model to extract just the clothing item first

## More examples

The [tryon-examples](https://github.com/DecartAI/tryon-examples) repo includes six production-ready Next.js examples:

| Example              | Use case                       | Highlights                                                |
| -------------------- | ------------------------------ | --------------------------------------------------------- |
| **E-commerce**       | "Try it on" on product pages   | Pre-written prompts, modal overlay                        |
| **Standalone**       | Dedicated try-on experience    | LLM-generated prompts from garment images                 |
| **Person Detection** | Kiosks and unattended displays | Auto-connects only when someone is in frame               |
| **Full-Featured**    | Complete try-on experience     | Person detection, clothing extraction, extreme precision  |
| **Digital Mirror**   | In-store kiosk / smart mirror  | Two-device setup (display + phone controller via QR code) |
| **Outfit Builder**   | Multi-garment styling          | Compose top + bottom garments into full outfits           |

## Next steps

<CardGroup cols={2}>
  <Card icon="bolt" title="Virtual Try-On Guide" href="/models/realtime/virtual-try-on">
    Full guide for realtime and batch virtual try-on, including all parameters and SDK examples.
  </Card>

  <Card icon="code" title="Batch API Reference" href="/api-reference/lucy-21-vton">
    Process pre-recorded videos asynchronously with the batch virtual try-on endpoint.
  </Card>

  <Card icon="key" title="Client Tokens" href="/getting-started/client-tokens">
    Secure your integration with short-lived ephemeral tokens.
  </Card>

  <Card icon="bolt" title="Streaming Best Practices" href="/models/realtime/streaming-best-practices">
    Optimize your realtime integration for the best experience.
  </Card>
</CardGroup>
