Skip to main content

Custom Marketplace UI

A custom marketplace UI presents your integrations with your app's own components instead of Prismatic's marketplace iframe. You can show integrations as cards, in a table, as a list of Connect buttons, or inside an existing settings page, and keep the same look, accessibility, and behavior as the rest of your app.

A custom marketplace of integration cards built with the app's own components, with search, a category filter, and status badges

Build a custom marketplace with Solis, Prismatic's custom UI SDK. Solis supplies the marketplace, the customer's instances, and actions to activate and manage them, and your app renders everything the customer sees. For a step-by-step walkthrough, see Build a custom marketplace with Solis.

A full example is the Headless Configuration page of the example embedded app, in headless-configuration.tsx.

Listing marketplace integrations​

In React, useMarketplace() returns the integrations available to the signed-in customer. Each item in data.items is its own resource, so a card can render as soon as its data loads:

List marketplace integrations with Solis React
import { useMarketplace } from "@prismatic-io/solis-react";

function Marketplace() {
const marketplace = useMarketplace({ includeActiveIntegrations: true });
if (marketplace.status === "loading") return <Spinner />;
if (marketplace.status === "error") return <p>{marketplace.error.message}</p>;

return marketplace.data.items.map((item) =>
item.status === "success" ? (
<IntegrationCard key={item.id} integration={item.data} />
) : null,
);
}

useMarketplace() accepts these filters. Results are ordered by category, then name, unless you pass ordering.

FilterDescription
searchIntegrations that match a search term
categoryIntegrations in one category
labelIntegrations with one label
activatedtrue for integrations the customer has activated, false for those they haven't
includeActiveIntegrationsAlso list integrations the customer has an instance of that aren't in your marketplace, such as a customer-specific integration
filterQueryA filter expression on name, category, or labels, as in advanced integration filters
orderingA list of { field, direction } sort orders, such as { field: "NAME", direction: "ASC" }

To build category and label filter controls, useMarketplaceFilterOptions() returns every distinct categories and labels value in your marketplace.

If your app doesn't use React, @prismatic-io/solis-core provides the same data and actions. See Building configuration UIs in Angular, Vue, and Svelte.

Integration data​

Each marketplace integration includes the data you need to display it:

PropertyDescription
id, nameThe integration's ID and name
description, overviewA short description, and the long-form marketplace copy, typically Markdown
category, labelsThe integration's category and labels
avatarUrlA path to the integration's icon. See Displaying integration avatars.
configurationExperienceheadless or hosted, for the latest version in your marketplace
instancesThe customer's instances of the integration, newest first, each an instance resource
allowMultipleInstancesWhether the customer can activate more than one instance
permissionscreateInstance, which says whether the customer can activate the integration, and if not, why

Each instance's lifecycle summarizes its status, so a card can show a badge such as Active or Paused:

LifecycleMeaning
notDeployedCreated, but never deployed
needsReconfigurationThe instance needs its configuration reviewed
needsUserConfigurationThe current user needs to save their user-level settings
pausedPaused
pendingChangesSaved changes haven't been deployed
activeRunning

Activating and configuring integrations​

To activate an integration, call createInstance on the integration's actions. The instance is notDeployed until the customer configures and deploys it:

const integration = useMarketplaceIntegration(integrationId);
// After checking integration.status and permissions.createInstance:
const result = await integration.actions.createInstance.execute({
name: integration.data.name,
});

Then load the instance's configuration with useConfiguration({ instanceId }), and route on its configurationExperience:

  • hosted: The integration uses config pages. Render <HostedConfiguration instanceId={instanceId} onClose={close} />, which shows Prismatic's config wizard inside your UI. When the wizard closes, Solis refreshes the instance and the marketplace.
  • headless: The integration uses headless configuration. Render your own form, then call init, save, and deploy. See Building configuration UIs with Solis React.

Use the configuration's configurationExperience, not the listing's, to choose. The listing describes the latest version in your marketplace, and an instance may run an earlier one.

Because HostedConfiguration handles integrations with config pages, a custom marketplace can list all of your integrations, whether or not they use headless configuration.

Managing instances​

useInstance(instanceId) returns an instance with actions to pause, resume, remove, upgrade, and deploy it. Each action has a permission of the same name in data.permissions. When a newer version is available, data.update describes it. See Updates.

One-click connect​

For integrations that need little configuration, a list of Connect buttons can replace a full marketplace. One click creates the instance, authorizes the customer's account with OAuth, saves the configuration the integration's init suggests, and deploys. See Build a one-click connect list with Solis.

Displaying integration avatars​

Each integration has an optional avatarUrl property, which you can use to fetch the integration's icon. Your avatarUrl looks something like /media/UUID/Integration/UUID/EnumMeta.AVATAR/UUID.png.

Make a request to your Prismatic URL with avatarUrl as the path (for example, https://app.prismatic.io/media/UUID/Integration/UUID/EnumMeta.AVATAR/UUID.png). Authenticate the request with the customer's embedded JWT in an Authorization: Bearer ${TOKEN} header. The response contains a short-lived, presigned URL for the image:

{ "url": "https://s3.us-west-2.amazonaws.com/PRESIGNED-URL" }

Set that URL as your image's src. This React component displays an integration's avatar, or the integration's initials when it has none:

Display an integration's avatar in React
import { useEffect, useState } from "react";

function IntegrationAvatar({
name,
avatarUrl,
token,
prismaticUrl = "https://app.prismatic.io",
}: {
name: string;
avatarUrl: string | null;
token: string;
prismaticUrl?: string;
}) {
const [src, setSrc] = useState("");

useEffect(() => {
if (!avatarUrl) return;
let mounted = true;
fetch(`${prismaticUrl}${avatarUrl}`, {
headers: { Authorization: `Bearer ${token}` },
})
.then((response) => response.json())
.then((body: { url?: string }) => {
if (mounted && body.url) setSrc(body.url);
})
.catch(() => {
// Keep the initials
});
return () => {
mounted = false;
};
}, [avatarUrl, token, prismaticUrl]);

return src ? (
<img src={src} alt="" width={40} height={40} />
) : (
<span>{name.slice(0, 2).toUpperCase()}</span>
);
}