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. The completed code is available in this GitHub repository.

The tutorial 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.

Alternatively, for a production-ready implementation of the SDK embedded in a Next.js app, see our Example App repository.

Demo

In this tutorial, we'll build the user interface to display a list of Paragon integrations embedded in your application:

Prerequisites

  • This guide embeds the Integration Catalog in a React.js app. React is not a requirement for using the SDK, but the tutorial steps will use React-specific functions to dynamically update the Catalog UI.

  • This guide assumes that you've already installed the SDK and configured User Authentication. We will change your implementation slightly so that your React app can listen for the SDK events for loading and authenticating.

1. Add hooks for authenticating the Paragon User

First, 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";
import { paragon, AuthenticatedConnectUser } from "@useparagon/connect";

async function getParagonUserToken(): Promise<string> {
  // Replace this with the logic that your app uses for
  // fetching the Paragon User Token.
}

export default function useParagonAuth(): { user?: AuthenticatedConnectUser; 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 (token && !error) {
      paragon
        .authenticate(PARAGON_PROJECT_ID, token)
        .then(() => setUser(paragon.getUser()))
        .catch(setError);
    }
  }, [token, error]);

  return { user, error };
}

Replace PARAGON_PROJECT_ID with your Project ID in Paragon. You can find your Project ID in the Overview tab of any Integration within the dashboard.

This hook can then be consumed from a component that will render your integrations catalog as follows:

// IntegrationsCatalog.tsx
import useParagonAuth from "../hooks/useParagonAuth";

function IntegrationsCatalog() {
    const { user } = useParagonAuth();
    
    return (<div className="catalog">
        <h1>Integrations Catalog</h1>
    </div>);
}

export default IntegrationsCatalog;

2. 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 { paragon } from "@useparagon/connect";

function IntegrationsCatalog() {
    const { user } = useParagonAuth();
    
    return (<div className="catalog">
        <h1>Integrations Catalog</h1>
        {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;

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:

3. 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 { paragon } from "@useparagon/connect";

function IntegrationsCatalog() {
    const { user } = useParagonAuth();
    
    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:

4. 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
import { paragon, AuthenticatedConnectUser, SDK_EVENT } from '@useparagon/connect';

export default function useParagonAuth(): { user?: AuthenticatedConnectUser; 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 = () => {
        const authedUser = paragon.getUser();
        if (authedUser.authenticated) {
          setUser({ ...authedUser });
        }
    };
    paragon.subscribe(SDK_EVENT.ON_INTEGRATION_INSTALL, listener);
    paragon.subscribe(SDK_EVENT.ON_INTEGRATION_UNINSTALL, listener);
    return () => {
      paragon.unsubscribe(SDK_EVENT.ON_INTEGRATION_INSTALL, listener);
      paragon.unsubscribe(SDK_EVENT.ON_INTEGRATION_UNINSTALL, listener);
    };
  }, []);

  useEffect(() => {
    if (token && !error) {
      paragon
        .authenticate(PARAGON_PROJECT_ID, token)
        .then(() => setUser(paragon.getUser()))
        .catch(setError);
    }
  }, [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.

Last updated