diff --git a/registries/marketplace/react/registry.json b/registries/marketplace/react/registry.json
index 3219a96f..5b48a668 100644
--- a/registries/marketplace/react/registry.json
+++ b/registries/marketplace/react/registry.json
@@ -1,23 +1,33 @@
{
"$schema": "https://ui.shadcn.com/schema/registry.json",
- "name": "sitecore marketplace",
+ "name": "Sitecore Marketplace",
"homepage": "https://blok.sitecore.com",
"items": [
{
"name": "quickstart",
"type": "registry:block",
- "title": "Quickstart for React SPA Sitecore marketplace apps with marketplace sdk",
- "description": "A quickstart for next.js Sitecore marketplace apps with marketplace sdk",
+ "title": "Quickstart for React/Vite Sitecore Marketplace apps",
+ "description": "A quickstart for React/Vite Sitecore Marketplace apps with the Marketplace SDK",
"dependencies": ["@sitecore-marketplace-sdk/client"],
+ "registryDependencies": [
+ "https://blok.sitecore.com/r/badge.json",
+ "https://blok.sitecore.com/r/card.json",
+ "https://blok.sitecore.com/r/collapsible.json"
+ ],
"files": [
{
"path": "src/marketplace/react/components/providers/marketplace.tsx",
- "type": "registry:file",
- "target": "app/providers/marketplace.tsx"
+ "type": "registry:component",
+ "target": "components/providers/marketplace.tsx"
},
{
- "path": "src/marketplace/react/components/examples/example.tsx",
- "type": "registry:file",
+ "path": "src/marketplace/react/components/examples/built-in-auth/application-context.tsx",
+ "type": "registry:component",
+ "target": "components/examples/built-in-auth/application-context.tsx"
+ },
+ {
+ "path": "src/marketplace/react/components/examples/built-in-auth/example.tsx",
+ "type": "registry:page",
"target": "app/page.tsx"
}
]
@@ -25,29 +35,76 @@
{
"name": "quickstart-with-xmc",
"type": "registry:block",
- "title": "Examples for React SPA Sitecore marketplace apps with marketplace sdk",
- "description": "Examples for next.js Sitecore marketplace apps with marketplace sdk",
+ "title": "Quickstart for React/Vite with XMC API examples",
+ "description": "A quickstart for React/Vite Sitecore Marketplace apps with XMC client-side API examples",
"dependencies": ["@sitecore-marketplace-sdk/xmc"],
"registryDependencies": [
- "https://blok.sitecore.com/r/marketplace/react/quickstart.json"
+ "https://blok.sitecore.com/r/marketplace/react/quickstart.json",
+ "https://blok.sitecore.com/r/button.json",
+ "https://blok.sitecore.com/r/alert.json",
+ "https://blok.sitecore.com/r/skeleton.json",
+ "https://blok.sitecore.com/r/separator.json"
],
"files": [
{
"path": "src/marketplace/react/components/providers/marketplace-w-xmc.tsx",
- "type": "registry:file",
+ "type": "registry:component",
"target": "components/providers/marketplace.tsx"
},
{
- "path": "src/marketplace/react/components/examples/with-xmc/list-languages.tsx",
- "type": "registry:file",
- "target": "components/examples/with-xmc/list-languages.tsx"
+ "path": "src/marketplace/react/components/examples/built-in-auth/with-xmc/list-languages.tsx",
+ "type": "registry:component",
+ "target": "components/examples/built-in-auth/with-xmc/list-languages.tsx"
},
{
- "path": "src/marketplace/react/components/examples/with-xmc/example.tsx",
- "type": "registry:file",
+ "path": "src/marketplace/react/components/examples/built-in-auth/with-xmc/example.tsx",
+ "type": "registry:page",
"target": "app/page.tsx"
}
]
+ },
+ {
+ "name": "quickstart-with-custom-auth",
+ "type": "registry:block",
+ "title": "Quickstart for React/Vite with Auth0 SPA",
+ "description": "Auth0 client-side authentication for Sitecore Marketplace apps on Vite. This is SPA auth only—no Next.js middleware, route handlers, or server sessions.",
+ "dependencies": ["@auth0/auth0-react"],
+ "registryDependencies": [
+ "https://blok.sitecore.com/r/marketplace/react/quickstart.json"
+ ],
+ "files": [
+ {
+ "path": "src/marketplace/react/vite-env.d.ts",
+ "type": "registry:file",
+ "target": "vite-env.d.ts"
+ },
+ {
+ "path": "src/marketplace/react/lib/auth/env.ts",
+ "type": "registry:file",
+ "target": "lib/auth/env.ts"
+ },
+ {
+ "path": "src/marketplace/react/components/providers/auth.tsx",
+ "type": "registry:component",
+ "target": "components/providers/auth.tsx"
+ },
+ {
+ "path": "src/marketplace/react/components/providers/with-custom-auth.tsx",
+ "type": "registry:component",
+ "target": "components/providers/with-custom-auth.tsx"
+ }
+ ],
+ "envVars": {
+ "VITE_AUTH0_DOMAIN": "https://auth.sitecorecloud.io",
+ "VITE_AUTH0_AUDIENCE": "https://api-webapp.sitecorecloud.io",
+ "VITE_AUTH0_SCOPE": "openid profile email offline_access",
+ "VITE_AUTH0_CLIENT_ID": "your-marketplace-client-id",
+ "VITE_SITECORE_APP_ID": "your-app-id",
+ "VITE_APP_BASE_URL": "https://localhost:5173",
+ "VITE_SITECORE_ORGANIZATION_ID": "your-organization-id",
+ "VITE_SITECORE_TENANT_ID": "marketplace-app-tenant-id-after-installation"
+ },
+ "docs": "Use HTTPS in development (e.g. vite-plugin-mkcert or a reverse proxy). Wrap your root with MarketplaceAppProvidersWithCustomAuth from components/providers/with-custom-auth instead of MarketplaceProvider alone. Set all VITE_* variables in .env. XMC examples in this registry use the Marketplace SDK from the browser only (quickstart-with-xmc); there is no server-side or backend-proxy block."
}
]
}
diff --git a/src/marketplace/react/components/examples/built-in-auth/application-context.tsx b/src/marketplace/react/components/examples/built-in-auth/application-context.tsx
new file mode 100644
index 00000000..034ad197
--- /dev/null
+++ b/src/marketplace/react/components/examples/built-in-auth/application-context.tsx
@@ -0,0 +1,44 @@
+import { useAppContext } from "@/components/providers/marketplace";
+import { Badge } from "@/components/ui/badge";
+import { CardTitle } from "@/components/ui/card";
+import {
+ Collapsible,
+ CollapsibleContent,
+ CollapsibleTrigger,
+} from "@/components/ui/collapsible";
+import { ChevronDown, ChevronRight } from "lucide-react";
+import { useState } from "react";
+
+export const ApplicationContext = () => {
+ const appContext = useAppContext();
+ const [isExpanded, setIsExpanded] = useState(false);
+ return (
+
+
+
+
+
+ Application Context
+
+ Client-side
+ SDK Built-in Auth
+
+ {isExpanded ? (
+
+ ) : (
+
+ )}
+
+
+
+
+ {JSON.stringify(appContext, null, 2)}
+
+
+
+ );
+};
diff --git a/src/marketplace/react/components/examples/built-in-auth/example.tsx b/src/marketplace/react/components/examples/built-in-auth/example.tsx
new file mode 100644
index 00000000..392d3ffd
--- /dev/null
+++ b/src/marketplace/react/components/examples/built-in-auth/example.tsx
@@ -0,0 +1,20 @@
+import { ApplicationContext } from "@/components/examples/built-in-auth/application-context";
+
+function Examples() {
+ return (
+
+
+
+ Marketplace SDK Demo
+
+
+ Marketplace SDK with built-in authentication
+
+
+
+
+
+ );
+}
+
+export default Examples;
diff --git a/src/marketplace/react/components/examples/built-in-auth/with-xmc/example.tsx b/src/marketplace/react/components/examples/built-in-auth/with-xmc/example.tsx
new file mode 100644
index 00000000..a499550d
--- /dev/null
+++ b/src/marketplace/react/components/examples/built-in-auth/with-xmc/example.tsx
@@ -0,0 +1,32 @@
+import { ApplicationContext } from "@/components/examples/built-in-auth/application-context";
+import { ListLanguagesFromClientSdk } from "@/components/examples/built-in-auth/with-xmc/list-languages";
+import { Separator } from "@/components/ui/separator";
+
+function Examples() {
+ return (
+
+
+
+ Marketplace SDK Demo
+
+
+ Marketplace SDK with built-in authentication and XMC client-side
+ examples
+
+
+
+
+
+
+
+
+
Built-in Auth Examples
+
+
+
+
+
+ );
+}
+
+export default Examples;
diff --git a/src/marketplace/react/components/examples/built-in-auth/with-xmc/list-languages.tsx b/src/marketplace/react/components/examples/built-in-auth/with-xmc/list-languages.tsx
new file mode 100644
index 00000000..3d691d08
--- /dev/null
+++ b/src/marketplace/react/components/examples/built-in-auth/with-xmc/list-languages.tsx
@@ -0,0 +1,109 @@
+import {
+ useAppContext,
+ useMarketplaceClient,
+} from "@/components/providers/marketplace";
+import { Alert, AlertDescription } from "@/components/ui/alert";
+import { Badge } from "@/components/ui/badge";
+import { Button } from "@/components/ui/button";
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardHeader,
+ CardTitle,
+} from "@/components/ui/card";
+import { Skeleton } from "@/components/ui/skeleton";
+import type { Xmapp } from "@sitecore-marketplace-sdk/xmc";
+import { useState } from "react";
+
+export const ListLanguagesFromClientSdk = () => {
+ const client = useMarketplaceClient();
+ const appContext = useAppContext();
+ const [languages, setLanguages] = useState([]);
+ const [loading, setLoading] = useState(false);
+ const [error, setError] = useState(null);
+
+ const fetchLanguages = async () => {
+ setLoading(true);
+ setError(null);
+
+ const contextId = appContext?.resourceAccess?.[0]?.context
+ ?.preview as string;
+ if (!contextId) {
+ setError("No context ID available");
+ setLoading(false);
+ return;
+ }
+
+ const data = {
+ query: {
+ sitecoreContextId: contextId,
+ },
+ };
+
+ try {
+ const languagesResponse = await client.query("xmc.sites.listLanguages", {
+ params: data,
+ });
+ console.log("languages from client", languagesResponse);
+ setLanguages(languagesResponse.data?.data ?? []);
+ } catch (err) {
+ console.log("error from client", err);
+ setError(err instanceof Error ? err.message : "An error occurred");
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ return (
+
+
+
+ Client SDK Example
+ Client-side
+ SDK Built-in Auth
+
+
+ Fetch languages using Sitecore Marketplace SDK from client component
+
+
+
+
+
+ {error && (
+
+ {error}
+
+ )}
+
+
+
+ Languages found:
+ {loading ? (
+
+ ) : (
+ {languages?.length || 0}
+ )}
+
+
+ {languages.length > 0 && (
+
+
Language names:
+
+ {languages.map((language, index) => (
+ {language.name}
+ ))}
+
+
+ )}
+
+
+
+ );
+};
diff --git a/src/marketplace/react/components/examples/example.tsx b/src/marketplace/react/components/examples/example.tsx
index fb3b9be6..4d0e9e01 100644
--- a/src/marketplace/react/components/examples/example.tsx
+++ b/src/marketplace/react/components/examples/example.tsx
@@ -1,13 +1 @@
-import { useAppContext } from "@/components/providers/marketplace";
-
-function ClientSideExamples() {
- const appContext = useAppContext();
- return (
-
-
Application Context
-
{JSON.stringify(appContext)}
-
- );
-}
-
-export default ClientSideExamples;
+export { default } from "@/components/examples/built-in-auth/example";
diff --git a/src/marketplace/react/components/examples/with-xmc/example.tsx b/src/marketplace/react/components/examples/with-xmc/example.tsx
index cade93fa..b7a6b502 100644
--- a/src/marketplace/react/components/examples/with-xmc/example.tsx
+++ b/src/marketplace/react/components/examples/with-xmc/example.tsx
@@ -1,17 +1 @@
-import { ListLanguagesFromClientSdk } from "@/components/examples/with-xmc/list-languages";
-import { useAppContext } from "@/components/providers/marketplace";
-
-function ClientSideExamples() {
- const appContext = useAppContext();
- return (
-
-
Application Context
-
{JSON.stringify(appContext)}
-
-
Different ways to call XMC APIs from the browser
-
-
- );
-}
-
-export default ClientSideExamples;
+export { default } from "@/components/examples/built-in-auth/with-xmc/example";
diff --git a/src/marketplace/react/components/examples/with-xmc/list-languages.tsx b/src/marketplace/react/components/examples/with-xmc/list-languages.tsx
index a4e35435..0cf82a8f 100644
--- a/src/marketplace/react/components/examples/with-xmc/list-languages.tsx
+++ b/src/marketplace/react/components/examples/with-xmc/list-languages.tsx
@@ -1,45 +1 @@
-import {
- useAppContext,
- useMarketplaceClient,
-} from "@/components/providers/marketplace";
-import type { Xmapp } from "@sitecore-marketplace-sdk/xmc";
-import { useState } from "react";
-
-export const ListLanguagesFromClientSdk = () => {
- const client = useMarketplaceClient();
- const appContext = useAppContext();
- const [languages, setLanguages] = useState([]);
-
- const fetchLanguages = async () => {
- const contextId = appContext?.resourceAccess?.[0]?.context
- ?.preview as string;
- if (!contextId) {
- return;
- }
-
- const data = {
- query: {
- sitecoreContextId: contextId,
- },
- };
-
- try {
- const languagesResponse = await client.query("xmc.sites.listLanguages", {
- params: data,
- });
- console.log("languages from client", languagesResponse);
- setLanguages(languagesResponse.data?.data ?? []);
- } catch (err) {
- console.log("error from client", err);
- }
- };
-
- return (
-
-
Client API Request
-
-
Languages: {languages.length}
-
{languages.map((language) => language.name)}
-
- );
-};
+export { ListLanguagesFromClientSdk } from "@/components/examples/built-in-auth/with-xmc/list-languages";
diff --git a/src/marketplace/react/components/providers/auth.tsx b/src/marketplace/react/components/providers/auth.tsx
new file mode 100644
index 00000000..f7600889
--- /dev/null
+++ b/src/marketplace/react/components/providers/auth.tsx
@@ -0,0 +1,55 @@
+import {
+ type Auth0ContextInterface,
+ Auth0Provider,
+ type GetTokenSilentlyOptions,
+ useAuth0,
+ withAuthenticationRequired,
+} from "@auth0/auth0-react";
+import {
+ getAuth0AuthorizationParams,
+ getAuth0ClientEnv,
+ getSitecoreTokenParams,
+} from "@/lib/auth/env";
+import type React from "react";
+
+export const WithAuth = withAuthenticationRequired(
+ ({ children }: { children: React.ReactNode }) => children,
+);
+
+export const AuthProvider = ({ children }: { children: React.ReactNode }) => {
+ const { domain, clientId } = getAuth0ClientEnv();
+ const authParams = getAuth0AuthorizationParams();
+
+ return (
+
+ {children}
+
+ );
+};
+
+export const useAuth = (): Auth0ContextInterface => {
+ const { getAccessTokenSilently, ...rest } = useAuth0();
+ const sitecoreParams = getSitecoreTokenParams();
+
+ const customGetAccessTokenSilently = (options?: GetTokenSilentlyOptions) => {
+ return getAccessTokenSilently({
+ ...options,
+ authorizationParams: {
+ ...options?.authorizationParams,
+ ...sitecoreParams,
+ },
+ });
+ };
+
+ return {
+ ...rest,
+ getAccessTokenSilently:
+ customGetAccessTokenSilently as Auth0ContextInterface["getAccessTokenSilently"],
+ };
+};
diff --git a/src/marketplace/react/components/providers/with-custom-auth.tsx b/src/marketplace/react/components/providers/with-custom-auth.tsx
new file mode 100644
index 00000000..3815a71f
--- /dev/null
+++ b/src/marketplace/react/components/providers/with-custom-auth.tsx
@@ -0,0 +1,19 @@
+import { AuthProvider } from "@/components/providers/auth";
+import { MarketplaceProvider } from "@/components/providers/marketplace";
+import type { ReactNode } from "react";
+
+/**
+ * Wrap your Vite app root with this instead of only {@link MarketplaceProvider}
+ * when using the custom Auth0 SPA flow from the registry quickstart-with-custom-auth block.
+ */
+export function MarketplaceAppProvidersWithCustomAuth({
+ children,
+}: {
+ children: ReactNode;
+}) {
+ return (
+
+ {children}
+
+ );
+}
diff --git a/src/marketplace/react/lib/auth/env.ts b/src/marketplace/react/lib/auth/env.ts
new file mode 100644
index 00000000..0d2feb74
--- /dev/null
+++ b/src/marketplace/react/lib/auth/env.ts
@@ -0,0 +1,30 @@
+export function getAuth0ClientEnv(): { domain: string; clientId: string } {
+ const domain = import.meta.env.VITE_AUTH0_DOMAIN;
+ const clientId = import.meta.env.VITE_AUTH0_CLIENT_ID;
+ if (!domain || !clientId) {
+ throw new Error(
+ "Auth0 domain and client ID are required (VITE_AUTH0_DOMAIN, VITE_AUTH0_CLIENT_ID)",
+ );
+ }
+ return { domain, clientId };
+}
+
+export function getAuth0AuthorizationParams(): Record {
+ const appId = import.meta.env.VITE_SITECORE_APP_ID;
+ return {
+ organization_id: import.meta.env.VITE_SITECORE_ORGANIZATION_ID,
+ tenant_id: import.meta.env.VITE_SITECORE_TENANT_ID,
+ product_codes: appId ? `mkp_${appId}` : undefined,
+ audience: import.meta.env.VITE_AUTH0_AUDIENCE,
+ redirect_uri: import.meta.env.VITE_APP_BASE_URL,
+ scope: import.meta.env.VITE_AUTH0_SCOPE,
+ };
+}
+
+/** Passed through to silent token calls (organization / tenant). */
+export function getSitecoreTokenParams(): Record {
+ return {
+ organization_id: import.meta.env.VITE_SITECORE_ORGANIZATION_ID,
+ tenant_id: import.meta.env.VITE_SITECORE_TENANT_ID,
+ };
+}
diff --git a/src/marketplace/react/vite-env.d.ts b/src/marketplace/react/vite-env.d.ts
new file mode 100644
index 00000000..70453f91
--- /dev/null
+++ b/src/marketplace/react/vite-env.d.ts
@@ -0,0 +1,16 @@
+///
+
+interface ImportMetaEnv {
+ readonly VITE_AUTH0_DOMAIN?: string;
+ readonly VITE_AUTH0_CLIENT_ID?: string;
+ readonly VITE_AUTH0_AUDIENCE?: string;
+ readonly VITE_AUTH0_SCOPE?: string;
+ readonly VITE_SITECORE_APP_ID?: string;
+ readonly VITE_APP_BASE_URL?: string;
+ readonly VITE_SITECORE_ORGANIZATION_ID?: string;
+ readonly VITE_SITECORE_TENANT_ID?: string;
+}
+
+interface ImportMeta {
+ readonly env: ImportMetaEnv;
+}