Skip to main content
@catenaclearing/connect-sdk-react is a thin React layer over @catenaclearing/connect-sdk. It adds what a React component needs and nothing else:
  • The flow is torn down when the component that launched it unmounts.
  • The handler that runs is the one from your latest render.
  • The redirect return page is one hook.
Nothing about the flow is different. The options, the events and the three modes are the core package’s. The JavaScript reference, Events and outcomes and How the flow runs document them.

What you need

The same things the core package needs: an invitation URL, an embed key, and the origin of every launching page registered against that key. What you need says where each comes from. React 18 or 19.

Install

Beta. Install with the @beta tag. A plain npm install @catenaclearing/connect-sdk-react resolves to 0.0.1, a deprecated placeholder, until the stable 1.0.0 is published.
The package page is npmjs.com/package/@catenaclearing/connect-sdk-react. Copy the command from here or from that page rather than typing the name, because look-alike names exist and @catenaclearing is the only scope that is ours. See Official packages. The core package comes with it as a dependency, so this is the only package a React application has to install. If your application also installs the core package directly, keep the two on the same major. During the beta, that means installing the core with @beta too. The wrapper hands every launch to the core it was built against, and one copy of the core is what keeps one flow running at a time.

Quick start

Launch from a button with the hook:
Or render the flow inline, as part of the page, with the component. Size the element and the flow fills it:
Either way, the flow does not remove itself when the user presses Done. With the hook, call the destroy() it returns from onClose, as above. The component does not expose destroy(), so take it down by unmounting it, for example by clearing the state that renders it in onClose. See When the user presses Done.

If the browser blocks the popup

This part is optional. If a browser blocks both the iframe and the popup, the flow takes over the browser tab your app is in. When the user finishes, it returns to the success or failure URL on your invitation, if the invitation has them. Without them the flow stays on its last screen, there is no page for the hook to run on, and your webhooks are the only record. To get the outcome when the URLs are set, render a component at each one that calls the resume hook. The failure page also receives integration requests, with status=integration-requested. If the browser blocks the popup lists the query parameters.
If the browser blocks the popup says what happens without these pages. The Verify your integration checklist applies unchanged.

useCatenaConnect(options)

Returns { open, openWith, destroy }. All three are stable across renders, so they can go straight into an onClick or a dependency list. Every option other than inviteUrl, container and preload is the core package’s, with the same meaning and default. open(options) is the reference. Those three differ to fit a component’s life cycle:
  • inviteUrl may be absent while your application is still loading it. open() throws until it is there. That is a programming error to fix, not a state to wait out: a button wired up before the invitation arrived. When it does arrive, it should come from your own server, never from the page’s URL or other input a visitor controls. The inviteUrl option says why.
  • container is a ref, read when open() is called, so it can point at an element rendered by the same component. Omit it and the flow renders in the core package’s overlay.
  • preload resolves the mode decision while the component is mounted, so the first launch opens straight into the flow instead of a loading state. It runs with the launch’s embed key whenever inviteUrl or embedKey changes, never throws, and is an optimization only. Set it to false to leave the decision until open().
open() launches the flow with the options as of your latest render, or brings the live surface back if a launch is already up. One flow runs at a time across the whole page, as in the core package: a second open(), from this component or any other, focuses the live surface and does not start another. Once a launch has ended and you have not called destroy(), open() replaces it with a new one. “Latest render” is meant literally. A value you set in the same event handler has not rendered yet, so open() cannot see it. If you fetch the invitation when the button is pressed, setInviteUrl(url); open(); launches whatever inviteUrl was before: the previous invitation if there was one, and a throw if there was none. Pass the new value to openWith() instead. openWith(options) is open() with some options supplied for this launch in place of the rendered ones. It takes inviteUrl, embedKey, container and the presentation options. Handlers always come from the latest render. An option you pass replaces the rendered value, and an option you leave out keeps it. Passing undefined clears an optional one, such as theme. For inviteUrl it makes openWith() throw, as open() does.
open() takes no argument so that onClick={open} keeps working. React passes the click event to whatever is in onClick, which is why openWith() is a separate function. Fetching the invitation before the button is shown is still the simpler page when you can do it: the button can be disabled until it arrives, and preload gets to warm the launch. destroy() tears the current launch down: its surface, the frame or window it opened, and its listeners. It is safe to call at any point, including with nothing launched. You rarely need it. Unmounting the component does the same thing, so a flow never outlives the component that launched it, and a message arriving after unmount reaches no handler. Handlers follow the render. The handler that runs is the one you passed most recently, so a handler can close over current state without a ref of your own, and changing a handler never relaunches. Launch options are fixed per launch. Changing inviteUrl, embedKey, container or a presentation option does not touch a live flow. The next open() after the launch ends uses the new values. That is the core package’s behaviour, and the reason a launch cannot be edited from underneath the user. Use one way in per page. The hook can tell its own launches apart, but not a flow that the core package’s own open() started. That is why the core’s open(), resume() and preload() are not re-exported here. A React page drives the flow through this package alone.

<CatenaConnect />

The hook on an element the component renders itself, for when the flow is part of the page rather than a modal over it. The props are the launch options above, minus container and preload: the component is the container, and it launches as soon as it mounts. On top of those it takes whatever a div takes, so className, style, id, data-* and ARIA attributes go onto the element. inviteUrl is required. The element’s contents belong to the flow, so children, dangerouslySetInnerHTML and ref are not accepted. It renders one div and launches the flow into it on mount. Size that element, through className or style, and give it a height of at least 480px. The flow fills whatever it is given, and overflows anything shorter. Unmounting tears the launch down and leaves the page as it was. A launch is fixed for its lifetime, as above: changing inviteUrl, embedKey or a presentation prop does not touch the flow on the page. Change key to relaunch with new options. Handler props follow the render, so a new onSuccess is the one that runs at the next delivery. Under React’s strict mode in development, the first launch is torn down and a second one started, as strict mode does with every effect. One surface is visible either way.

useCatenaConnectResume(options)

The core package’s resume() as a hook, for the redirect mode, and only useful when your invitation has redirect URLs. Call it from the component rendered at each of the two, with outcome: "success" on one and outcome: "exit" on the other. The options are the core’s resume() options, unchanged. resume(options) says why the outcome is yours to declare and why these are two separate call sites. If a redirected launch went out from this tab in the last 30 minutes and nothing has reported it yet, the matching handler runs once, followed by onClose. Delivery is tied to mount, not to render, so re-renders deliver nothing further. Under strict mode the outcome is still delivered once: the core consumes the record on the first call and the second finds nothing. The handler that runs is the one from your most recent render. With no launch in flight the hook does nothing, so it can sit in a destination page’s component unconditionally. The core package’s caveats apply as written:
  • connectionIds is empty on this path, and reason comes from the failure URL’s skip_reason or status.
  • Both destination pages must be on the launching origin.
  • A callback means a redirected launch left this tab recently, not that this page was reached from the flow. Keep the hook to your two destination pages and nowhere else.
When you receive nothing has the detail.

Server rendering

The package is safe to import and render on the server. The hooks do no browser work there, and the component renders its empty div. The flow mounts on the client after hydration. Every emitted JavaScript file begins with "use client". Elsewhere the directive is inert. A server-components application can render <CatenaConnect /> from a server tree only with no handlers. Functions cannot cross from a server component to a client one. To receive onSuccess, onExit or onClose, which you need to unmount it after Done, render it from a "use client" component of your own.

Events

The six callbacks, their payloads, and what each one does and does not mean are the core package’s, and this package delivers them unchanged. Events and outcomes documents them. In particular, onSuccess means the user came back, not that a connection was made. Your connection webhooks are the record.

Troubleshooting

The entries below are the symptoms this package can cause on its own. For everything about the flow itself, such as the surface showing a button instead of the flow, a tab navigating away, or a callback never arriving, Troubleshooting goes from the symptom to the fix, and the three modes explains the mechanism.
Cause. Two components launched: two <CatenaConnect /> elements are mounted, or a component launched while another’s flow was live. One flow runs at a time, so the second launch focused the first surface. The two elements are what you see.Fix. Render one.
It should not. The handler that runs is the one from the most recent render. If a handler closes over a value from an earlier render, that is a bug in this package. File it, with the component that shows it.
Expected. The flow mounts on the client. Server rendering produces the component’s empty div and no browser work. See Server rendering.
Cause. The component that launched the flow is still mounted. A launch is torn down by its own component’s unmount, or by its destroy(). Another component’s unmount does not touch it.Fix. Unmount the component that called open(), or call its destroy().
Cause. The hook was asked to launch before the invitation URL arrived.Fix. Disable the button, or do not call open(), until inviteUrl is a string. If you set the URL in the same handler that calls open(), it has not rendered yet, so use openWith(). Every other throw is the core package’s, usually an invitation URL that is not https. See Troubleshooting.
Cause. open() ran in the handler that set a new inviteUrl, so it launched the one from the latest render.Fix. Pass the new URL to openWith().

Versioning and support

This package follows the core package’s policy. Versioning and support has what the promise covers for React.