---
title: "Quick start (manual)"
slug: "quick-start--manual-"
abstract: "Start developing for the Sitecore Marketplace locally by connecting your app to Sitecore. Build a client-side app with built-in authorization step-by-step, in an empty React or Next.js project."
---
# Quick start (manual)

This guide describes how to start developing for the Sitecore Marketplace locally by connecting a JavaScript or TypeScript app to Sitecore using the Marketplace SDK. This guide is for building a client-side[^UUID-9f3af1b0-4e64-fc35-eb7d-0b2a8bdb572c_N68c3cf9476daa] app with built-in authorization[^UUID-9f3af1b0-4e64-fc35-eb7d-0b2a8bdb572c_mp-builtinauth].

In this guide, you:

## Prerequisites

Check that you have the following before getting started:

- A Marketplace app [installed](https://doc.sitecore.com/mp/en/developers/marketplace/install-an-activated-custom-app.html) in your Sitecore Cloud Portal organization.
- [Node.js 16](https://nodejs.org/) or later. Check your installed version by using the `node --version` command.
- [npm 10](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm) or later. Check your installed version by using the `npm --version` command.

    > [!note]
    > We strongly recommend that you also install [Blok](https://blok.sitecore.com) in your app. Blok is the Sitecore product design system, and installing it is the easiest way you can give your app the Sitecore look and feel.
    >
    > Matching the Sitecore look and feel is required for public Marketplace apps and strongly recommended for custom Marketplace apps. This is to ensure a consistent user experience for the Sitecore product user.

## Create an app

If you don't already have an app, create a JavaScript or TypeScript one.

::::tabs
:::tab{title="React"}

To create a new JavaScript or TypeScript app, run the following command in your terminal:

```bash
npm create vite@latest
```

:::
:::tab{title="Next.js (Pages Router)"}

To create a new JavaScript or TypeScript app, run the following command in your terminal:

```bash
npx create-next-app@latest
```

When prompted, select:

- **Yes** for TypeScript and the `src/` directory.
- **No** for App Router and customizing the import alias.
- **Yes** or **No** for the other prompts, depending on your application requirements.

:::
:::tab{title="Next.js (App Router)"}

To create a new JavaScript or TypeScript app, run the following command in your terminal:

```bash
npx create-next-app@latest
```

When prompted, select:

- **Yes** for TypeScript, the `src/` directory, and App Router.
- **No** for customizing the import alias.
- **Yes** or **No** for the other prompts, depending on your application requirements.

:::
::::

## Install the packages

To install the Marketplace SDK packages in your app, run the following commands:

```bash
npm install @sitecore-marketplace-sdk/client # Required for all Marketplace apps
npm install @sitecore-marketplace-sdk/xmc # Required only if your app uses SitecoreAI APIs
npm install @sitecore-marketplace-sdk/ai # Required only if your app uses AI skills APIs
```

## Initialize the SDK

To initialize the Marketplace SDK:

1. In your code editor, in the `src` folder of your app, create a hook, for example, `/utils/hooks/useMarketplaceClient.ts`.
2. Paste the following code into your new hook:

    :::tab{title="react"}
    ### React and Next.js

    ```typescript
    // 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]);
    }
    ```

    :::

    This script manages the initialization and state of the SDK and sets up communication with `window.parent` so the app can run in an iframe.

3. In your app's main page, such as `App.tsx` or `pages/index.tsx`, initialize the Marketplace client in an Effect Hook and create your first query:

    :::tab{title="react"}
    ### React

    ```typescript
    // src/App.tsx

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

    export default 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]);

      if (!appContext) {
        return null;
      }

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

    :::
    :::tab{title="nextjs-pages"}
    ### Next.js (Pages Router)

    ```typescript
    // src/pages/index.tsx

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

    export default 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]);

      if (!appContext) {
        return null;
      }

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

    :::
    :::tab{title="nextjs-app"}
    ### Next.js (App Router)

    ```typescript
    // src/app/page.tsx

    "use client";

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

    export default function Home() {
      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]);

      if (!appContext) {
        return null;
      }

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

    :::

    This script:

    - Uses an Effect Hook to check if the Marketplace client is initialized.
    - Makes a query to retrieve details about your Marketplace app: `client.query("application.context")`
    - Extracts the app details from the API response and stores it in state: `setAppContext(res.data);`
    - Displays the app name in the user interface: `<h1>Welcome to {appContext?.name}</h1>`
4. Start your app by entering the following command in your terminal:

    ```bash
    npm run dev
    ```

    > [!note]
    > Start your app on the same localhost address that you specified during [app configuration](https://doc.sitecore.com/mp/en/developers/marketplace/configure-and-activate-a-custom-app.html).

## Open your app in Sitecore

After initializing the SDK, you open your Marketplace app in a [Sitecore extension point](https://doc.sitecore.com/mp/en/developers/marketplace/extension-points.html) you selected for it during [app configuration](https://doc.sitecore.com/mp/en/developers/marketplace/configure-and-activate-a-custom-app.html).

To open your app in Sitecore:

1. In your web browser, in the [Cloud Portal](https://portal.sitecorecloud.io/), find and open your Marketplace app in one of its extension points.

    Your app now appears in Sitecore and the two are securely communicating.

2. On the same page, open your console to find the logs included in the SDK initialization code.

    > [!note]
    > During development, trace console logs in your web browser's console in the Sitecore extension point, not on your app's localhost address.
    >
    > Similarly, always preview your app in the Sitecore extension point. Any functionality that requires communication with Sitecore, such as getting application details and calling SitecoreAI APIs, only works in the extension points.

## Next steps { .notintoc }

You've now displayed your app in Sitecore, set up communication between the two, and made your first query. Next, you can:

- Continue developing your app:
    - If your app needs to interact with the SitecoreAI APIs, [initialize the `xmc` package](https://doc.sitecore.com/mp/en/developers/sdk/0/sitecore-marketplace-sdk/initialize-the-xmc-package.html "Initialize the xmc package").
    - If your app needs to interact with AI skills APIs, [initialize the `ai` package](https://doc.sitecore.com/mp/en/developers/sdk/0/sitecore-marketplace-sdk/initialize-the-ai-package.html "Initialize the ai package").
    - [Make more queries](https://doc.sitecore.com/mp/en/developers/sdk/0/sitecore-marketplace-sdk/understanding-queries.html "Understanding queries") to retrieve data from Sitecore.
    - [Make mutations](https://doc.sitecore.com/mp/en/developers/sdk/0/sitecore-marketplace-sdk/understanding-mutations.html "Understanding mutations") to change data in Sitecore.
- Learn more about the Marketplace SDK with the [reference documentation](https://github.com/Sitecore/sitecore-marketplace-sdk/tree/main/docs).
- Explore [other tasks to complete](https://doc.sitecore.com/mp/en/developers/marketplace/en/introduction-to-sitecore-marketplace-for-custom-and-public-apps.html#UUID-1735fb7a-559b-d6af-ec6f-91456d2504c5_section-idm234813214660084) after you finish building your app.

[^UUID-9f3af1b0-4e64-fc35-eb7d-0b2a8bdb572c_N68c3cf9476daa]: A Marketplace application that uses a client-side architecture. Client-side apps run entirely in the user's web browser. All API requests from the app to SitecoreAI APIs run indirectly, via the web browser. Ideal for simple UI extensions and functionalities that don't require server-side processing.

[^UUID-9f3af1b0-4e64-fc35-eb7d-0b2a8bdb572c_mp-builtinauth]: An authorization flow for Marketplace applications that is managed entirely by the Marketplace SDK, abstracting it away from the developer.
