Documentation

Welcome to Retool! We're a fast way to build custom internal software.

You'll find the 5 minute demo, quickstart guide, and documentation for each of our connectors and components here. If you've got any questions -- chat with us on the bottom right!

Get Started    Guides

Custom Components

If you have a use case that isn't handled by Retool's built-in components, you can build your own custom component to solve that use case.

You may not need a custom component

If your use case is something general that seems like it should be supported by a built-in Retool component, consider submitting a feature request before building a custom component. The Retool team would be happy to build your component if it's something that other Retool customers also need.

Prerequisites

This guide assumes that:

Setup

  1. Add a Custom Component to your Canvas. The default Custom Component includes some example code to demonstrate how to interact with the rest of your app from within the Custom Component.
Figure 1. The default Custom Component

Figure 1. The default Custom Component

  1. Click the Custom Component in order to show the Custom Component Editor.
Figure 2. The **Custom Component Editor**

Figure 2. The Custom Component Editor

Here's an overview of the fields in the Custom Component Editor:

  • Model. Data to pass into the Custom Component.
  • Iframe Code. The HTML, CSS, and JavaScript for the Custom Component.
  • Hide (Collapse) This Component When True. A JavaScript expression that produces a boolean value. When the value is true the component is hidden. See Dynamically Hide Components.

The next sections of this guide explain how to use these fields in the context of common tasks, like passing data from your app to your Custom Component.

Implement your Custom Component

The Iframe Code field of the Custom Component Editor is where you put all of the HTML, CSS, and JavaScript code for your Custom Component. As the field name suggests, your Custom Component is embedded in an <iframe> when your app runs.

boilerplate.html below describes the minimum code needed to create a working Custom Component.

<script src="https://cdn.tryretool.com/js/react.production.min.js" 
        crossorigin></script>
<script src="https://cdn.tryretool.com/js/react-dom.production.min.js" 
        crossorigin></script>
<div id="react"></div>
<script type="text/babel">
  const MyCustomComponent = ({ triggerQuery, model, updateModel }) => (
    <p>Hello, Retool!</p>
  );
  const ConnectedComponent = Retool.connectReactComponent(MyCustomComponent);
  ReactDOM.render(<ConnectedComponent />, document.getElementById('react'));
</script>

Pass data from your app to your Custom Component

Suppose that you want your Custom Component to display the value of a Text Input component. Specifically, if the value in the Text Input component is Alice, you want the Custom Component to display Hello, Alice!.

Figure 3. An app that passes the value of a Text Input component to a Custom Component

Figure 3. An app that passes the value of a Text Input component to a Custom Component

To pass the data from your Text Input component to your Custom Component:

  1. Open the Custom Component Editor.
  2. Use the model.json code below for the Model field.
{
  "name": {{textinput1.value ? textinput1.value : 'World'}}
}

As the model.json code above shows, you can hard-code property names or values, or you can use JavaScript to set them dynamically. When passing data dynamically, the Custom Component updates whenever the model updates.

  1. Use the inflow.html code below for the Iframe Code field.
<script src="https://cdn.tryretool.com/js/react.production.min.js" 
        crossorigin></script>
<script src="https://cdn.tryretool.com/js/react-dom.production.min.js" 
        crossorigin></script>
<div id="react"></div>
<script type="text/babel">
  const MyCustomComponent = ({ triggerQuery, model, updateModel }) => (
    <p>Hello, { model.name }!</p>
  );
  const ConnectedComponent = Retool.connectReactComponent(MyCustomComponent);
  ReactDOM.render(<ConnectedComponent />, document.getElementById('react'));
</script>

Pass data from your Custom Component to your app

Suppose that you want a Text component to display a value that was entered into an input within your Custom Component. Specifically, when the user types Valentine in your Custom Component, you want the Text component to display Hello, Valentine!.

Figure 4. An app that passes data from a Custom Component to a Text component

Figure 4. An app that passes data from a Custom Component to a Text component

To pass data from your Custom Component to the rest of your app:

  1. Use the outflow.html code below for the Iframe Code field. The third argument that's passed when the Custom Component is created, updateModel, is the key to passing data from your Custom Component to the rest of your app. updateModel takes a single argument of type object. This argument represents updates that should be made to the Custom Component's model. Think of updateModel() like setState().
<style>
  body {
    border: 5px solid red;
  }
  #react {
    display: flex;
    justify-content: center;
    align-items: center;
  }
</style>
<script src="https://cdn.tryretool.com/js/react.production.min.js" crossorigin></script>
<script src="https://cdn.tryretool.com/js/react-dom.production.min.js" crossorigin></script>
<script src="https://unpkg.com/@material-ui/core/umd/material-ui.production.min.js"></script>
<div id="react"></div>
<script type="text/babel">
  const { Input } = window["material-ui"];
  const MyCustomComponent = ({ triggerQuery, model, updateModel }) => (
    <Input
        color="primary"
        variant="outlined"
        value={model.textInputValue}
        onChange={(e) =>
          updateModel({ name: e.target.value })
        }
    />
  );
  const ConnectedComponent = Retool.connectReactComponent(MyCustomComponent);
  ReactDOM.render(<ConnectedComponent />, document.getElementById("react"));
</script>
  1. Set the value of the Text component to Hello, {{ customcomponent1.model.name }}!.

Trigger queries from your Custom Component

Suppose that you want to trigger a query based on text input that a user has typed within your Custom Component. For example, by default your table displays all books in your inventory:

Figure 5. The default view

Figure 5. The default view

After the user types something into the input within the Custom Component and then presses Search, the table only shows books with titles that match the user's input:

Figure 6. The updated view based on the user's search

Figure 6. The updated view based on the user's search

To trigger a query from your Custom Component:

  1. Set any model data that's required for your query. triggerquery.html contains the full Iframe Code that was used for the app in Figures 5 and 6. In this example app the Input component updates the Custom Component's model whenever the value of the Input changes.
<style>
  body {
    border: 5px solid red;
  }
  #react {
    display: flex;
    justify-content: center;
    align-items: center;
    height: 100%;
  }
  .button {
    margin-left: 1em;
  }
</style>
<script src="https://cdn.tryretool.com/js/react.production.min.js" crossorigin></script>
<script src="https://cdn.tryretool.com/js/react-dom.production.min.js" crossorigin></script>
<script src="https://unpkg.com/@material-ui/core/umd/material-ui.production.min.js"></script>
<div id="react"></div>
<script type="text/babel">
  const { Input, Button } = window["material-ui"];
  const MyCustomComponent = ({ triggerQuery, model, updateModel }) => (
    <div>
      <Input
          color="primary"
          variant="outlined"
          value={model.textInputValue}
          onChange={(e) =>
            updateModel({ name: e.target.value ? e.target.value : '%' })
          }
      />
      <Button
          className="button"
          color="primary"
          variant="outlined"
          onClick={() => triggerQuery('query1')}
      >
        Search
      </Button>
    </div>
  );
  const ConnectedComponent = Retool.connectReactComponent(MyCustomComponent);
  ReactDOM.render(<ConnectedComponent />, document.getElementById("react"));
</script>
  1. Use the triggerQuery function to trigger a query from within your Custom Component. In triggerquery.html the trigger is queried when a user clicks the Search button. This is handled by the onClick event listener of the Button component.

  2. Update your query so that it accesses data from your Custom Component's model. query1.sql provides the statement that was used for the app in Figures 5 and 6.

SELECT
  *
FROM
  products
WHERE
  name ILIKE {{ '%' + customcomponent1.model.name + '%' }}

Note that for the app in Figures 5 and 6, the name property of the Custom Component's model is set to % by default, as shown below in model.json. Note also the updateModel({ name: e.target.value ? e.target.value : '%' }) call in triggerquery.html. The % value is provided as a fallback for the name property so that the app displays the full inventory by default.

{
  name: "%"
}

More examples

Custom Plot.ly charts

Here's how to pass data from a Retool app to a Plot.ly chart.

Set the model for the Plot.ly chart:

{
  "plotData": { x: [1,2,3], y: [1,2,3]}
}

Add the following code to the Iframe Code field in order to create the Plot.ly chart:

<style>
  body {
    margin: 0;
  }
</style>
<script src="https://cdn.tryretool.com/js/react.production.min.js" crossorigin></script>
<script src="https://cdn.tryretool.com/js/react-dom.production.min.js" crossorigin></script>

<script src="	https://cdn.plot.ly/plotly-latest.min.js"></script>
<script src="https://unpkg.com/react-plotly.js@latest/dist/create-plotly-component.js"></script>
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
<script src="https://unpkg.com/@material-ui/core/umd/material-ui.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.9.1/underscore-min.js"></script>

<div id="react" />
<script type="text/babel">
	const Plot = createPlotlyComponent.default(Plotly);
  console.log(Plot);
  

  const MyCustomComponent = ({ triggerQuery, model, updateModel }) => (
      <Plot
        data={[
          {
            x: model.plotData.x,
            y: model.plotData.y,
            type: 'bar',
            marker: {color: 'purple'},
          }
        ]}
        onClick={(v) => {
					updateModel({ selectedPoints: v.points.map(p => ({ x: p.x, y: p.y })) })
        }}
        layout={ {title: 'Scatter Plot'} }
        style={ {width: "100%", height: "100vh"} }
      />
  );

  const ConnectedComponent = Retool.connectReactComponent(MyCustomComponent);
  ReactDOM.render(<ConnectedComponent />, document.getElementById("react"));
</script>

And voila!

Figure 7. The Custom Component featuring a Plot.ly chart

Figure 7. The Custom Component featuring a Plot.ly chart

Custom buttons / controls

Figure 8. A Custom Component featuring a Material Design button and input

Figure 8. A Custom Component featuring a Material Design button and input

Set the model for the Custom Component:

{
  "queryToTrigger": "query1",
  "textInputValue": "Hello world!"
}

Add the following code to the Iframe Code field in order to implement the Custom Component:

<style>
  body {
    margin: 0;
  }
</style>
<script src="https://cdn.tryretool.com/js/react.production.min.js" crossorigin></script>
<script src="https://cdn.tryretool.com/js/react-dom.production.min.js" crossorigin></script>
<script src="https://unpkg.com/@material-ui/core/umd/material-ui.production.min.js"></script>

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

<script type="text/babel">
  const { Button, Card, CardContent, Input } = window["material-ui"];

  const MyCustomComponent = ({ triggerQuery, model, updateModel }) => (
    <Card>
      <CardContent>
        <Button
          color="primary"
          variant="outlined"
          onClick={() => triggerQuery(model.queryToTrigger)}
        >
          Trigger {model.queryToTrigger}
        </Button>
        <br />
        <br />
        <Input
          color="primary"
          variant="outlined"
          value={model.textInputValue}
          onChange={(e) =>
            updateModel({ textInputValue: e.target.value })
          }
        />
      </CardContent>
    </Card>
  );

  const ConnectedComponent = Retool.connectReactComponent(MyCustomComponent);
  ReactDOM.render(<ConnectedComponent />, document.getElementById("react"));
</script>

Non-React JavaScript

If you're writing pure JavaScript that isn't React code, you can you use window.Retool to update models, trigger queries, or listen to new values injected into your model.

The relevant methods are updateModel, triggerQuery, and subscribe:

<html>
  <body>
    <script>
      function updateName(e) {
        window.Retool.updateModel({ name: e.target.value });
      }
      function buttonClicked() {
        window.Retool.triggerQuery('query1')
      }
      window.Retool.subscribe(function(model) {
        // subscribes to model updates
        // all model values can be accessed here
        document.getElementById("mirror").innerHTML = model.name || '';
      })
    </script>
    <input onkeyup="updateName(event)" />
    <button onclick="buttonClicked()">Trigger Query</button>
    <div id="mirror"></div>
  </body>
</html>

Custom Components


Suggested Edits are limited on API Reference Pages

You can only suggest edits to Markdown body content, but not to the API spec.