# How to Use Global Variables in Retool

- Tool: Retool
- Difficulty: Beginner
- Time required: 10-15 min
- Compatibility: Retool Cloud and Self-hosted
- Last updated: March 2026

## TL;DR

Global variables in Retool persist across all pages in a multipage app. Create them in the Globals panel, set values with global.setValue('varName', value) in a JS Query, and read them anywhere with {{ global.varName }}. Unlike temporary state variables, globals survive page navigation for the duration of the user session.

## Cross-Page State with Global Variables

Global variables in Retool allow you to store data that is accessible from any page in a multipage application. This is fundamentally different from temporary state variables, which are scoped to a single page and reset on navigation.

A common use case is storing the currently selected record (e.g., a customer ID) on one page so that a detail page can query for it without relying on URL parameters. Another use case is tracking the user's workflow progress across a wizard-style app that spans multiple pages.

This tutorial covers creating global variables, setting their initial values, updating them from JS Queries, and reading them in component bindings and subsequent queries.

## Before you start

- A Retool account (Cloud or Self-hosted)
- A multipage Retool app (or a single-page app to understand the concept)
- Basic familiarity with the Retool editor and JS Queries
- Understanding of the difference between the Code panel and the Inspector panel

## Step-by-step guide

### 1. Open the Globals panel in the left sidebar

In the Retool editor, click the left sidebar and locate the Globals tab (it looks like a globe icon). This panel lists all global variables defined for the current app. If you are working in a single-page app, you will not see Globals — global variables only appear in multipage apps. If you do not have a multipage app, go to the app settings and enable multiple pages first.

**Expected result:** The Globals panel is visible and shows an empty list or existing global variables.

### 2. Create a new global variable

In the Globals panel, click the + button to add a new global variable. Give it a descriptive name such as selectedCustomerId. Set the Default value — this can be an empty string, null, 0, or any JSON-serializable value. The default value is what the variable holds when the app first loads, before any JS Query has set it.

**Expected result:** A new global variable named selectedCustomerId appears in the Globals panel with the default value you specified.

### 3. Set the global variable from a JS Query

Create a new JS Query (click + in the Code panel → JavaScript). To update the global variable, call global.setValue() and await the result. The method signature is: await global.setValue('varName', newValue). Note that setValue is asynchronous — if you need to read the value immediately after setting it, you must await the call or read from a local variable instead of from global.varName right away.

```
// In a JS Query named 'setSelectedCustomer'
// Called from a Table row click event handler
const selectedRow = table1.selectedRow;
const customerId = selectedRow.data.id;

// setValue is async — always await it
await global.setValue('selectedCustomerId', customerId);

// Now navigate to the detail page
utils.openPage('CustomerDetail');
```

**Expected result:** The global variable selectedCustomerId is updated to the selected row's ID, and the app navigates to the CustomerDetail page.

### 4. Read the global variable in a component or query

On any page in the app, you can reference a global variable using double curly braces: {{ global.selectedCustomerId }}. Use this in a query's WHERE clause, a Text component's value, or a conditional visibility expression. For example, in a SQL query on the CustomerDetail page, you would write WHERE id = {{ global.selectedCustomerId }} to fetch the selected customer's data.

```
-- SQL Query on CustomerDetail page
SELECT *
FROM customers
WHERE id = {{ global.selectedCustomerId }}
LIMIT 1;
```

**Expected result:** The query returns data for the customer whose ID was stored in the global variable.

### 5. Reset a global variable to its default

To reset a global variable to its default value, use global.resetValue('varName') from a JS Query. This is useful when a user logs out or navigates back to a list view and you want to clear the selection state. You can call this in an event handler on a Back button or in a page-load event.

```
// JS Query: resetGlobalVars
await global.resetValue('selectedCustomerId');
// Variable is now back to its default value (e.g., null)
```

**Expected result:** The global variable reverts to its default value. Components bound to it re-render with the default state.

### 6. Use global variables for multi-page wizard state

A powerful pattern for multi-page wizards is to accumulate form data into a global object variable. Create a global variable named wizardData with a default value of {}. On each page, merge new form fields into it using global.setValue(). On the final submission page, read the complete object and send it to your backend query.

```
// Page 1 — save step 1 data
const existing = global.wizardData;
await global.setValue('wizardData', {
  ...existing,
  firstName: textInput1.value,
  lastName: textInput2.value
});
utils.openPage('WizardStep2');

// Final page — submit accumulated data
const formData = global.wizardData;
await submitNewUser.trigger({
  additionalScope: { payload: formData }
});
```

**Expected result:** Each wizard page adds its data to the global variable, and the final page can access the full accumulated form data.

## Complete code example

File: `JS Query: globalVariableWorkflow`

```javascript
// === Page 1: List page — set global on row click ===
// Event handler on table1 (Row click)
const selectedRow = table1.selectedRow;
if (!selectedRow || !selectedRow.data) {
  utils.showNotification({ title: 'No row selected', notificationType: 'warning' });
  return;
}

// Set the global variable (async — always await)
await global.setValue('selectedCustomerId', selectedRow.data.id);

// Optionally store full row data for richer detail page
await global.setValue('selectedCustomerData', selectedRow.data);

// Navigate to detail page
utils.openPage('CustomerDetail');

// === CustomerDetail page — read global in SQL query ===
// Query: getCustomerDetail (runs on page load)
// SQL:
// SELECT c.*, o.count AS order_count
// FROM customers c
// LEFT JOIN (
//   SELECT customer_id, COUNT(*) AS count FROM orders GROUP BY customer_id
// ) o ON o.customer_id = c.id
// WHERE c.id = {{ global.selectedCustomerId }}

// === Reset on back button click ===
// JS Query: goBackToList
await global.resetValue('selectedCustomerId');
await global.resetValue('selectedCustomerData');
utils.openPage('CustomerList');
```

## Common mistakes

- **Reading global.varName immediately after calling setValue() without await** — undefined Fix: Always use await global.setValue('varName', value) and then read global.varName in a subsequent line or query. Without await, you are reading the stale value.
- **Creating global variables in a single-page app and wondering why they are not available** — undefined Fix: Global variables only exist in multipage apps. For single-page apps, use Temporary State (state variables) instead.
- **Storing large arrays or query result sets in global variables** — undefined Fix: Global variables are held in memory and serialized. Store only IDs or minimal selector state in globals, then re-query the full data on each page.
- **Forgetting that global variables reset to defaults on a fresh browser session** — undefined Fix: Do not rely on global variables for data that must survive page refresh. Use URL parameters or query the database for persistent state.

## Best practices

- Always await global.setValue() calls — it is asynchronous and not reading the value immediately after setting without await will give you stale data.
- Set meaningful default values (e.g., null for IDs, {} for objects, [] for arrays) so components bound to globals render correctly on first load.
- Do not store sensitive data like passwords or tokens in global variables — they are accessible from any query or component in the app.
- Prefer global variables over URL parameters when passing complex objects between pages, since URL params only support strings.
- Use global.resetValue() in cleanup handlers (logout, back navigation) to prevent stale state leaking between user sessions.
- Keep global variable names descriptive and namespaced (e.g., selectedOrderId, filterState) to avoid confusion in large apps.
- Document your global variables in the app's description or a comment-only text component so teammates understand the data flow.

## Frequently asked questions

### Do Retool global variables persist after a page refresh?

No. Global variables reset to their default values on every new browser session or page refresh. They only persist during active navigation within a single session. For data that must survive refresh, use URL parameters or query your database.

### Can I use global variables in single-page Retool apps?

No. The Globals panel and global.setValue() are only available in multipage apps. For single-page apps, use Temporary State variables (state1.value and state1.setValue()) to manage cross-component state.

### Is there a limit to how many global variables I can create in Retool?

Retool does not publish a hard limit on global variable count, but best practice is to keep the number small. Each global variable adds to the app definition size. Store IDs and minimal state rather than full query result sets.

---

Source: https://www.rapidevelopers.com/retool-tutorial/how-to-use-global-variables-in-retool
© RapidDev — https://www.rapidevelopers.com/retool-tutorial/how-to-use-global-variables-in-retool
