Skip to content

StackdropCO/retool-ai-editor

Repository files navigation

AI-powered Editor.js component for Retool

This repo contains the custom component built during the Retool webinar on building custom components. It's an Editor.js block editor wired into Retool with block-level AI transformations.

Editor.js outputs clean JSON instead of HTML. Each paragraph, heading, and list is a discrete block with structured data, which makes it a better fit for internal tools where you need that data flowing through queries, tables, and workflows in the rest of your Retool app.

What's in the box

src/
  components/
    AIEditor/
      index.tsx             # React component with all Retool state/event wiring
      AITransformTune.ts    # Custom Block Tune for per-block AI actions
      AIAction.ts           # Custom Block Tool for slash-menu AI inserts (Draft reply)
      AIEditor.module.css   # Container styles
  index.tsx                 # Barrel export for the AIEditor component

AI block transformer

Every block gets an AI action menu in its settings panel (the three-dot toolbar). Options are Summarize and Rewrite. Each fires a Retool event with the block's content, transformation type, and block id. The LLM response replaces that block's text in place via editor.blocks.update(id, data); no index math, no re-inserts.

The menu is built with Editor.js's MenuConfig return value from the tune's render() method, so the buttons, icons, and hover states come from Editor.js itself.

Draft reply (slash menu)

Typing / opens the slash menu with a Draft reply entry. Picking it inserts a dimmed, pulsing placeholder block and fires aiTransformRequest with transformScope: 'insert' and transformType: 'draft'. When your query writes the response back to transformResponse, the component splits it on blank lines and replaces the placeholder with one paragraph block per chunk.

Prerequisites

  • Node.js v20+
  • A Retool account with admin permissions
  • A Retool API access token with read/write scopes for Custom Component Libraries

Setup

git clone <this-repo-url> ai-editor-retool
cd ai-editor-retool
npm install

Log in to your Retool instance:

npx retool-ccl login

Initialize the library (first time only):

npx retool-ccl init

Start dev mode:

npx retool-ccl dev

This syncs your local changes to Retool on every save. Open any Retool app and drag the AIEditor component onto the canvas.

Wiring it up in Retool

The component communicates with your Retool app through state properties and events.

1. Show the editor output

Drag a Table or JSON Viewer onto the canvas. Bind its data to:

{{ AIEditor1.editorData.blocks }}

You'll see the block-level JSON update in real time as you type (debounced at 400ms).

2. Wire up block transformations

Create a query that calls your LLM endpoint (Retool AI, OpenAI, Anthropic, etc.). Switch on {{ AIEditor1.transformScope }} first to pick the right prompt shape, then on {{ AIEditor1.transformType }} for the action. Use {{ AIEditor1.transformBlockContent }} as the user content for block scope. Reference any other app state in the prompt to ground the response.

For insert scope (Draft reply from the slash menu), transformBlockContent is empty; ground the draft entirely from app state relevant to what you want to draft. Return blank-line-separated paragraphs; the component splits the response and inserts one paragraph block per chunk, replacing the placeholder.

Add an event handler on the AIEditor1 component:

  • Event: aiTransformRequest
  • Action: Trigger aiTransformQuery
  • Then: Set AIEditor1.transformResponse to {{ aiTransformQuery.data }}

The component reads transformBlockId internally to know which block to update or replace, so your query doesn't need to touch it.

3. Ground the prompt in app state

Your transform query runs in the Retool app's full scope, so reference any query or component directly in the prompt:

{{ someQuery.data }}
{{ someTable.selectedRow }}
{{ someInput.value }}

No extra binding on the component is needed. The component owns the editor; the query owns the context.

Component properties reference

Output (component writes, your app reads)

Property Type Description
editorData object Full Editor.js JSON output, updated on every change
transformBlockContent string Text content of the block being transformed (empty for insert scope)
transformType string summarize, rewrite, or draft
transformBlockId string Editor.js id of the block being transformed or replaced
transformScope string block (tune-menu transforms) or insert (slash-menu Draft reply)

Input (your app writes, component reads)

Property Type Description
transformResponse string LLM response for block transforms. Bind to your query result.
readOnly boolean Toggle read-only mode from your app.

Events

Event Fires when
aiTransformRequest User picks a transformation from the block menu or Draft reply from the slash menu
editorChange Editor content changes (debounced 400ms)

How events carry data

Retool custom component events carry no payload. The pattern is: set state first, then fire the event. Your event handler reads the state to get the data it needs.

// Component sets block content, type, and id, then fires the event
setTransformBlockContent(content)
setTransformType(type)
setTransformBlockId(blockId)
aiTransformRequest()

// In Retool, the query reads {{ AIEditor1.transformBlockContent }} and {{ AIEditor1.transformType }}

This is the same pattern used across all Retool custom components. The CCL TypeScript API docs cover this in detail.

Deploying

When you're ready to ship:

npx retool-ccl deploy

This pushes an immutable version to your Retool instance. Pin specific versions per-app under Custom Component settings in the app editor.

Resources

About

Editor.js block editor for Retool with AI integrations

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

No contributors