@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.
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.@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: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, withstatus=integration-requested. If the browser blocks the popup lists the query parameters.
- Success page
- Failure page
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:
inviteUrlmay 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. TheinviteUrloption says why.containeris a ref, read whenopen()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.preloadresolves 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 wheneverinviteUrlorembedKeychanges, never throws, and is an optimization only. Set it tofalseto leave the decision untilopen().
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:
connectionIdsis empty on this path, andreasoncomes from the failure URL’sskip_reasonorstatus.- 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.
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 emptydiv. 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.Two surfaces on the page
Two surfaces on the page
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.A handler sees stale state
A handler sees stale state
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.
Nothing happens on the server
Nothing happens on the server
Expected. The flow mounts on the client. Server rendering produces the component’s empty
div and no browser work. See Server rendering.An overlay is left behind
An overlay is left behind
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().open() throws mentioning inviteUrl
open() throws mentioning inviteUrl
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.The flow opens an earlier invitation
The flow opens an earlier invitation
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().