@avis-ai/tryout
v1.0.6
Published
React components and hooks for Avis AI virtual try-on (reference images, generate, optional local persistence).
Downloads
772
Maintainers
Readme
Avis Tryout (@avis-ai/tryout)
React components and hooks for virtual try-on: upload or import garment and face reference images, optional body measurements, and generate a composed look using the same project API key and billing model as AvisChat. Reference images are stored on the API host (S3); only public https URLs are sent to the image model (no huge base64 payloads).
Headless option: use AvisTryoutClient from this package (same REST calls as the UI). It builds on @avis-ai/sdk-js for Authorization: Bearer requests and error parsing.
Contents
- Getting your API key
- Allowed domains (browser)
- Installation
- Quick start (embedded UI)
- Headless: client + hook
- REST API overview
- Images: upload, import, generate
- Partner integrations (URLs vs upload)
- Body measurements
<AvisTryout />propsuseAvisTryouthook- Result preview & zoom (optional)
- Session & local history
- React Native / mobile
- Embedding without React (script widget)
- Proxy / production
- TypeScript types
- Styling
- Errors
- Next.js notes
Getting your API key
Same project key as chat — create a project in the dashboard and use its API key on every tryout request.
- Log in to the Dashboard
- Create a project (or open an existing one)
- Copy the API key — shown once; store it securely
Use the key as apiKey on <AvisTryout /> / AvisTryoutClient, or keep it on your server and proxy tryout routes (see Proxy / production).
Allowed domains (browser)
If the project has allowed domains configured in the dashboard, browser calls must send a valid Origin header that matches one of those domains. Otherwise the API responds with 403 (DOMAIN_NOT_ALLOWED). Server-to-server calls (no Origin) are not restricted by that list.
Installation
Peer dependencies: React 18+ and React DOM 18+ (React DOM is required for optional full-screen zoom, which uses createPortal).
npm install @avis-ai/tryout @avis-ai/sdk-js
pnpm add @avis-ai/tryout @avis-ai/sdk-js
yarn add @avis-ai/tryout @avis-ai/sdk-jsQuick start (embedded UI)
"use client";
import { AvisTryout } from "@avis-ai/tryout";
export function MyTryout() {
return (
<AvisTryout
apiKey={process.env.NEXT_PUBLIC_AVIS_API_KEY!}
/>
);
}You get gender chips, optional face/top/bottom/accessory image URL + file upload, measurements panel, generate button, Reset all, and recent results with Edit & generate / Regenerate actions. baseUrl defaults to https://api-ai.avi-s.in if omitted.
Headless: client + hook
AvisTryoutClient
import { AvisTryoutClient } from "@avis-ai/tryout";
const client = new AvisTryoutClient({
apiKey: process.env.AVIS_API_KEY!,
});
const { url } = await client.uploadTryoutImage(file);
const out = await client.generate({
sessionId: "my-session",
person: { faceImageUrl: url, gender: "female" },
outfit: {
top: { imageUrl: topUrl, color: "navy" },
bottom: { imageUrl: bottomUrl }
}
});
console.log(out.imageUrl);useAvisTryout
Same defaults as <AvisTryout />, plus local profile + history. On the web, persistence defaults to localStorage plus IndexedDB for mirrored result blobs when available. Pass persistence to use AsyncStorage (or in-memory) on React Native — see React Native / mobile.
import { useAvisTryout } from "@avis-ai/tryout";
function CustomTryout() {
const { profile, setProfile, history, isLoading, error, generateTryout, resetTryout } = useAvisTryout({
apiKey: "your-api-key"
});
return (
<div>
<button type="button" disabled={isLoading} onClick={() => generateTryout()}>
Generate
</button>
<button type="button" onClick={() => void resetTryout()}>
Reset
</button>
{error && <p>{error.message}</p>}
{history.map((h) => (
<img key={h.id} src={h.displayUrl ?? h.imageUrl} alt="" width={200} />
))}
</div>
);
}REST API overview
All routes use Authorization: Bearer <project_api_key> and Content-Type: application/json except POST /v1/tryout/upload (multipart form field file).
| Method | Path | Purpose |
|--------|------|--------|
| POST | /v1/tryout/upload | Multipart image → stored file → returns { url } (public https). |
| POST | /v1/tryout/import-image | JSON { "url" } → server fetches image (rules apply) → returns { url }. |
| POST | /v1/tryout/generate | JSON tryout payload → returns { imageUrl, sessionId, ... }. |
| POST | /v1/tryout/mirror-result-image | JSON { "imageUrl" } → returns raw image bytes (allowlisted hosts); used to persist results locally without CORS issues. |
Default API origin: https://api-ai.avi-s.in (strip trailing slash when overriding).
Images: upload, import, generate
- Upload (
uploadTryoutImage) or import (importTryoutImageFromUrl) each reference image so you receive a stablehttpsURL on your storage (e.g. S3). - Put those URLs in
person.faceImageUrl,outfit.top.imageUrl,outfit.bottom.imageUrl,outfit.accessories[].imageUrlas needed. - Call
generatewith that JSON.data:URLs are rejected — always host first.
Partner integrations (URLs vs upload)
- Multipart upload — best when you already have file bytes (browser or your backend).
- Import by URL — best when partners expose direct CDN image URLs (
image/jpeg, etc.). Product HTML pages are a poor source; the server may try to resolveog:image/twitter:imagefor HTML responses, but direct asset URLs are what you want in production.
Body measurements
Optional person.bodyMeasurements uses US-style retail units:
| Field | Unit |
|-------|------|
| heightInches | inches |
| weightLbs | pounds |
| chestInches, waistInches, hipInches, shoulderInches, inseamInches | inches |
Omit any field you do not need.
<AvisTryout /> props
Required: apiKey. All other props are optional.
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| apiKey | string | — | Project API key (Bearer). |
| baseUrl | string | https://api-ai.avi-s.in | API origin (no trailing slash). Optional if you use the hosted API. |
| sessionId | string | auto in hook | Passed through to generate; hook persists session id in localStorage. |
| quality | "low" \| "medium" \| "high" | provider/default behavior | Optional integration-level override for generation quality (no built-in UI selector). When set, each generate call uses this quality. |
| theme | "light" \| "dark" | "dark" | |
| title / subtitle | string | defaults | Header copy. |
| className / style | — | — | Outer container. |
| styles | AvisTryoutStyleOverrides | — | Inline style overrides per region (see source types). |
| maxAccessories | number | 10 | Cap for accessory rows. |
| showImagePreviews | boolean | true | Toggle inline previews for selected images. |
| zoomableGeneratedImages | boolean | false | When true, tap result for full-screen zoom (wheel + drag). Off for API-only / custom layouts. |
| persistence | TryoutPersistence | web default | Pluggable storage for profile/history/session (e.g. createAsyncStorageTryoutPersistence on native). |
| uploadLoader | ReactNode \| (section) => ReactNode | AVIS branded loader | Custom loader for active upload section. Only the in-progress section is disabled; other sections stay interactive. |
| labels | object | — | generateButton, resetButton, regenerateButton, editButton, recentTitle, addAccessory, removeAccessory, noHistory, tapToZoomHint. |
Custom upload loader
uploadLoader controls what is shown while a section upload/import is in progress.
Behavior:
- Loader appears only for the active section that is uploading.
- Only that section's upload controls are disabled; other sections remain usable.
- If
uploadLoaderis omitted, AvisTryout uses the default AVIS-branded animated loader.
You can pass either:
ReactNode: one shared loader UI for every section.(section: string) => ReactNode: dynamic loader based on section id.
Section ids passed to the function:
facetopbottomacc-<index>(for accessories, e.g.acc-0,acc-1)
Example (single shared loader):
<AvisTryout
apiKey={API_KEY}
uploadLoader={<span style={{ fontSize: 12 }}>Uploading to AVIS…</span>}
/>Example (custom per section):
<AvisTryout
apiKey={API_KEY}
uploadLoader={(section) => (
<span style={{ fontSize: 12 }}>
{section.startsWith("acc-") ? "Uploading accessory…" : `Uploading ${section}…`}
</span>
)}
/>useAvisTryout hook
Options: apiKey, baseUrl?, sessionId?, quality? (same as component/client), plus:
persistence?— seeTryoutPersistenceandcreateAsyncStorageTryoutPersistencein the package exports.persistHistoryBySession?(boolean, defaulttrue) — stores/loads recent history under a session-scoped key so refresh keeps the same session's recents.
Quality behavior
- If you pass
quality, the hook/component applies it on everygeneraterequest. - Supported values:
low,medium,high. - In Replicate mode, server maps quality to resolution before upstream call:
low -> 1Kmedium -> 2Khigh -> 4K
Returns:
| Field | Type | Description |
|-------|------|-------------|
| profile | TryoutGenerateInput | Form state; synced to localStorage. |
| setProfile | (patch) => void | Merge patch into profile and persist. |
| history | TryoutHistoryItem[] | Recent generations; displayUrl may be a blob: from IndexedDB. |
| isLoading | boolean | true while generate (+ mirror) runs. |
| error | Error \| null | Last error. |
| generateTryout | (input?) => Promise<TryoutGenerateOutput> | Merges profile, calls API, updates history, mirrors result for offline-friendly thumbnails. |
| resetTryout | () => Promise<void> | Clears profile, history, mirrored local blobs, and persisted tryout sessionId for a fresh start. |
Use the embedded component's history actions to speed iteration:
Edit & generate: loads a previous item's input back into the form so users can tweak and regenerate.Regenerate: reruns generation with that item's exact previous input.
Result preview & zoom (optional)
Set zoomableGeneratedImages to enable an e-commerce style tap → full-screen preview: scroll to zoom, drag when zoomed, double-click to reset, Esc / backdrop / Close to dismiss. Partners who only consume generate JSON can leave this false.
Session & local history
sessionIdin the generate payload groups usage on the server; the hook generates and persists one under the keyavis-tryout-session-id(in the active persistence adapter) unless you passsessionId.- History is JSON under
avis-tryout-historyand (by default) also underavis-tryout-history:<sessionId>. Session-scoped history is what enables recent items to survive refresh for the same session in the demo. - On the web, mirrored result thumbnails use IndexedDB when
persistence.canMirroris true; otherwise history items use the remoteimageUrlonly (URLs may expire). - Old saved profiles with cm/kg measurements are migrated to inches/lb on load when possible.
resetTryout()clears profile/history/session keys and mirrored local images for complete reset flows.
React Native / mobile
<AvisTryout />is built for React DOM (react-dom, file inputs,createPortal). Use it inside a WebView if you want the stock UI on mobile, or build your own screen with the primitives below.AvisTryoutClientisfetch-only — safe to use in React Native and native shells for upload, import, generate, and mirror.useAvisTryoutworks in RN when you passpersistence: usecreateAsyncStorageTryoutPersistence(AsyncStorage)from@react-native-async-storage/async-storage(you add that dependency in your app; this package does not depend on it). Mirroring to a local blob cache stays off on that adapter (canMirror: false), sohistory[].displayUrlis usually undefined and you should renderimageUrl(or download/cache images yourself if you need offline thumbnails).
import AsyncStorage from "@react-native-async-storage/async-storage";
import {
useAvisTryout,
createAsyncStorageTryoutPersistence
} from "@avis-ai/tryout";
const persistence = createAsyncStorageTryoutPersistence(AsyncStorage);
function TryoutScreen() {
const { profile, setProfile, history, isLoading, error, generateTryout } = useAvisTryout({
apiKey: YOUR_KEY,
persistence
});
// … your RN UI; images: <Image source={{ uri: h.displayUrl ?? h.imageUrl }} />
}For tests or SSR with no storage, use createMemoryTryoutPersistence().
Embedding without React (script widget)
If your site is not React-based, load the hosted tryout iframe widget script from your API origin.
<script
src="https://api-ai.avi-s.in/tryout-widget.js"
data-api-key="sk_live_xxx"
data-embed-url="https://demo-ai.avi-s.in/tryout-embed"
data-floating="true"
data-width="420px"
data-height="760px"
></script>Supported attributes:
data-api-key(required): project API key used by the embedded tryout UI.data-embed-url(optional): hosted embed page URL. Defaults tohttp://localhost:3002/tryout-embedfor local development.data-floating(optional, defaulttrue): floating launcher mode (true) vs always-visible panel (false).data-width,data-height,data-bottom,data-right,data-border-radius,data-z-index(optional): iframe layout tuning.data-launcher-size,data-launcher-label,data-launcher-emoji(optional): floating launcher appearance.
For production, host your own public embed page (or use your demo host), then point data-embed-url to that URL.
Proxy / production
For production, avoid exposing a live project secret in the browser if your policy forbids it. Proxy /v1/tryout/* from your origin and attach the key on the server, or issue short-lived tokens your BFF exchanges for upstream calls.
<AvisTryout
apiKey={process.env.NEXT_PUBLIC_AVIS_API_KEY!}
baseUrl={process.env.NEXT_PUBLIC_AVIS_BASE_URL}
/>NEXT_PUBLIC_* keys are visible to clients — use only for dev/staging or public keys you accept rotating.
TypeScript types
Re-exported from @avis-ai/tryout:
import type {
AvisTryoutProps,
AvisTryoutStyleOverrides,
TryoutPersistence,
TryoutGenerateInput,
TryoutGenerateOutput,
TryoutClientOptions,
TryoutHistoryItem,
TryoutPerson,
TryoutOutfit,
TryoutRender,
BodyMeasurements,
GarmentInput,
AccessoryInput,
UseAvisTryoutOptions,
UseAvisTryoutReturn
} from "@avis-ai/tryout";Styling
<AvisTryout /> uses inline styles by default. Pass styles (AvisTryoutStyleOverrides) to merge React CSSProperties over regions such as container, form, buttonPrimary, historyCard, imagePreview, etc. (see AvisTryout.tsx).
Errors
- Failed uploads/imports/generate throw from
AvisTryoutClientusingparseErrorResponsefrom@avis-ai/sdk-js. <AvisTryout />shows the lasterror.messageunder the form.401/403— bad key, or origin not in allowed domains.
Next.js notes
- Add
"use client"to any file that importsAvisTryoutoruseAvisTryout. - Do not read
localStorageforsessionIdon the server render; follow the same hydration pattern as AvisChat README Session persistence (initialize ids inuseEffectafter mount).
Related packages
| Package | Role |
|---------|------|
| @avis-ai/avischat | Chat UI + useAvisChat |
| @avis-ai/sdk-js | Avis client, parseErrorResponse, chat APIs |
