Paragon
Search
K
Links

Building an In-App Integrations Catalog

In this tutorial, we'll use the Paragon SDK to build an integrations catalog into your app.
This guide provides an example implementation of building an integrations catalog into your React app, using TypeScript. For a full implementation, see our Example App built in Next.js.

Demo

The repository with the completed code is available here: https://github.com/useparagon/paragon-integrations-catalog-tutorial
  • Note: The demo repository signs Paragon User Tokens in the frontend application, which should NOT be used in production. Replace getParagonUserToken with your own app's Paragon User Token generation, which should be performed on your server only.

Prerequisites

1. Remove Paragon <script> tag

First, we will remove the global <script> tag that you may have added to your application.
- <script type="text/javascript" src="https://cdn.useparagon.com/latest/sdk/index.js"></script>
We will replace this in the next step with a hook that can mount the same script tag to your application, but in a way that your React app can listen for when it finishes loading.

2. Add hooks for mounting the Paragon SDK

Next, create a new file to define a hook. We'll call this useParagonGlobal.ts. Copy the following contents to this file:
// useParagonGlobal.ts
import { useCallback, useEffect, useRef, useState } from "react";
export default function useParagonGlobal() {
const mountedParagon = useRef(false);
const [paragonReady, setParagonReady] = useState(false);
const initParagon = useCallback(async () => {
if (window.paragon) {
setParagonReady(true);
}
}, []);
useEffect(() => {
if (typeof window !== "undefined" && !paragonReady && !mountedParagon.current) {
if (window.paragon) {
initParagon();
} else {
mountedParagon.current = true;
const paragonSrc = document.createElement("script");
paragonSrc.src = "https://cdn.useparagon.com/latest/sdk/index.js";
paragonSrc.onload = initParagon;
document.body.appendChild(paragonSrc);
}
}
}, [paragonReady, initParagon]);
if (paragonReady && window.paragon) {
return window.paragon;
}
return undefined;
}
Create another new file to define a hook used for SDK authentication. We'll call this useParagonAuth.ts. Copy the following contents to this file:
import { useEffect, useState } from "react";
async function getParagonUserToken(): Promise<string> {
// Replace this with the logic that your app uses for
// fetching the Paragon User Token.
}
export default function useParagonAuth(
paragon?: ConnectSDK
): { user?: ConnectUser; error?: Error } {
const [token, setToken] = useState<string | null>(null);
const [user, setUser] = useState<ConnectUser | undefined>();
const [error, setError] = useState<Error | undefined>();
useEffect(() => {
getParagonUserToken().then(setToken).catch(setError);
}, []);
useEffect(() => {
if (paragon && token && !error) {
paragon
.authenticate(PARAGON_PROJECT_ID, token)
.then(() => setUser(paragon.getUser()))
.catch(setError);
}
}, [paragon, token, error]);
return { user, error };
}
These hooks can then be consumed from a component that will render your integrations catalog as follows:
// IntegrationsCatalog.tsx
import useParagonGlobal from "../hooks/useParagonGlobal";
function IntegrationsCatalog() {
// paragon is the SDK singleton or `undefined`
const paragon = useParagonGlobal();
const { user } = useParagonAuth(paragon);
return (<div className="catalog">
<h1>Integrations Catalog</h1>
</div>);
}
export default IntegrationsCatalog;

3. Displaying integrations

Next, we'll show the list of integrations loaded from your project. The paragon global is loaded as a stateful value, so when the SDK loads, the component will update accordingly:
// IntegrationsCatalog.tsx
import useParagonGlobal from "../hooks/useParagonGlobal";
function IntegrationsCatalog() {
// paragon is the SDK singleton or `undefined`
const paragon = useParagonGlobal();
const { user } = useParagonAuth(paragon);
return (<div className="catalog">
<h1>Integrations Catalog</h1>
+ {paragon &&
+ paragon.getIntegrationMetadata().map((integration) => {
+ return <div
+ key={integration.type}
+ onClick={() => paragon.connect(integration.type)}
+ >
+ <img src={integration.icon} width={20} height={20} />
+ <p>{integration.name}</p>
+ </div>;
+ })}
</div>);
}
export default IntegrationsCatalog;
After checking that paragon is not undefined, this snippet calls .getIntegrationMetadata to get all active integrations in your project, along with their names and icon URLs.
For every integration, we display their icons and names. We also set up an event handler to call .connect with the integration.type when the element is clicked. This will display the Connect Portal over your app, prompting the user to connect an account to the specified integration.
Result:
When you click on any one of the integrations, the Connect Portal overlay appears over your app.

4. Displaying account status

Next, we'll update your integrations catalog to display your user's account status for each integration within the element that shows the icon and name.
We can get the account status using .getUser, which returns the current state of the authenticated user from the SDK, including their connected integrations and enabled workflows.
// IntegrationsCatalog.tsx
import useParagonGlobal from "../hooks/useParagonGlobal";
function IntegrationsCatalog() {
// paragon is the SDK singleton or `undefined`
const paragon = useParagonGlobal();
const { user } = useParagonAuth(paragon);
return (<div className="catalog">
<h1>Integrations Catalog</h1>
{paragon &&
paragon.getIntegrationMetadata().map((integration) => {
+ const integrationEnabled = user?.integrations?.[integration.type]?.enabled;
return <div
key={integration.type}
onClick={() => paragon.connect(integration.type)}
>
<img src={integration.icon} width={20} height={20} />
<p>{integration.name}</p>
+ <p>{integrationEnabled ? "Connected" : "Not connected"}</p>
</div>
})}
</div>);
}
export default IntegrationsCatalog;
Result:
Each integration will show the "Connected" or "Not connected" status for the current user.

5. Adding subscriptions for account status changes

Finally, we want to make sure that the catalog components that we've created update automatically when the SDK handles a state change with one of our user's integrations. Specifically, when a user connects or disconnects their account, we want the account status to reflect that immediately.
To do this, we can use .subscribe, which allows us to listen for global SDK events. We will listen for the events "onIntegrationInstall" and "onIntegrationUninstall" to detect when a user has connected or disconnected an account.
// useParagonAuth.ts
export default function useParagonAuth(
paragon?: ConnectSDK
): { user?: ConnectUser; error?: Error } {
const [token, setToken] = useState<string | null>(null);
const [user, setUser] = useState<ConnectUser | undefined>();
const [error, setError] = useState<Error | undefined>();
useEffect(() => {
getParagonUserToken().then(setToken).catch(setError);
}, []);
+ // Listen for account state changes
+ useEffect(() => {
+ const listener = () => {
+ if (paragon) {
+ setUser({ ...paragon.getUser() });
+ }
+ };
+ paragon?.subscribe("onIntegrationInstall", listener);
+ paragon?.subscribe("onIntegrationUninstall", listener);
+ return () => {
+ paragon?.unsubscribe("onIntegrationInstall", listener);
+ paragon?.unsubscribe("onIntegrationUninstall", listener);
+ };
+ }, [paragon]);
useEffect(() => {
if (paragon && token && !error) {
paragon
.authenticate(PARAGON_PROJECT_ID, token)
.then(() => setUser(paragon.getUser()))
.catch(setError);
}
}, [paragon, token, error]);
return { user, error };
}
Our integrations catalog will now re-render with the most up-to-date information when account state changes.
Result: You've completed a basic Integrations Catalog with the Connect SDK!

Recap

In this guide, you built an integrations catalog that you can embed in your app.
  • You defined 2 hooks: useParagonGlobal (for mounting the Paragon SDK) and useParagonAuth (for authenticating a user).
  • You used getIntegrationMetadata to get all available integrations in the project with name and icon information.
  • You used getUser to get the user's account status.
  • You used subscribe to listen for user account status changes to sync updates to your catalog UI.