Extensions SDK

Build custom views that run inside a Conexus X module — the way a monday.com module view works. A view is a web page you host; Conexus X frames it in an iframe and talks to it over a single postMessage channel.

Why an iframe

A custom view is code you did not write. Rendered directly into the module DOM it would share globals, styles, and the signed-in user’s JWT. On its own origin it can read none of that — every piece of data it gets, it gets because the host handed it over.

Two independent gates stand between a view and customer data:

  • Scopes — the view’s manifest declares what it needs, an admin approves a subset, the host serves only the intersection.
  • The route allowlist — a fixed table of endpoints a view may reach at all. /api-key, /auth/*, /agent/*, /conversations/* and similar are not on it, whatever a view was granted. See Scopes & routes.

Underneath both, the host proxies every call with the signed-in user’s own credentials, so the API re-checks workspace membership and module access exactly as it would in the browser. A scope narrows what a view can ask for; it never widens what the person looking at it may do.

Entry points

PackageForNeeds React
@conexus-x/sdkThe view, any framework or noneNo
@conexus-x/sdk/reactThe view, with hooksYes
@conexus-x/sdk/hostThe Conexus X app side, framework-freeNo
@conexus-x/sdk/react-hostThe Conexus X app side, as a hookYes
@conexus-x/sdk/manifestThe review pipeline — validate a submissionNo

Quick start

The fastest path is the CLI — see CLI & templates. The view itself looks like this:

tsx
import {
    useConnection, useViewContext, useScope, useRecords, useCommands, useAutoResize
} from "@conexus-x/sdk/react";

export default function View() {
    const { status } = useConnection();
    const context = useViewContext();
    const canWrite = useScope("records:write");
    const commands = useCommands();

    // Refetches on its own when the module changes — no socket, no polling
    const { data: records, loading } = useRecords(context?.collectionId);

    useAutoResize();

    if (status !== "ready") return <Spinner />;

    return records?.map((record) => (
        <Row key={record._id} record={record} canWrite={canWrite}
             onOpen={() => commands.openRecord(record._id)} />
    ));
}

Where this sits in the bigger plan

Built today: the protocol, the guest client and host bridge, React bindings for both halves, the scope model and route allowlist, the manifest format and its validator, and two starters (conexus-x-np for Next.js, conexus-x-rp for React + Vite) to scaffold from.

Not built yet, each its own piece of work: backend models/routes for apps, versions, installs and grants; a sandbox test workspace; an automated test-case runner gating submission; an admin approval queue; mounting a view inside the module page itself (today that route is one module with no view switcher); a marketplace, billing and versioned rollout.