Scripting Retool

Run JS Code (Perform custom behavior using Javascript)

Retool allows interacting with components and queries through JavaScript methods. This is useful for building interactions between components that aren't currently supported via the properties exposed in the Inspector tab.

To use this feature, let's open up the bottom pane, create a new query and select the Run JS Code in the "Select resource" dropdown.

Here we'll be able to programmatically set component's properties, trigger queries, and access useful Javascript libraries. Below are some examples on how to use Javascript to take your Retool app to the next level.


Safety First!

Not all of Javascript will be available for you to use in your code snippets. In order to ensure safety and security for all users, certain interactions with the browser and the context outside of the Retool app will not be run. For example, you won't have access to browser events or libraries like jQuery.

Example: firing an API request for each row in a CSV

For this example, we'll try to fire an API request for each CSV, and then also show any errors that occurred along the way for each of the rows in the CSV.

1. Uploading a CSV

Create a FilePicker component - and a Table component. We'll use the table to render a preview of the CSV that we upload to Retool - so we'll set the Table's data property to filepicker1.parsedValue. The FilePicker component automatically parses CSVs and JSON files and you can access those values in the parsedValue property.

2. Crafting a query

Now that we have it hooked up, we'll create a query that we want to execute for each row in the table. By default the i property is going to be 0 - our JS Code is going to substitute the i property so that this query ends up executing against different data across the entire run of the query.

3. Add a button and make it execute query1 for each row in the CSV.

We'll create two text components - one to show the current status of the requests (e.g. how many have we processed) and one to show all the errors encountered while processing the CSV. We'll also add a button that will trigger the process.

And then, we'll have button1 trigger the following Javascript query.

var rows = filepicker1.parsedValue;
var errors = '';
var total = rows.length;

function runQuery (i) {
  // Update the statusText with the current progress
  statusText.setValue(i.toString() + '/' + total.toString())
  if (i >= rows.length) {
    console.log('Finished running all queries');

  console.log('Running query for row', i);

    additionalScope: { i: i }, // This is where we override the `i` variable from step 2!
    // You can use the argument to get the data with the onSuccess function
    onSuccess: function(data) {
      runQuery(i + 1);
    onFailure: function(error) {
      // Update the errorsText with all the errors encountered
      errors += 'Found error at line ' + i.toString() + ':  ' + error +  '\n\n';
      runQuery(i + 1);


4. Use it!

Since our api endpoint at doesn't actually exist, all our requests fail and we'll see that we found an error at each of the lines.

Common Use Cases

Clearing state after running a query

To clear state after a query runs, you can use the following code snippet:


Triggering a query

To programatically trigger a query, you can type the following:


You can also pass in additional arguments to customize the behavior of the query. Here is an example of doing so:

  additionalScope: {
    name: 'hi',
  // You can use the argument to get the data with the onSuccess function
  onSuccess: function(data) {
    console.log('Successully ran!');

The additionalScope option allows you to pass to the query additional variables not defined on the global scope. Now, in query1 you may use {{name}} to refer to the new variable defined by the added scope.

The onSuccess callback is called once the function successfully completes.

Retrieving Source of Query Trigger

The variable triggeredById will return the name of the component that triggered a query. You can reference this inside of {{ }} in any kind of query to return the name of the component which triggered it to run. If the query was triggered by another query, triggeredById will return undefined.

text1.setValue("I was triggered by component: " + triggeredById)

Retrieving Index of Triggering Component

If a query is triggered by a component inside of a listView or table, the variable i will be defined in the query and return the component's index in that listView or table.

text1.setValue("I was triggered by component at index: " + i)

Triggering a query for each item in an array

Here is a complete example of doing so:

var rows = [{ a: 1 }, { a: 2 }, { a: 3 }, { a: 4 }];

function runQuery (i) {
  if (i >= rows.length) {
    console.log('Finished running all queries');
  var data = rows[i];
  console.log('Running query for row', data);

    additionalScope: {
      data: data
    // You can use the argument to get the data with the onSuccess function
    onSuccess: function(data) {
      runQuery(i + 1);


Returning data

Besides just manipulating components and queries, Javascript queries can also return data. For example, let's say you want to generate a random number inside a Javascript query, and then use it elsewhere. You can do that by first writing some simple JS in the query (let's call the "query" generateRandomNumber):

return Math.random()

Now, when this query is triggered, you can access the number generated via the .data property on the query (so {{ }}).

Promises and async queries

Javascript queries can be asynchronous. If you return a Promise, Retool will wait for the promise to resolve before considering the query as "done."

Simple promise

Passing in a query to be triggered as the first argument in a Promise.resolve() will trigger the other query, wait for it to finish, and then return the .data result from the triggered query.

return Promise.resolve(query1.trigger())

Promising an array of query returns

You can also pass in an array of items which are dependent on query triggers, and use Promise.all() to return all of their results in an array of the same length. In this case, we would trigger query1 for each row in table1, and pass in the id value from each row. Using {{id}} inside query1 will evaluate as the provided row id when it is run.

const promises = => {
    return query1.trigger({
        additionalScope: {

return Promise.all(promises);

Resolve, reject

In this case, if you want to return a value from the query, you can pass it in as a parameter to the Promise's resolve function. You can also use the reject function from the Promise to fail the query.

In the following example, the query takes 2 seconds to run, and returns the value 12345 through the resolve function.

return new Promise((resolve, reject) => {
  setTimeout(() => {
  }, 2000)


Ask the community about JS/Scripting

If you're running into any issues with writing JS or scripting in Retool, check out some of the answered questions on our community forums, or ask one of your own.

Reference: Component and Query Methods.

All queries:


The options is an object with any of the following properties:

  additionalScope: {},
  onSuccess: (data) => { },
  onFailure: (error) => { },
  • The additionalScope option allows you to define or override the variables used in the query being triggered with any key/values present in the additionalScope object. For example, with additionalScope: { test: "New Value" }, {{test}} inside the triggered query would return "New Value" when it is run.


additionalScope keys will be undefined until run

In the triggered query, any keys passed in the additionalScope object will be available only when it is actually triggered by the javascript query. Until that exact moment, those values will evaluate as undefined

  • The onSuccess option is a callback that will be called when the query successfully finishes running, with the data of the query as an argument
  • The onFailure option is a callback that will be called when the query encounters an error while running with the error as an argument.

The .trigger function also returns a promise that resolves to the query's .data property.


This function clears the .data and .error property on the query.

Temporary State


Sets the value of the temporary state variable to whatever value is passed in as an argument

state.setIn(path, value)

Sets the value of state.value at a specified location. The path variable accepts an array of keys/indexes to select. The value will be set without destroying and recreating the other values of the state.
state.setIn( [ 0, "trout" ], {length_inches: 16 } )

Component JS Methods



Changes the selected row in the table to be the index (integer | array) passed as an argument. Can be a single index or an array of indexes ( [0,4,8] ). Sending null will clear the selection.


Changes the selected page in the table to be the index (integer) passed as an argument.


Overrides the data of the Table to whatever is passed as an argument. Any input type is accepted, but only an array of objects or object of arrays will display correctly.
[ { key1: val, key2: val }, { key1: val, key2: val } ] or { key1: [val, val], key2: [val, val] }

table.setSort(columnName, descending)

Sorts the table by a columnName (string) in a specified direction, true for descending, false for ascending.
table.setSort("name", true)

Passing in null will reset the sorting of the table:

table.setFilters(filters: FilterObject[], filterStackType?: 'and' | 'or' )

Set one or more filters with an array of filter objects.
setFilters([{ columnName: "name", filterValue: "max", operator: "contains" }])


Operator Syntax



does not contain


is empty


is not empty




does not equal


greater than


less than


greater than or equals


less than or equals






Passing in an empty array will reset the filters on the table:

When passing multiple filters, you should set the filterStackType. FilterStackType must be either "and" or "or" (defaults to "and" if no value set):
setFilters([{ columnName: "name", filterValue: "max", operator: "contains" }, { columnName: "name", filterValue: "john", operator: "contains" }], "or")

Text Component

text.setValue(string | number | null)

Sets the text value of the Text widget to whatever is passed in as an argument

Text Input

textinput.setValue(string | number | null)

Sets the value of the TextInput to the string or number passed as an argument, or clears the component if null is sent.


Focuses the text input, causing the user's cursor to select inside the input

Checkbox or Checkbox Group


Sets the value of the Checkbox to whatever boolean is passed in as an argument. Sending null here will change the value to null, but not uncheck the box graphically.

Rich text editor


Sets the value of the Rich Text Editor to whatever is passed in as an argument

JSON Editor


Sets the value of the JSON Editor to whatever is passed in as an argument.

JSON Schema Form


Clears all the inputs in the JSON Schema Form

Datetimepicker and Date range picker

datetimepicker.setValue(string | date)

Sets the value of the Date Time Picker to whatever is passed in as an argument. It will attempt to parse any string value as a date.

daterangepicker.setStartValue(string | date)

daterangepicker.setEndValue(string | date)

Sets the start or end value of the Date Range Picker to whatever is passed in as an argument. It will attempt to parse any string value as a date.

daterangepicker.setRange( [ startValue, endvalue ] )

Sets the start and end values of the Date Range picker. Accepts and array of two dates or strings as the argument for the start and end values.



Clears all the inputs in the Form

Tabbed Container


Changes the selected tab in the tabbed container to be the index (integer) passed as an argument.


container.scrollIntoView({behavior: string})

Scrolls the page to the container component. Accepts a behavior object such as {behavior:'smooth'} to change the scrolling behavior, with either "smooth" or "auto" as the behavior value.


Opens a modal


Closes the modal

Select and Multiselect

select.setValue(string | number | null)

Sets the value of the Select component (Dropdown) to whatever is passed in as an argument

multiselect.setValue(string[] | number[])

Sets the value of the Multi-Select component to whatever is passed in as an argument. The argument should be an array since the multiselect's .value property is an array.



No arguments needed. Reloads the current URL of the targeted iFrame component.

Radio Group

radiogroup.setValue(string | number | null)

Sets the value of the Radio Group to whatever is passed in as an argument

Button Group


Sets the value of the Button Group to whatever is passed in as an argument



Sets the value of the Rating component to whatever is passed in as an argument



Resets the filepicker to it's default state (no file selected).


utils.openUrl(url, { newTab: boolean = true, forceReload: boolean = false })

Opens a URL (string) in a new tab by default. Pass in { newTab: false } to open url in current tab. Pass in { forceReload: true } to prevent client side routing and force a page reload. URL string must start with http:// or https://.

utils.downloadFile(data, fileName, fileType)

Downloads the data value using fileName (string) and fileType (string) if possible

Note: to download base64 encoded files, use the following syntax

utils.downloadFile({base64Binary: BASE64_STRING} , fileName, fileType)


Example: Download query data as CSV is a Postgres query.

In order to download this data as CSV,
utils.downloadFile(Papa.unparse(formatDataAsArray(, 'test_csv', 'csv')


Example: REST Query as .docx

RESTQuery1 fetches from

Call utils.downloadFile( to download metrics.pdf
Call utils.downloadFile(, 'custom_file', 'docx') to download the same file as custom_file.docx

utils.showNotification({ title?: String, description?: String, notificationType?: String, duration?: Number })

Shows a notification message on the top right corner of the screen for duration seconds (default 4.5s). Use this to display messages like error messages after a query fails. Supported notificationTypes are : "info" | "success" | "warning" | "error"

localStorage.setValue(key, value)

Saves a key-value pair to localStorage. The key must be a string and the value can be any type. You can access the data from localStorage elsewhere by using {{ localStorage.values.yourKey }}. You can clear the localStorage with the JavaScript function localStorage.clear().

Tip: to remove a single key-value pair from localStorage, you can set the value to undefined, like this: localStorage.setValue("removeThisKey",undefined).

utils.downloadPage(fileName, { selectorsToExclude, componentsToExclude, scale, fullscreen })

Downloads the current Retool page as a PDF






Required, Name to use for the exported file.



Optional, lets you specify an array of components by name (e.g. ['select1', 'textinput1']) to exclude from the screenshot.



Optional, lets you specify an array of components by css selector (e.g. ['._retool-text4']) to exclude from the screenshot.



Optional, lets you pass a numerical "scale" parameter to configure the resolution (defaults to window.devicePixelRatio)



Optional, true: exports entire app in presentation mode


Known Limitation

Custom components and any other elements embedded in iFrames will not appear in the pdf export.

utils.copyToClipboard( text: string )

Sets the clipboard's content to the provided string value


Anything between {{ }} is Javascript. We give you the following utility libraries:









Both v1 and v4; use uuid.v1() or uuid.v4()





Updated about a month ago

Scripting Retool

Run JS Code (Perform custom behavior using Javascript)

Suggested Edits are limited on API Reference Pages

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