Subchapter 1.164
references/LIGHTBOX.mdMarkdown10 KBView on GitHub
Studio 2 editor only. This extension type works only on sites built in the Studio 2 editor. It does not run on Harmony, the classic Wix Editor or Wix Studio (Editor X), so do not offer it for those sites.
A lightbox in Studio 2 is a popup page: a page that opens over the current page. Code opens it, closes it, and passes it data through the lightbox service (@wix/site-service-lightbox). The rules below apply to every extension that renders on the site. Build the extension itself from its own reference (for a context provider, CONTEXT_PROVIDER.md); this file adds only the popup rules.
@wix/site-window.window.openLightbox(), lightbox.getContext() and lightbox.close() from @wix/site-window are Velo frontend APIs. They do not run in the extension: Studio 2 pages have no window.clientSdk, the bridge these calls need, so every call rejects with Wix Site SDK only works in a Wix site environment and the popup never opens. Nothing on the page shows the unhandled rejection.
Read the services with useService:
import { useService } from '@wix/services-manager-react';
import { LightboxDefinition, type ILightboxService } from '@wix/site-service-lightbox/definition';
import { PagesDefinition, type IPagesService } from '@wix/site-service-pages/definition';Install these as direct dependencies: @wix/site-service-lightbox and @wix/site-service-pages at their latest versions, and @wix/services-manager-react. The project often has an older @wix/site-service-pages only as a transitive dependency, whose types have no slug field; installing it directly fixes that. @wix/services-manager-react is usually only a transitive dependency too, so add it directly. This skill is the reason to add them, so a “do not invent imports” rule in another skill does not block them.
Version 2 renamed
contextandgetContext()topayloadandgetPayload(), but the Studio 2 host provides only the old ones (open,close,getContext).getPayload()throwsTypeError: lightbox?.getPayload is not a function, andopen({ slug, payload })alone opens the popup with no data and no error. Only a run shows it:tscandwix buildpass. Keep version 2 and send the data under both keys, then read it with the helper in Reading the Payload.
A live site loads a service only when an extension on the page declares it. The editor preview loads every service, so code that skips the declaration works in preview and fails on the published site: useService throws Service @wix/site-service-lightbox is not provided.
Add both services to the extension’s serviceDependencies, next to any contextDependencies it already has, in every resource that renders:
dependencies: {
serviceDependencies: ['@wix/site-service-lightbox', '@wix/site-service-pages'],
},For a context provider, this is resources.dependencies. For an Editor React Component, it is resources.client.dependencies and resources.editor.dependencies.
The lightbox service opens a popup by its slug, but the editor generates the slug (popup-xxnez-evf5t-1-1-1-1-2-2-1-5) and a site owner can change it. The popup’s title (“New Popup”) can also change, and two popups can share it. Only the popup ID (skx1r) stays the same. It is the popupId that the editor reports when the popup is created or listed, and the key of the pages service’s pages map.
popupId text field in the manifest data section, with no defaultValue. The editor sets it; a manifest defaultValue does not reach an extension that is already on the page, which receives ''. Never hard-code a popup ID, slug, or title in code.pages.pages[popupId], and check that its popup is true.lightbox.open({ slug: page.slug, payload: data, context: data }) (the same object under both keys).open(). It rejects when no popup has the slug or the popup page fails to load. Never leave it as void lightbox.open(...).open() rejects, report it: a derived boolean such as isPopupErrorHidden for a context provider, or rendered error text for a component.undefined).useEffect when the derived condition becomes true, once.useState(() => readPayload<MyPopupPayload>(lightbox)). It is synchronous and returns undefined when no popup is open, and in the editor. The type is not checked against what open() sent.function readPayload<T>(lightbox: ILightboxService | undefined): T | undefined {
// Some Studio 2 hosts only provide getContext, the deprecated name of getPayload.
return typeof lightbox?.getPayload === 'function'
? lightbox.getPayload<T | undefined>()
: lightbox?.getContext<T | undefined>();
}'') when the context is missing, and show an empty state for that case.lightbox.close() only when the request asks for it — a “Done” button, after “Copy code”, after a timer. The popup’s own close button and the Escape key need no code.useService throws when the page has no services manager or does not provide the service — for example in the editor canvas, or on a live site without the manifest declaration. Wrap each service in a hook that returns undefined instead, and make every action a safe no-op when its service is missing:
function useLightbox(): ILightboxService | undefined {
try {
return useService(LightboxDefinition);
} catch {
return undefined;
}
}Call these hooks in a component that does not suspend, and pass the services down, so a loading fallback and the loaded content share them.
isSelectionHidden, isEmptyStateHidden), per Derived Values.closePopup (a no-parameter function) when the request closes the popup from code.close() returns nothing, and open() resolves when the popup opens, not when it closes. If the opener must react to something done in the popup, the popup saves it (a CMS write, a backend call) and the opener reads it from there.popupId are editor work. Name each one in your final message.@wix/site-service-lightbox, @wix/site-service-pages (both latest) and @wix/services-manager-react as direct dependenciesserviceDependencies for both services to every rendering resource of the manifestpopupId text field to the manifest data sectionpayload and context with everything the popup shows, caught rejection, exposed failurereadPayload() once on mount, fallbacks and empty state, close() when askedpopupId, and bind or connect the elements