npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@sitecore-marketplace-sdk/client

v0.2.2

Published

The `client` package provides secure, bidirectional communication between a Marketplace application (the client) and Sitecore (the host). Sitecore loads the Marketplace app inside a sandboxed [iframe](https://developer.mozilla.org/en-US/docs/Web/HTML/Elem

Downloads

2,067

Readme

Sitecore Marketplace SDK - client package

The client package provides secure, bidirectional communication between a Marketplace application (the client) and Sitecore (the host). Sitecore loads the Marketplace app inside a sandboxed iframe. The iframe and its parent window securely communicate using the web browser's PostMessage API.

This package lets you:

  • Make queries. Queries support one-off data requests and subscriptions for live updates. The client package lets you query the host's state and environment, and the context of your Marketplace app.
  • Make mutations. Mutations trigger state changes or HTTP requests in Sitecore.
  • Interact with Sitecore APIs to perform actions on behalf of the resource access it was granted during installation.

    [!TIP] Inspired by GraphQL and React Query, the query/mutation API manages internal state, loading status, and error handling.

The client package is required for all Marketplace apps.

Prerequisites

  • Node.js 16 or later. Check your installed version by using the node --version command.
  • npm 10 or later. Check your installed version by using the npm --version command.
  • Access to the Sitecore Cloud Portal.

Installation

npm install @sitecore-marketplace-sdk/client

Initialization

Before you use queries or mutations, you must initialize the Client SDK.

  1. Create a hook that will handle the initialization:
// utils/hooks/useMarketplaceClient.ts

import { ClientSDK } from '@sitecore-marketplace-sdk/client';
import { useEffect, useState, useCallback, useMemo, useRef } from 'react';

export interface MarketplaceClientState {
  client: ClientSDK | null;
  error: Error | null;
  isLoading: boolean;
  isInitialized: boolean;
}

export interface UseMarketplaceClientOptions {
  /**
   * Number of retry attempts when initialization fails
   * @default 3
   */
  retryAttempts?: number;

  /**
   * Delay between retry attempts in milliseconds
   * @default 1000
   */
  retryDelay?: number;

  /**
   * Whether to automatically initialize the client
   * @default true
   */
  autoInit?: boolean;
}

const DEFAULT_OPTIONS: Required<UseMarketplaceClientOptions> = {
  retryAttempts: 3,
  retryDelay: 1000,
  autoInit: true,
};

let client: ClientSDK | undefined = undefined;

async function getMarketplaceClient() {
  if (client) {
    return client;
  }

  const config = {
    target: window.parent,
  };

  client = await ClientSDK.init(config);
  return client;
}

export function useMarketplaceClient(options: UseMarketplaceClientOptions = {}) {
  // Memoize the options to prevent unnecessary re-renders
  const opts = useMemo(() => ({ ...DEFAULT_OPTIONS, ...options }), [options]);

  const [state, setState] = useState<MarketplaceClientState>({
    client: null,
    error: null,
    isLoading: false,
    isInitialized: false,
  });

  // Use ref to track if we're currently initializing to prevent race conditions
  const isInitializingRef = useRef(false);

  const initializeClient = useCallback(
    async (attempt = 1): Promise<void> => {
      // Use functional state update to check current state without dependencies
      let shouldProceed = false;
      setState((prev) => {
        if (prev.isLoading || prev.isInitialized || isInitializingRef.current) {
          return prev;
        }
        shouldProceed = true;
        isInitializingRef.current = true;
        return { ...prev, isLoading: true, error: null };
      });

      if (!shouldProceed) return;

      try {
        const client = await getMarketplaceClient();
        setState({
          client,
          error: null,
          isLoading: false,
          isInitialized: true,
        });
      } catch (error) {
        if (attempt < opts.retryAttempts) {
          await new Promise((resolve) => setTimeout(resolve, opts.retryDelay));
          return initializeClient(attempt + 1);
        }

        setState({
          client: null,
          error:
            error instanceof Error ? error : new Error('Failed to initialize MarketplaceClient'),
          isLoading: false,
          isInitialized: false,
        });
      } finally {
        isInitializingRef.current = false;
      }
    },
    [opts.retryAttempts, opts.retryDelay],
  ); // Removed state dependencies

  useEffect(() => {
    if (opts.autoInit) {
      initializeClient();
    }

    return () => {
      isInitializingRef.current = false;
      setState({
        client: null,
        error: null,
        isLoading: false,
        isInitialized: false,
      });
    };
  }, [opts.autoInit, initializeClient]);

  // Memoize the return value to prevent object recreation on every render
  return useMemo(
    () => ({
      ...state,
      initialize: initializeClient,
    }),
    [state, initializeClient],
  );
}
  1. Initialize the SDK:
// src/pages/index.tsx

import { useState, useEffect } from "react";
import type { ApplicationContext } from "@sitecore-marketplace-sdk/client";
import { useMarketplaceClient } from "@/utils/hooks/useMarketplaceClient";

function App() {
  const { client, error, isInitialized } = useMarketplaceClient();
  const [appContext, setAppContext] = useState<ApplicationContext>();

  useEffect(() => {
    if (!error && isInitialized && client) {
      console.log("Marketplace client initialized successfully.");

      // Make a query to retrieve the application context
      client.query("application.context")
        .then((res) => {
          console.log("Success retrieving application.context:", res.data);
          setAppContext(res.data);
        })
        .catch((error) => {
          console.error("Error retrieving application.context:", error);
        });
    } else if (error) {
      console.error("Error initializing Marketplace client:", error);
    }
  }, [client, error, isInitialized]);

  return (
    <>
      <h1>Welcome to {appContext?.name}</h1>
    </>
  );
}

export default App;

Usage

Make a query

Use the query method to make one-off data requests and live subscriptions. Pass a value to the method depending on the data you want to retrieve.

For example, pass 'application.context' to retrieve details about the Marketplace app and the Sitecore host, including, for example, your Context IDs:

client
  .query('application.context')
  .then((res) => {
    console.log('Success retrieving application.context:', res.data);
    setAppContext(res.data);
  })
  .catch((error) => {
    console.error('Error retrieving application.context:', error);
  });

The application context provides information about your Marketplace app, such as its ID, URL, name, type, icon URL, installation ID, and associated resource access:

{
   id: 'my-app-id',
   name: 'My App',
   type: 'portal',
   url: 'https://my-app.com/app',
   iconUrl: 'https://my-app.com/assets/icon.png',
   installationId: 'abc1234567890',
   resourceAccess: [
     {
       resourceId: 'resource-1',
       tenantId: 'tenant-1',
       tenantName: 'Example Tenant',
       context: {
         live: '1234567890',
         preview: '0987654321'
       }
     }
   ]
}

For an overview of all the possible values, refer to the QueryMap interface.

Make a mutation

Use the mutate method to trigger changes in Sitecore (the host). Pass a value to the method depending on the change you want to make.

For example, pass 'pages.reloadCanvas' to reload the XM Cloud page builder canvas:

const reloadCanvas = () => {
  client?.mutate('pages.reloadCanvas');
};

For an overview of all the possible values, refer to the MutationMap interface.

[!NOTE] Behind the scenes, the Host SDK (integrated via the internal core package) attaches the required user token and performs the HTTP request on behalf of the Marketplace app (the client).

Documentation

For more information, refer to the reference documentation in the /docs folder, and the official Marketplace developer documentation and Marketplace SDK documentation.

License

This package is part of the Sitecore Marketplace SDK, licensed under the Apache 2.0 License. Refer to the LICENSE file in the repository root.

Status

The client package is actively maintained as part of the Sitecore Marketplace SDK.