Skip to main content

Custom Components

Custom components are React applications that render inside the NoClick interface tab. They run in sandboxed iframes with access to the full @noclick/sdk API, Tailwind CSS, and any npm package.

Creating a Component

From the Canvas

  1. Open the Interface tab in the workflow editor
  2. Drag Custom Component from the UX panel onto the grid
  3. Click the node on the canvas to open its config
  4. Write JSX in the code editor

From MCP (Claude Code, Cursor)

How It Works

  1. You write JSX/TSX in the node’s jsx_source config field
  2. When the node executes, the backend transpiles it via Sucrase (runs in QuickJS, ~15ms)
  3. An import map is generated with React 19 from esm.sh, Tailwind CSS CDN, and any npm packages you import
  4. The @noclick/sdk is injected as a base64 data URI in the import map
  5. Everything is assembled into an HTML document (srcdoc) and rendered in an iframe

Using npm Packages

Just import them. The transpiler auto-detects imports and maps them to esm.sh:
Packages are bundled with &bundle to avoid transitive dependency issues.

Fullscreen Mode

Set fullscreen="true" in the node config to render the component as a full-viewport tab instead of a grid block:
When multiple fullscreen components exist, tabs appear below the main tab bar. Tabs are:
  • Draggable: reorder by dragging
  • Renamable: click the active tab label to edit
  • Persistent: tab order survives page reloads

Styling

Tailwind CSS is included automatically. Use standard Tailwind classes:

Using the SDK

Import any SDK namespace directly:

Read Node Output

Run a Node and Get Results

Persistent State

Requires a State Manager node in the workflow:

OAuth and Credentials

File Upload

Dataset CRUD

Caching

Compiled srcdoc is cached in IndexedDB (one slot per node). Components render instantly on tab switch and page reload without re-transpilation. The cache auto-invalidates when jsx_source changes. Copied/duplicated nodes share cache via source hash matching.

Troubleshooting

IssueSolution
Gray iframe on first loadesm.sh cold start (~3-5s). Packages are cached after first load.
< in stored JSXUse field="jsx_source" body syntax in MCP XML. Entity escaping is handled automatically.
{{ }} in JSX strippedFixed: unresolvable {{ }} patterns (like JSX inline styles) are preserved as-is.
Drag/resize stuck on canvasFixed: pointer-events: none on iframes during drag/resize via CSS.
SDK method hangsAll requests timeout after 30s with a descriptive error.