Developing Custom Retool Components
Retool covers most internal tool tasks with built-in widgets. However, sooner or later there is a requirement that no ready-made component exists for: specific data visualization, an interactive map with custom markers, complex drag-and-drop with business logic. For this, Retool provides a Custom Components mechanism — iframe-isolated React applications embedded in the interface via postMessage API.
How the Custom Components Mechanism Works
A custom component in Retool is a full-fledged React application that lives in an isolated iframe. Interaction with the host is built through Retool.useStateValue and Retool.useEventCallback. Data is passed from Retool to the component via model, the component sends events back via triggerQuery or arbitrary event callbacks.
The development environment is a local server (Vite or CRA), the code is deployed to any static hosting or loaded directly into Retool.
Component Structure
// src/index.tsx — entry point of custom component
import { Retool } from '@tryretool/custom-component-support';
interface ModelData {
items: Array<{ id: string; label: string; value: number; color: string }>;
selectedId: string | null;
}
export const BubbleChart: FC = () => {
const [items] = Retool.useStateValue<ModelData['items']>({
name: 'items',
initialValue: [],
label: 'Chart items',
inspector: 'array',
});
const [selectedId, setSelectedId] = Retool.useStateValue<string | null>({
name: 'selectedId',
initialValue: null,
label: 'Selected item ID',
inspector: 'string',
});
const onSelect = Retool.useEventCallback({ name: 'onItemSelect' });
const handleBubbleClick = (id: string) => {
setSelectedId(id);
onSelect({ id });
};
return (
<BubbleChartRenderer
items={items}
selectedId={selectedId}
onSelect={handleBubbleClick}
/>
);
};
The @tryretool/custom-component-support package is a mandatory dependency. It abstracts postMessage and provides React hooks for state synchronization.
Building and Deployment
The component is built into a single bundle and deployed to static hosting. Retool supports two connection methods: via URL (CDN/S3/Vercel) and via Retool Toolbox for self-hosted instances.
// vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
build: {
lib: {
entry: 'src/index.tsx',
name: 'RetoolCustomComponent',
fileName: 'index',
formats: ['umd'],
},
rollupOptions: {
external: ['react', 'react-dom'],
output: {
globals: {
react: 'React',
'react-dom': 'ReactDOM',
},
},
},
},
});
After vite build — dist/index.umd.js is uploaded to CDN or Retool Cloud Storage. In the Retool interface, a Custom Component is created, the bundle URL is specified, and the iframe-URL of the page is set.
Passing Complex Data
Retool limits the data model to serializable types. For complex structures (nested objects, files), JSON serialization is used within a string field:
const [rawConfig] = Retool.useStateValue<string>({
name: 'config',
initialValue: '{}',
label: 'JSON Config',
inspector: 'text',
});
const config = useMemo(() => {
try {
return JSON.parse(rawConfig);
} catch {
return {};
}
}, [rawConfig]);
For large datasets (thousands of rows), data is passed through a Retool Query, which returns JSON — the component receives it directly without duplicating state in the model.
Typical Tasks for Custom Components
Visualizations: D3.js charts (chord diagram, force-directed graph, sankey), ECharts with custom series, Three.js 3D model previews.
Interactive tables: virtualized lists via @tanstack/virtual for 50k+ rows, inline editing with custom validators, drag-and-drop sorting via @dnd-kit/core.
Maps: Mapbox GL JS / Leaflet with dynamic clusters, drawing tools for geozones, isochrones via Mapbox Isochrone API.
Specific UI patterns: Kanban board with business constraints, Gantt timeline, code editor based on Monaco.
What Happens During Development
Days 1–2: project setup (Vite + React + TypeScript + Retool SDK), agreement on data contract — which fields in the model, which events to output. Minimal working component with mock data.
Days 3–4: implementation of logic and rendering. In parallel — testing integration with real Retool environment (cloud or self-hosted — it matters, SDK versions differ).
Day 5: handling edge cases, data loading, styling according to client's design system, deployment to CDN, delivery of component with documentation.
A component of medium complexity (custom chart or interactive map without complex logic) — 3–5 days. Kanban or Gantt with drag-and-drop and persistence — 1–2 weeks.







