Subchapter 1.21
references/context-provider/LOADING_PATTERN.mdMarkdown8 KBView on GitHub
Read the page’s own context first. This example fetches a Bookings service with the SDK only to show the loading pattern. When the data belongs to Stores, Blog, Bookings or Events and the site already provides that context (), read the hook instead: no SDK call and no field. A provider that only reads a vertical hook still exposes the loading pair, derived from the loading and error fields that context lists (for example and ). Use this pattern for data no context provides.
serviceIdisLoadinghasErrorOne provider, complete. Everything after this section explains a decision visible in it — read the code first, then the rules.
From the request “On my service page, show the name and price of the service from Wix Bookings, and show a grey placeholder bar in place of each while it loads. The Contact Us button and the header image should stay visible the whole time. Use context providers and native components, do not create an editor react component.”
@wix/bookings appears only because this is a Bookings example — the pattern does not depend on it. Substitute whatever data source the request names. The one package the pattern itself requires is @wix/react-use-promise-polyfill, for use().
src/extensions/site/contexts/booking-service-context/booking-service-context.tsx:
import React, {
Suspense,
createContext,
useContext,
useRef,
useState,
} from 'react';
import { use } from '@wix/react-use-promise-polyfill';
import { services } from '@wix/bookings';
import type { BooleanValue, Text } from '@wix/editor-react-types';
// Use the SDK's own exported entity type. Do NOT derive one from the method —
// see "Name the entity type, do not derive it" below.
type ServiceDetails = services
context, data and contextSpecifier in src/extensions/site/contexts/booking-service-context/booking-service-context.extension.ts (the rest of the file is generated by wix generate):
context: {
items: {
serviceName: {
dataType: 'text',
displayName: 'Service name',
description: 'Name of the service currently being shown. Bind to the service name text element content.',
},
servicePrice: {
dataType: 'text',
displayName: 'Service price',
description: 'The service price, already formatted in the site currency. Bind to the price text element content.',
},
isContentHidden: {
dataType: 'booleanValue',
displayName: 'Content is hidden',
description: 'True when no service is loaded. Bind to the hidden property of the name and price elements.',
},
isPlaceholderHidden: {
dataType: 'booleanValue',
displayName: 'Loading placeholders are hidden',
description: 'True when no fetch is in progress. Bind to the hidden property of the grey placeholder bars.',
},
showService: {
dataType: 'function',
displayName: 'Show a different service',
description: 'Loads another service and swaps it in without blanking the page. Bind to a service link or button click handler.',
function: {
parameters: [
{
dataType: 'text',
displayName: 'Service ID',
description: 'ID of the service to show.',
},
],
async: false,
},
},
},
},
data: {
serviceId: {
dataType: 'text',
deprecated: false,
displayName: 'Service ID',
},
},
resources: {
contextSpecifier: {
hook: 'useBookingServiceContext',
moduleSpecifier: '<moduleSpecifierPrefix>booking-service-context',
},
},Each text element’s content goes to serviceName or servicePrice (the platform converts text to rich text at the binding), and both elements’ hidden property to isContentHidden. The grey placeholder bars bind their hidden property to isPlaceholderHidden. The Contact Us button and header image are bound to nothing — no visibility binding, so they stay visible throughout.