How to Add Virtual Try-On to Your Shopify Store (Step-by-Step Tutorial)
Adding virtual try-on to a Shopify store is usually a product-page feature, not a platform migration. You keep your storefront, product catalog, and checkout flow, then connect a try-on service that can generate an image result when a shopper wants to see how an item might look on them. For apparel merchants, that can help reduce hesitation before purchase and give shoppers a more interactive product page.
Why virtual try-on helps Shopify stores
Apparel returns are often cited in the 20% to 30% range industry-wide, and fit uncertainty is one of the reasons shoppers abandon carts or send items back. A virtual try-on flow gives customers a clearer preview before they buy. For Shopify merchants, that can mean a better product page experience, more confident decisions, and fewer support questions about how an item will look.
Virtual try-on is also useful because it fits into the page people already browse. Instead of sending shoppers to a separate tool, you can add a "Try it on me" button directly on the product page. That keeps the path to purchase short.
What you need
- A Shopify store with access to your product template or theme app extension
- A FitInView API key from the developers page
- Basic JavaScript and a small backend proxy, or Shopify App Proxy
- A product page that can expose the garment image URL you want to send to the API
- A person photo source, either uploaded by the shopper or selected from a profile image flow you already use
FitInView provides public API endpoints for submitting a try-on job, checking job status, listing recent jobs, and reading account info. The public dev pack pricing is also visible on the developers page, and credits never expire. Pricing is listed at 1K resolution, while higher resolutions scale with resolution.
Step 1: Get a FitInView API key
Start in the FitInView developers area and create an API key for your store or development workspace. Test keys use the prefix `fiv_test_` and return mock responses without charge, which is helpful while you build. Production keys are used for real jobs.
Keep the key server-side. Do not place it in browser code or theme JavaScript that runs in the shopper’s browser. Your storefront should talk to your own backend proxy, and that backend can call FitInView securely.
Recommended setup
- Use a test key while you build the first version
- Store the production key in server environment settings
- Send the key only from your backend proxy to FitInView
- Use the `X-Request-Id` response header when you need support help
Step 2: Add a "Try-On" button to your product template
In Shopify, you can add a button to the product template or a theme section near the add-to-cart area. The button should open a small panel or modal where the shopper can provide or confirm the photo used for try-on.
{
"type": "p",
"text": "Example placement: on the product page, near the add-to-cart button, show a \"Try it on me\" button that opens a modal for photo upload."
}
document.addEventListener('DOMContentLoaded', () => {
const button = document.getElementById('fitinview-tryon-button');
const modal = document.getElementById('fitinview-tryon-modal');
if (!button || !modal) return;
button.addEventListener('click', () => {
modal.hidden = false;
});
});
The modal can include a file input, a short privacy notice, and a submit button. If your flow already has a user profile image, you can use that image URL instead of asking for a new upload. The key point is that the try-on request needs a person photo URL and one or more garment image URLs.
Step 3: Backend proxy to keep your API key server-side
The browser should not call FitInView directly with your production key. Instead, create a small backend endpoint, or use Shopify App Proxy, that accepts the shopper action and then submits the try-on job to FitInView.
That proxy can also attach an idempotency key if you want to avoid duplicate submissions when a shopper double-clicks. FitInView states that duplicate requests within 24 hours return the cached response when the same idempotency key is used.
| Option | Good for | Notes |
|---|---|---|
| Shopify App Proxy | Stores already using Shopify app infrastructure | Keeps requests on the Shopify side and lets your app act as the server-side bridge |
| Small custom server | Simple prototypes or headless setups | Fast to build, but you still need to host and secure the proxy |
| Direct browser call | Not recommended | Would expose your API key to shoppers |
What the proxy sends
- person_url, the shopper photo URL
- garment_urls, an array of one or more garment image URLs
- output_size, one of 1K, 2K, or 4K
- webhook_url, if you want completion callbacks
- metadata, any JSON you want returned with the callback
Step 4: Wire upload + polling logic
The simplest flow is: upload or select the shopper photo, submit a job, then poll until the job completes. FitInView exposes POST /api/v1/tryon to create the job, and GET /api/v1/jobs/{id} to check status.
You can use the product image URL from Shopify as the garment image. For one product page, a single garment URL is enough. If your store supports bundles or outfit sets, the API accepts multiple garment image URLs.
async function submitTryOn({ personUrl, garmentUrls, outputSize = '1K' }) {
const response = await fetch('/apps/fitinview/tryon', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
person_url: personUrl,
garment_urls: garmentUrls,
output_size: outputSize
})
});
if (!response.ok) {
throw new Error('Try-on request failed');
}
return response.json();
}
async function pollJob(jobId) {
while (true) {
const response = await fetch(`/apps/fitinview/jobs/${jobId}`);
const data = await response.json();
if (data.status === 'completed') return data;
if (data.status === 'failed') throw new Error('Try-on job failed');
await new Promise(resolve => setTimeout(resolve, 2000));
}
}
Your backend proxy would be the one calling FitInView with Authorization: Bearer
import requests
API_BASE = 'https://api.fitinview.com'
def create_tryon(api_key, person_url, garment_urls, output_size='1K', webhook_url=None, metadata=None):
payload = {
'person_url': person_url,
'garment_urls': garment_urls,
'output_size': output_size,
}
if webhook_url:
payload['webhook_url'] = webhook_url
if metadata is not None:
payload['metadata'] = metadata
response = requests.post(
f'{API_BASE}/api/v1/tryon',
json=payload,
headers={
'Authorization': f'Bearer {api_key}',
'Content-Type': 'application/json'
},
timeout=30,
)
response.raise_for_status()
return response.json()
Step 5: Display the result image and capture analytics
When GET /api/v1/jobs/{id} returns a completed job, the response includes a result_url. Show that image in your modal or a results panel, then give the shopper a clear next action, such as adding the item to cart or trying another photo.
You should also capture a few basic analytics events in your own app or analytics tool. Useful events include button click, upload started, job submitted, job completed, and add-to-cart after try-on. If you already track Shopify theme events, map the try-on steps into the same system.
| Event | When to fire | Why it matters |
|---|---|---|
| tryon_button_clicked | User opens the modal | Measures product-page interest |
| tryon_submitted | Proxy returns job id | Shows how often shoppers start the flow |
| tryon_completed | Status becomes completed | Helps you measure successful results |
| tryon_added_to_cart | User adds the item after viewing result | Connects try-on to commerce action |
Tip: keep the result panel close to the add-to-cart area so shoppers do not lose context after the image appears.
Production hardening, use webhooks instead of polling for scale
Polling is the easiest first version, but webhooks are better for a production store. FitInView can send a completion callback to your webhook_url when the job completes. The webhook must be HTTPS, and the signature header is X-FitInView-Signature.
Webhook verification is straightforward. FitInView signs the raw request body with HMAC-SHA256, hex-encodes the result, and you compare it with the header value using a constant-time comparison.
import crypto from 'crypto';
function verifyFitInViewWebhook(rawBody, signatureHeader, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(rawBody)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(expected, 'hex'),
Buffer.from(signatureHeader || '', 'hex')
);
}
For a real storefront, webhooks reduce the number of status checks your app has to make and make the flow more reliable during peak traffic. You can still keep polling as a fallback in case a callback is delayed.
Practical launch checklist
- Use a test key first and verify mock responses
- Confirm the product image URL is valid and publicly reachable
- Handle loading, success, and error states in the modal
- Show a retry action when a job fails
- Log the X-Request-Id value for support
- Add webhook verification before turning on production callbacks
FitInView versus other public try-on options
If you are comparing providers, the main differences for Shopify integration are usually pricing transparency, webhook support, and whether the API fits a simple storefront workflow. Here is a fair comparison based only on public facts.
| Provider | Public pricing info | Webhook support on public page | Resolution notes |
|---|---|---|---|
| FitInView | Public dev packs listed on the developers page | Yes, webhook_url is documented | 1K, 2K, 4K; higher resolutions scale with resolution |
| FASHN.ai | Subscription plus top-ups; per-try-on cost not publicly broken out | Not stated here | Up to 4K image generation |
| FitRoom | Dollar pricing gated behind purchase buttons | Not mentioned on public pricing page | Up to 2048px resolution |
| Segmind | PAYG entry and subscription tiers publicly listed | Not stated here | Per-GPU-second pricing on serverless models |
| tryon-api.com | Free Starter and paid tiers listed; per-call rate not publicly listed | Yes, callback completion flows supported | Specific session allowances not published |
For a Shopify merchant, the right choice often comes down to how easily you can add the feature and what the public API exposes. FitInView gives you a straightforward job submission flow, job polling, account info, test keys, and webhook callbacks, which is a practical combination for store integrations.
How long this takes
A basic version can usually be built in 30 to 60 minutes if your store already has a developer-friendly theme and you are comfortable with JavaScript and a small backend proxy. A production-ready version takes longer because you need to handle uploads, state changes, webhook verification, error recovery, analytics, and theme QA across devices.
Conclusion
To add virtual try-on to a Shopify store, start with a simple product-page button, send the shopper photo and garment image URLs through your backend proxy, then render the completed result on the page. Build the first version with polling, then move to webhooks when you are ready for a cleaner production flow.
Next step: create a test key, wire the button into one product template, and validate one end-to-end try-on before rolling it out to the rest of your catalog.