ZeDaq.ai
Menswear shopper viewing a suit on a fashion store website

Adding Virtual Try-On to a React Storefront

By the ZeDaq.ai team · · 4 min read

On this page
  1. Why can't React call the API directly?
  2. How do you build the upload component?
  3. What does the server proxy look like?
  4. How do you handle the 202 response?
  5. Which errors should the interface handle?
  6. Where do you put the button on the product page?

Adding virtual try-on to a React storefront takes two pieces: a small component that lets the shopper pick a photo, and a server route that calls the ZeDaq.ai API with your key. The key must never ship in your React bundle, so the browser talks only to your own server.

This guide assumes a client-rendered React app, for example one built with Vite. For the API basics, see our virtual try-on API guide for developers. If your app is Shopify Hydrogen or Next.js, use the dedicated guides linked below.

Why can't React call the API directly?

Anything in a React bundle can be read by every visitor. The API reference says to keep server keys on your server and to lock keys to your domains.

So the browser sends the photo to your endpoint, and your endpoint adds the x-api-key header. If you only need a button and no custom interface, the script-tag widget skips this work.

How do you build the upload component?

The component reads the file, shrinks it, sends it to your proxy and shows the result. The API docs say a full-length photo about 800px wide works well, so resize before you upload.

import { useState } from 'react';

async function toBase64(file, maxWidth = 800) {
  const bitmap = await createImageBitmap(file);
  const scale = Math.min(1, maxWidth / bitmap.width);
  const canvas = document.createElement('canvas');
  canvas.width = bitmap.width * scale;
  canvas.height = bitmap.height * scale;
  canvas.getContext('2d').drawImage(bitmap, 0, 0, canvas.width, canvas.height);
  return canvas.toDataURL('image/jpeg', 0.9).split(',')[1];
}

export function TryOnButton({ productImageUrl, garmentType }) {
  const [state, setState] = useState({ status: 'idle' });
  async function onFile(e) {
    const file = e.target.files?.[0];
    if (!file) return;
    setState({ status: 'loading' });
    const res = await fetch('/api/tryon', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        person_image_base64: await toBase64(file),
        product_image_url: productImageUrl,
        garment_type: garmentType,
      }),
    });
    const data = await res.json();
    if (!res.ok) return setState({ status: 'error', message: data.message });
    setState({ status: 'done', src: `data:${data.mime_type};base64,${data.image_base64}` });
  }
  return (
    <div>
      <input type="file" accept="image/jpeg,image/png" onChange={onFile} />
      {state.status === 'loading' && <p>Creating your try-on...</p>}
      {state.status === 'error' && <p role="alert">{state.message}</p>}
      {state.status === 'done' && <img src={state.src} alt="You wearing this garment" />}
    </div>
  );
}

Add a clear note near the input that explains what happens to the photo. Our piece on shopper privacy and virtual try-on lists what to say.

What does the server proxy look like?

The proxy below uses Express and Node's built-in fetch. It downloads the product image, calls the API and returns the result to the browser.

import express from 'express';
const app = express();
app.use(express.json({ limit: '8mb' }));
app.post('/api/tryon', async (req, res) => {
  const { person_image_base64, product_image_url, garment_type } = req.body;
  const img = await fetch(product_image_url);
  const product_image_base64 = Buffer.from(await img.arrayBuffer()).toString('base64');
  const headers = { 'x-api-key': process.env.ZEDAQ_API_KEY, 'Content-Type': 'application/json' };
  let r = await fetch('https://www.zedaq.ai/api/v1/tryon', {
    method: 'POST', headers,
    body: JSON.stringify({ person_image_base64, product_image_base64, garment_type }),
  });
  let data = await r.json();
  while (r.status === 202) {
    await new Promise((ok) => setTimeout(ok, 2000));
    r = await fetch('https://www.zedaq.ai' + data.poll_url, { headers });
    data = await r.json();
  }
  res.status(r.status).json(data);
});
app.listen(3001);

In production, only accept product URLs from your own catalog. Otherwise, anyone could use your server to fetch arbitrary addresses.

How do you handle the 202 response?

The 202 means the render is still running. The proxy waits 2 seconds between checks, as the API reference suggests, and returns the finished image.

For busy sites, return the job_id to the browser and poll your own endpoint instead of holding the request open. Usage is only charged once a render succeeds.

Which errors should the interface handle?

The API returns an error code and a message. Show the message for caps and trial limits, and log the code on the server. A shopper does not need to see invalid_api_key; they need a calm "try again later".

Where do you put the button on the product page?

Place it beside the size selector and pass the garment type that matches the product: upper_body, lower_body or dresses. Pair it with accurate size information, as in the product page checklist for fashion stores.

Building a mobile app instead? See try-on in a React Native app. Other web front ends follow the same pattern. See Next.js headless stores, Shopify Hydrogen storefronts, and, for a Shopify Plus build, adding try-on to Shopify Plus.

Get an API key for your React store

FAQs

Can I use a widget key inside React?

The widget key is meant for the script tag and must be locked to your domain. For a custom component, use the REST API through a server route.

Do I need Node on the server?

No. Any backend that can send an HTTPS request with a header works, including PHP or Python.

How big should the uploaded photo be?

The API reference suggests a full-length photo around 800px wide. Resizing in the browser also keeps requests small.

Does the same code work for sizing?

Not exactly. Sizing uses POST /api/v1/scan with pose data, and a helper script is described in the API reference.

Try ZeDaq.ai free

Virtual try-on and Find my size for your store, plus client, order and invoicing tools for tailors, in one subscription.

Related articles