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.

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:
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.
| Filter | Description |
|---|---|
search | Integrations that match a search term |
category | Integrations in one category |
label | Integrations with one label |
activated | true for integrations the customer has activated, false for those they haven't |
includeActiveIntegrations | Also list integrations the customer has an instance of that aren't in your marketplace, such as a customer-specific integration |
filterQuery | A filter expression on name, category, or labels, as in advanced integration filters |
ordering | A 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:
| Property | Description |
|---|---|
id, name | The integration's ID and name |
description, overview | A short description, and the long-form marketplace copy, typically Markdown |
category, labels | The integration's category and labels |
avatarUrl | A path to the integration's icon. See Displaying integration avatars. |
configurationExperience | headless or hosted, for the latest version in your marketplace |
instances | The customer's instances of the integration, newest first, each an instance resource |
allowMultipleInstances | Whether the customer can activate more than one instance |
permissions | createInstance, 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:
| Lifecycle | Meaning |
|---|---|
notDeployed | Created, but never deployed |
needsReconfiguration | The instance needs its configuration reviewed |
needsUserConfiguration | The current user needs to save their user-level settings |
paused | Paused |
pendingChanges | Saved changes haven't been deployed |
active | Running |
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 callinit,save, anddeploy. 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:
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>
);
}