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');
return;
}
console.log('Running query for row', i);
query1.trigger({
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';
errorText.setValue(errors);
runQuery(i + 1);
}
});
}
runQuery(0);
4. Use it!
Since our api endpoint at https://approvals.tryretool.com/api/users/approve 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:
userInput.setValue('');
emailInput.setValue('');
pricingTierDropdown.setValue(null);
Triggering a query
To programatically trigger a query, you can type the following:
query1.trigger()
You can also pass in additional arguments to customize the behavior of the query. Here is an example of doing so:
query1.trigger({
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');
return;
}
var data = rows[i];
console.log('Running query for row', data);
query1.trigger({
additionalScope: {
data: data
},
// You can use the argument to get the data with the onSuccess function
onSuccess: function(data) {
runQuery(i + 1);
}
});
}
runQuery(0);
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 {{ generateRandomNumber.data }}
).
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 = table1.data.map(row => {
return query1.trigger({
additionalScope: {
"id": row.id
}
});
});
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(() => {
resolve(12345)
}, 2000)
})
Reference: Component and Query Methods.
All queries:
query.trigger(options)
query.trigger(options)
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.
query.reset()
query.reset()
This function clears the .data
and .error
property on the query.
Temporary State
state.setValue(value)
state.setValue(value)
Sets the value of the temporary state variable to whatever value is passed in as an argument
state.setIn(path, value)
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
Table
table.selectRow(index)
table.selectRow(index)
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.
table.selectPage(index)
table.selectPage(index)
Changes the selected page in the table to be the index (integer) passed as an argument.
table.setData(data)
table.setData(data)
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)
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.setSort(null)
Text Component
text.setValue(string | number | null)
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)
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.
textinput.focus()
textinput.focus()
Focuses the text input, causing the user's cursor to select inside the input
Checkbox or Checkbox Group
checkbox.setValue(bool)
checkbox.setValue(bool)
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
texteditor.setValue(string)
texteditor.setValue(string)
Sets the value of the Rich Text Editor to whatever is passed in as an argument
JSON Editor
jsoneditor.setValue(value)
jsoneditor.setValue(value)
Sets the value of the JSON Editor to whatever is passed in as an argument.
JSON Schema Form
jsonschemaform.clear()
jsonschemaform.clear()
Clears all the inputs in the JSON Schema Form
Datetimepicker and Date range picker
datetimepicker.setValue(string | date)
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.setStartValue(string | date)
daterangepicker.setEndValue(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 ] )
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.
Form
form.clear()
form.clear()
Clears all the inputs in the Form
Tabbed Container
tabbedcontainer.selectTab(index)
tabbedcontainer.selectTab(index)
Changes the selected tab in the tabbed container to be the index (integer) passed as an argument.
Container
container.scrollIntoView({behavior: string})
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.
Modal
modal.open()
modal.open()
Opens a modal
modal.close()
modal.close()
Closes the modal
Select and Multiselect
select.setValue(string | number | null)
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[])
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.
IFrame
iframe.reload()
iframe.reload()
No arguments needed. Reloads the current URL of the targeted iFrame component.
Radio Group
radiogroup.setValue(string | number | null)
radiogroup.setValue(string | number | null)
Sets the value of the Radio Group to whatever is passed in as an argument
Button Group
buttongroup.setValue(string)
buttongroup.setValue(string)
Sets the value of the Button Group to whatever is passed in as an argument
Rating
rate.setValue(number)
rate.setValue(number)
Sets the value of the Rating component to whatever is passed in as an argument
Filepicker
filepicker.reset()
filepicker.reset()
Resets the filepicker to it's default state (no file selected).
Utilities
utils.openUrl(url, inNewPage)
utils.openUrl(url, inNewPage)
Opens a URL(string) in a new page by default. It can also be configured to open the URL in the same page by passing false
in the second argument.
utils.downloadFile(data, fileName, fileType)
utils.downloadFile(data, fileName, fileType)
Downloads the data value using fileName (string) and fileType (string) if possible
Example
RESTQuery1
fetches from http://somewebsite.com/metrics.pdfCall
utils.downloadFile(RESTQuery1.data)
to download metrics.pdf
Callutils.downloadFile(RESTQuery1.data, 'custom_file', 'docx')
to download the same file as custom_file.docx
utils.showNotification({ title: string, description: string, notificationType: string })
utils.showNotification({ title: string, description: string, notificationType: string })
Shows a notification message on the top right corner of the screen. Use this to display messages like error messages after a query fails. Title and Description are strings, accepts string for notificationTypes of : 'info' | 'success' | 'warning' | 'error'.
localStorage.setValue(key, value)
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 })
utils.downloadPage(fileName, { selectorsToExclude, componentsToExclude, scale, fullscreen })
Downloads the current Retool page as a PDF
Property | Type | Notes |
---|---|---|
| String | Required, Name to use for the exported file. |
| String[] | Optional, lets you specify an array of components by name (e.g. ['select1', 'textinput1']) to exclude from the screenshot. |
| String[] | Optional, lets you specify an array of components by css selector (e.g. ['._retool-text4']) to exclude from the screenshot. |
| Number | Optional, lets you pass a numerical "scale" parameter to configure the resolution (defaults to window.devicePixelRatio) |
| Boolean | Optional, |
Known Limitation
Custom components and any other elements embedded in iFrames will not appear in the pdf export.
Libraries
Anything between {{ }}
is Javascript. We give you the following utility libraries:
Name | Docs | Versions |
---|---|---|
|
| |
|
| |
| Both | |
|
|
Updated 9 days ago