How to Create a Retool Custom React Component

Retool is a fantastic tool for building internal apps quickly, but sometimes the out-of-the-box components don’t quite cut it.

I recently worked on a project for a US-based logistics firm where we needed a very specific interactive map that wasn’t available in the standard library.

That is when I dived deep into building a custom React component for Retool to bridge that gap.

In this guide, I will show you exactly how to build your own custom components to make your Retool apps much more powerful.

Retool Custom Component Architecture

Before we start coding, it is important to understand how Retool talks to your React code.

Retool hosts your custom component in an iframe to keep the main application secure and performant.

You will primarily interact with two things: the Model (data coming from Retool) and UpdateModel (sending data back to Retool).

Method 1: Use the Retool Web Editor (Quickest Way)

If you have a relatively simple component, you can write the code directly inside the Retool web interface.

I use this method frequently when I need a specialized UI element, like a custom US State selector with specific branding.

Step 1: Drag the Custom Component

First, open your Retool app and drag a “Custom Component” from the component library onto your canvas.

You will see some boilerplate code in the “Inspect” panel on the right side under the “Iframe Code” section.

Step 2: Define the Model

The model is a JSON object that holds the state of your component.

For a US-based sales dashboard, your model might look like this:

{
  "region": "Midwest",
  "salesTarget": 50000
}

Step 3: Write the React Code

Here is the full code for a custom “Sales Progress Circle” that changes color based on performance.

<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@mui/material@latest/umd/material-ui.development.js"></script>

<div id="react-root"></div>

<script type="text/babel">
  const { Typography, Box, CircularProgress } = MaterialUI;

  const SalesComponent = () => {
    // Retool specific hooks to get data and update it
    const [model, setModel] = window.Retool.useModel();
    
    const progress = (model.currentSales / model.salesTarget) * 100;
    const color = progress >= 100 ? 'success' : 'primary';

    return (
      <Box sx={{ display: 'flex', flexDirection: 'column', alignItems: 'center', p: 2 }}>
        <Typography variant="h6" gutterBottom>
          {model.region} Sales Performance
        </Typography>
        
        <Box sx={{ position: 'relative', display: 'inline-flex' }}>
          <CircularProgress 
            variant="determinate" 
            value={Math.min(progress, 100)} 
            color={color} 
            size={80} 
          />
          <Box
            sx={{
              top: 0,
              left: 0,
              bottom: 0,
              right: 0,
              position: 'absolute',
              display: 'flex',
              alignItems: 'center',
              justifyContent: 'center',
            }}
          >
            <Typography variant="caption" component="div" color="text.secondary">
              {`${Math.round(progress)}%`}
            </Typography>
          </Box>
        </Box>
        
        <Typography variant="body2" sx={{ mt: 2 }}>
          Target: ${model.salesTarget?.toLocaleString('en-US')}
        </Typography>
      </Box>
    );
  };

  const ConnectedComponent = window.Retool.connectReactComponent(SalesComponent);
  const root = ReactDOM.createRoot(document.getElementById('react-root'));
  root.render(<ConnectedComponent />);
</script>

You can refer to the screenshot below to see the output.

Create Retool Custom React Component

Method 2: Develop Locally with Create React App

For more complex components that require external libraries or many files, I prefer developing locally.

This allows me to use VS Code and modern build tools before deploying the code to Retool.

Step 1: Create a React Project

Open your terminal and create a new project. I usually name mine after the specific business function.

npx create-react-app retool-us-tax-calculator
cd retool-us-tax-calculator
npm install @mui/material @emotion/react @emotion/styled

Step 2: Implement the Retool Bridge

You need to ensure your local app can communicate with the Retool parent window.

In my experience, creating a helper hook makes the code much cleaner and easier to maintain.

Step 3: Full Code Example (Tax Calculator Component)

Here is a complete example of a component that calculates the estimated US Federal Tax based on Retool inputs.

App.js

import React, { useEffect, useState } from 'react';
import { Card, CardContent, Typography, TextField, Grid } from '@mui/material';

function App() {
  const [model, setModel] = useState({ income: 0, filingStatus: 'Single' });

  // Sync with Retool
  useEffect(() => {
    const handler = (event) => {
      if (event.data?.type === 'MODEL_UPDATED') {
        setModel(event.data.model);
      }
    };
    window.addEventListener('message', handler);
    return () => window.removeEventListener('message', handler);
  }, []);

  const calculateTax = (income) => {
    // Simplified 2024 Federal Tax Logic for example
    if (income <= 11600) return income * 0.10;
    if (income <= 47150) return 1160 + (income - 11600) * 0.12;
    return 5426 + (income - 47150) * 0.22;
  };

  const estimatedTax = calculateTax(model.income || 0);

  const handleIncomeChange = (e) => {
    const newIncome = parseFloat(e.target.value) || 0;
    // Update local state
    setModel(prev => ({ ...prev, income: newIncome }));
    // Update Retool Model
    window.parent.postMessage({
      type: 'UPDATE_MODEL',
      model: { income: newIncome }
    }, '*');
  };

  return (
    <Card sx={{ maxWidth: 400, m: 'auto', boxShadow: 3 }}>
      <CardContent>
        <Typography variant="h5" color="primary" gutterBottom>
          Internal Tax Estimator
        </Typography>
        <Grid container spacing={2}>
          <Grid item xs={12}>
            <TextField
              fullWidth
              label="Annual Gross Income (USD)"
              type="number"
              value={model.income}
              onChange={handleIncomeChange}
              variant="outlined"
            />
          </Grid>
          <Grid item xs={12}>
            <Typography variant="h6">
              Est. Federal Tax: 
              <span style={{ color: '#d32f2f' }}> 
                ${estimatedTax.toLocaleString('en-US', { minimumFractionDigits: 2 })}
              </span>
            </Typography>
          </Grid>
        </Grid>
      </CardContent>
    </Card>
  );
}

export default App;

You can refer to the screenshot below to see the output.

Retool Custom React Component

How to Trigger Retool Actions from Your Component

A common mistake I see developers make is building a component that looks great but doesn’t “talk” back to the Retool queries.

You can trigger a Retool query (like a SQL update) by using window.Retool.triggerQuery(‘queryName’).

I often use this for “Save” buttons within my custom React forms to ensure the database updates immediately.

Tips for Ranking High with Custom Components

When I build these for production, I always keep a few performance tips in mind.

First, keep your external dependencies minimal. Loading huge libraries like Three.js inside an iframe can slow down your Retool app.

Second, always use the window.Retool.subscribe method to ensure your React state stays in sync with Retool’s global state.

Handle CSS Scoping Issues

Since your component lives in an iframe, you don’t have to worry about your CSS leaking out into the rest of Retool.

However, make sure you set the iframe height to “Hug Contents” in the Retool inspector if your component grows dynamically.

I have spent hours debugging “hidden” buttons, only to realize the iframe container was just too short.

Conclusion

Building a custom React component for Retool is a game-changer for internal tool development.

It allows you to provide a bespoke user experience while still leveraging the speed of a low-code platform.

Whether you use the built-in editor for quick UI tweaks or a local development workflow for complex logic, the flexibility is unmatched.

I hope this tutorial helps you build better, more efficient tools for your team.

You may also like to read:

51 Python Programs

51 PYTHON PROGRAMS PDF FREE

Download a FREE PDF (112 Pages) Containing 51 Useful Python Programs.

pyython developer roadmap

Aspiring to be a Python developer?

Download a FREE PDF on how to become a Python developer.

Let’s be friends

Be the first to know about sales and special discounts.