Skip to navigation

Embedded authentication

@polytomic/connect-react renders the configuration form inside your React app, so users connect their systems without leaving your product. For non-React frontends, or to let Polytomic host the flow, see Hosted authentication.

Install

npm install @polytomic/connect-react

Requires react and react-dom 18 or 19.

Add two backend routes

Set up a proxy on your backend to mint Connect sessions and forward requests to Polytomic.

server.ts
import express from "express";
const app = express();
app.use(express.json());
// Returns a token for one connection, as plain text.
app.post("/connect-session", async (req, res) => {
// Decide these from your own records, not from the request.
const { name, type } = await connectionFor(req);
const response = await fetch(
"https://app.polytomic.com/api/connections/connect/",
{
method: "POST",
headers: {
Authorization: `Bearer ${process.env.POLYTOMIC_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ name, whitelist: [type] }),
},
);
const body = await response.json();
if (!response.ok) {
res.status(response.status).send(body.message ?? body.error?.message);
return;
}
res.type("text/plain").send(body.data.token);
});
// Forwards the form's calls to the Polytomic API.
app.use("/polytomic", async (req, res) => {
const response = await fetch(`https://app.polytomic.com/api${req.url}`, {
method: req.method,
headers: {
"Content-Type": "application/json",
"X-Polytomic-Version": req.get("X-Polytomic-Version") ?? "2025-09-18",
},
body: req.method === "GET" ? undefined : JSON.stringify(req.body),
});
res
.status(response.status)
.type("json")
.send(await response.text());
});

The embedded form shows one connection type, so send exactly one entry in whitelist. List the available types with GET /api/connection_types, and send a type’s id. Each type also has a logo_url and logo_dark_url for rendering your own picker. name is how the connection will appear in the organization, and must be unique per connection type. To reconnect an existing connection, send connection with its ID instead of whitelist. The other fields are in the API reference.

Render the form

import { ConnectionForm } from "@polytomic/connect-react";
import "@polytomic/connect-react/styles.css";
async function getSession() {
const response = await fetch("/connect-session", { method: "POST" });
const text = await response.text();
if (!response.ok) throw new Error(text);
return text;
}
<ConnectionForm
apiBaseUrl="/polytomic"
getSession={getSession}
onComplete={(connection) => save(connection.id)}
onCancel={() => closeDialog()}
/>;
PropPurpose
getSessionRequired. Returns a session token from your backend. Called once on mount.
apiBaseUrlPath of your proxy route. Default /api.
onCompleteReceives the connection’s id, name, type and status once connected.
onCancelCalled on Cancel. Close or unmount the form here.
onErrorCalled when the session can’t load or the flow can’t continue.
colorScheme'light' (default) or 'dark', to match the surface behind the form.
layout'contained' (default) fills a card or dialog. 'page' keeps fields at a standard width on an open page.

Validation errors, such as a missing field or failed credentials, display in the form and don’t call onError.

OAuth opens in a popup. If the browser blocks it, the form shows a button to open it.

If the user leaves before finishing, an incomplete connection can remain in the organization. Check a connection’s status before using it, and delete unused ones.

To restart the flow, remount the form with a new key.

Style it

Import the package stylesheet before your own CSS so your rules take precedence. Polytomic styles are scoped to the form’s root element, .ptc-root.

Theme

Theme elements like color and border radius are styled using CSS variables. Override them on .ptc-root, and set dark values under [data-color-scheme='dark']. Colors are HSL channels without the hsl() wrapper:

.ptc-root {
--ring: 250 60% 50%;
--radius: 0.25rem;
}
.ptc-root[data-color-scheme="dark"] {
--ring: 250 70% 70%;
}
TokensUsed for
--foreground, --muted-foregroundText, and secondary text such as descriptions
--background, --card, --popover, --popover-foreground, --mutedSurfaces: cards, menus, read-only inputs
--border, --border-strong, --input, --ringInput outlines, input fill and the focus ring
--button, --button-foreground, --button-hover, --button-active, --button-borderButtons
--control, --control-foreground, --control-hover, --control-activeChips and small controls
--secondary, --secondary-hover, --accent, --accent-foregroundHover and highlighted options
--selected, --selected-foreground, --selected-strong, --selected-strong-foregroundChosen options and days
--primaryThe Cancel link
--destructiveErrors
--radiusMenus and the calendar

Set font-family on .ptc-root to change the font, which defaults to Inter. Set --ptc-form-max-width to change the maximum width, which defaults to 36rem.

Elements

Elements are styled by data-slot, with state exposed through data and ARIA attributes. Scope rules under .ptc-root and include the state you’re targeting:

.ptc-root [data-slot="input"][aria-invalid="true"] {
box-shadow: 0 0 0 2px crimson;
}
.ptc-root [data-slot="combobox"][data-state="open"] {
box-shadow: 0 0 0 2px var(--acme-brand);
}

Only data-slot values and state attributes are stable. Other markup can change between versions.

Elementdata-slot values
Fieldfield, field-label, field-control, field-description, field-error
Text inputinput, input-wrapper, input-copy-button, textarea
Checkboxcheckbox, checkbox-label
Selectcombobox, combobox-menu, combobox-search, combobox-option, combobox-clear
Date pickerdate-picker, date-picker-wrapper, date-picker-calendar, calendar-day
Key-value listkey-value, key-value-row, key-value-add-button, key-value-remove-button
Hierarchy selecthierarchy-select, hierarchy-select-tree, hierarchy-select-row, hierarchy-select-refresh, hierarchy-select-footer
File uploadfile-upload, file-upload-dropzone, file-upload-file-name, file-upload-button
Google Drive filedrive-file, drive-file-browse-button, drive-file-list, drive-file-file-row, drive-file-refresh
HTTP authhttp-auth
Actionsbutton, button-wrapper, link, form-actions, form-actions-cancel
Flowform-container, form, form-status, form-error

Common states:

  • data-invalid, data-disabled and data-required on a field
  • aria-invalid on an input
  • data-state="open" on menus
  • data-checked on a selected menu option
  • data-selected on selected tree and file rows

Tooltips and dialogs are portalled to a separate .ptc-root container appended to the body, so rules scoped to .ptc-root apply to them too.