ExperienceThumbnail
Layered CDN gallery previews with optional parallax and hosted-player launch — available in Svelte, React, Next.js, and vanilla.
Overview
<ExperienceThumbnail> renders the same layered Meet-style preview used on
www.liforma.ai: avatar plate plus optional location background and foreground, with lazy load
and pointer/scroll parallax. It is a catalog preview, not a live player.
Painting the thumb needs no API key and no origin allowlist — pass galleryThumb from your server-side catalog fetch (paths, not CDN hosts). Keys
and allowlists apply only when a session starts (hosted launch click, or a page that mounts <Experience>).
Import the thumbnail entry (not the full /svelte / /react barrel) so gallery and landing pages do not load session / widget code.
Install
npm install @liforma/client | Surface | Import / tag |
|---|---|
| Svelte | @liforma/client/svelte/thumbnail |
| React | @liforma/client/react/thumbnail |
| Next.js | @liforma/client/next/thumbnail (client component) |
| Vanilla | <liforma-experience-thumbnail> via https://cdn.liforma.ai/sdk/v2/thumbnail.js |
Zero-config hosted launch
Pass experienceId and galleryThumb with no href / onclick. Click opens player.liforma.ai/{experienceId}?returnUrl=… and returns to the embedding
page on close. Add your site origin to the project allowlist first (launch path only).
<script>
import { ExperienceThumbnail } from '@liforma/client/svelte/thumbnail';
<\/script>
<ExperienceThumbnail
experienceId={experience.experienceId}
galleryThumb={experience.galleryThumb}
alt={experience.title}
/> import { ExperienceThumbnail } from '@liforma/client/react/thumbnail';
// Next.js App Router client files can also use:
// import { ExperienceThumbnail } from '@liforma/client/next/thumbnail';
export function GalleryCard({ experience }) {
return (
<ExperienceThumbnail
experienceId={experience.experienceId}
galleryThumb={experience.galleryThumb}
alt={experience.title}
/>
);
} <script type="module" src="https://cdn.liforma.ai/sdk/v2/thumbnail.js"><\/script>
<liforma-experience-thumbnail
experience-id="exp_T0I7ACMQLBMPG6K"
alt="Spanish Cafe"
gallery-thumb='{"avatarImage":"https://cdn.liforma.ai/avatars/05a87620/256/05a87620_neutral.webp"}'
></liforma-experience-thumbnail> App-owned routing
When your app owns the session page (Meet-style), pass href to your slug route.
That page should mount <Experience> with experienceId (and
usually sessionEndpoint for authenticated mint).
<script>
import { ExperienceThumbnail } from '@liforma/client/svelte/thumbnail';
<\/script>
<ExperienceThumbnail
galleryThumb={experience.galleryThumb}
alt={experience.title}
href={`/experiences/${experience.slug}`}
/> Presentational (parent owns the link)
Omit experienceId, href, and onclick. Wrap the thumb
yourself and use alt="" when the parent already provides the accessible name.
<script>
import { ExperienceThumbnail } from '@liforma/client/svelte/thumbnail';
<\/script>
<a href="/experiences/{experience.slug}" aria-label={experience.title}>
<ExperienceThumbnail galleryThumb={experience.galleryThumb} alt="" />
</a> Click behaviour
| Provide | Click does |
|---|---|
experienceId (no href/onclick) | Open hosted player; return to this page |
href | Navigate to that URL |
onclick / onClick | Run your handler (modal, analytics, SPA goto) |
| None of the above | Non-interactive preview |
Priority: href > click-only > hosted default > presentational.
Hosted launch is for public experiences. Authenticated gallery cards should use href / onclick plus your BFF session route — not the hosted player
default.
Props
| Prop | Type | Notes |
|---|---|---|
galleryThumb | GalleryThumb | Catalog shape: avatarImage, optional backgroundImage / foregroundImage — paths or URLs |
avatarImage | string | Flattened alternative to galleryThumb |
alt | string | Accessible name (use "" when a parent link names the card) |
experienceId | string | Required for hosted-player default |
href | string | App-owned destination; disables hosted default |
onclick / onClick | (event) => void | Custom handler; may preventDefault with href |
returnUrl | string | Hosted close destination; default = current page URL |
parallax | boolean | Default true; respects prefers-reduced-motion |
lazy | boolean | Default true; IntersectionObserver load |
backgroundImage / foregroundImage | string | Full URL, or a Liforma path such as /locations/…/scene (the component picks the plate size) |
cdnBaseUrl | string | Optional; default https://cdn.liforma.ai for catalog paths |
fallbackImage | string | Flat image if the layered avatar fails |
Layout and image size
The component fills its container. Set width / aspect-ratio on the parent — there is no size prop. Pass catalog paths (no host, no plate size) and the component expands them:
- Avatars:
/avatars/{id}or/avatars/{id}/256/…webp→ CDN/256/plates - Locations:
/locations/…/scene→ square hosts use256x256; landscape hosts use800x450(or1672x941when the box is larger)
Absolute URLs still work if you already have them. Prefer catalog galleryThumb over flat thumbnailPath.
Gallery walkthrough
For a full SvelteKit catalog → cards → slug → <Experience> flow, see Dynamic Experience Gallery.