# Retool Documentation

> Complete product documentation for Retool.

This file contains all documentation content in a single document following the llmstxt.org standard.

## Agent-to-agent communication

The Agent-to-agent (A2A) protocol provides standardized and secure communication between external agents and agents built with Retool, so you can trigger your agent from an external agent, or embed agents in your own systems.

## Overview
A2A support allows you to build agents across multiple platforms and have them talk to one another programmatically. You may want to use A2A, for example, to build distributed AI systems across different regions, to connect third-party agents to internal orchestration agents, or to daisy-chain specialized agents together to complete complex tasks.    

:::info
Currently, Retool supports ingress into Retool agents from an external agent.
:::

Retool has implemented the [core set of A2A functionality](https://a2a-protocol.org/v0.2.0/topics/key-concepts/#fundamental-communication-elements), so you can: 
    - View an agent card.
    - Send a message.
    - Poll for updates on tasks.
    - Stream processing or task updates via Server-Sent Events (SSE). 
    - Cancel tasks. 

Refer to [Set up an A2A trigger](../guides/triggers/trigger-with-a2a.mdx) to learn how to enable the A2A trigger, and [A2A endpoints](../reference/a2a-endpoints.mdx) to learn how to cURL for endpoint responses. 

## How it works
The diagram below details the four main steps an A2A request follows, as well as the interactions between the client, the A2A server, and your agent. 

## Limitations
Retool's A2A support has the following limitations: 

- Retool only supports ingress for A2A. 
- Retool only supports the HTTP+REST and JSON-RPC protocols.
- Retool only supports API key-based authentication. 
- Retool does not support the [input-required](https://a2a-protocol.org/v0.2.0/specification/#93-multi-turn-interaction-input-required) or [auth-required](https://a2a-protocol.org/v0.2.0/specification/#63-taskstate-enum) task states, so when using A2A with Retool agents, calling tools that require approvals and have delegated authentication will fail. 
 
Additional functionality will be added over time. 

## FAQ

### How can I provide feedback on A2A? 
Please provide feedback at [this form link](https://example.retool.com/form/ddaf5f13-5d66-4aa8-868b-95dfc4edff61) or reply to the [Agent-to-agent protocol support topic](https://community.retool.com/t/agent-to-agent-protocol-support/62334) the Retool Community Forum. 

### Does enabling A2A expose all agents? 
No, Retool requires you to enable A2A as a trigger on a specific agent. Self-hosted Retool customers can toggle off the **AI Agents A2A** feature flag in **Settings** > **Beta** to remove A2A support; it is on by default. 

### Can I call Retool Agents from non-agent systems like external apps or backends?
Yes, A2A can be used by a programmatically-invoked REST API from non-agent systems. 

### Will Retool support additional transport protocols like gRPC?
Retool will support additional protocols depending on customer demand. If you'd like to request additional protocol support, [please fill out this form](https://example.retool.com/form/ddaf5f13-5d66-4aa8-868b-95dfc4edff61). 

### Does Retool A2A support streaming responses?
Yes, Retool supports streaming responses via SSE. 

### Can we connect to external A2A systems from Retool?
Yes, Retool supports ingress into Retool agents via the A2A protocol. You can build custom tools in agents, or custom resource queries in apps and workflows to trigger external agents via REST APIs.

### The URL value in my agent card is missing the HTTP or HTTPS prefix, how do I fix this?
For self-hosted Retool, set the [BASE_DOMAIN](../../self-hosted/reference/environment-variables/#property-BASE_DOMAIN.mdx) environment variable. 

### Can I rotate my API keys? 
Yes, API keys can be rotated. An API key is provided for each agent, and must be passed to the client via an `X-Api-Key` header.

## Additional resources
To continue learning about A2A, explore the following resources:

* [Set up an A2A trigger](../guides/triggers/trigger-with-a2a.mdx)
* [A2A tutorial](../tutorials/a2a-tutorial.mdx)
* [What is A2A Protocol?](https://a2a-protocol.org/dev/)
* [A2A endpoints](../reference/a2a-endpoints.mdx)

---

## Agentic workflows

import Shared from '/docs/_shared/_agentic-workflows.mdx';

---

## Retool Agents best practices

Retool recommends using the following best practices to create operationally efficient, cost-aware, performant, reliable, secure, and sustainable agents.

## Operational excellence
Best practices for operating Retool Agents effectively include implementation strategies, monitoring, and continuous improvement methodologies.

Test agents using the built-in [chat](../guides/chat-with-agent.mdx) interface, which uses the latest saved version, before deploying to users.

The agent logs provide run details including inputs, outputs, thoughts, and runtime information helpful for debugging tool and LLM calls.

Use [evals](./evals.mdx) for systematic testing of agent behavior after significant changes to instructions or tool descriptions, ensuring consistent performance and quality across scenarios.

Use the monitoring page to observe agents in production, providing visibility into runs, errors, tool calls, token usage, cost, runtime and more.

## Cost optimization
Best practices on managing and optimizing costs associated with Retool Agents include resource allocation, usage patterns, and budgeting considerations.

Clear [instructions](#instruction-guidance) allow the agent to optimally choose tools and iterate the fewest number of times, reducing total runtime and associated costs.

Clear, short, specific tool descriptions allow the agent to optimally select its execution path, reducing unnecessary tool calls and iterations.

Consider the total size of responses from your tool calls. Returning large amounts of data results in token-inefficient LLM calls, so wherever possible, minimize the data returned from tools.

Select the fastest model that can effectively solve your particular problem to minimize total runtime and associated costs.

## Performance
Best practices for performance include strategies and techniques to maximize the performance of Retool Agents, including response-time optimization, and throughput considerations.

Connect only the necessary [tools](../guides/tools) to each agent.

Set clear timeout thresholds for each tool to prevent long-running tool calls from degrading overall agent responsiveness.

### Instruction guidance

In general, Retool Agents perform best when they have a set of well-written instructions. Use the following best practices to create instructions with Retool Agents:

* **Explicitly state the agent's purpose**: Clearly specify the agent's role and goals upfront to ensure alignment.

  
  ```You are a data analyst agent tasked with identifying anomalies in daily sales data.```
  

* **Break down tasks clearly**: Provide step-by-step guidance to simplify complex instructions into manageable steps.

  
  ```
  1. Pull sales data from the provided database.
  2. Calculate the weekly average sales figures.
  3. Identify any days with sales deviations greater than ±15% from the weekly average.
  ```
  

* **Be precise**: Explicitly state validation rules and decision-making criteria. Include relevant context directly in each instruction; avoid relying on prior prompts or implicit context.

  
  ```All phone numbers must contain exactly 10 digits. If a provided number doesn't meet this criterion, flag it and request correction.```
  

* **Include explicit error handling guidance**: Clearly state how the agent should handle errors, edge cases, or unexpected scenarios.
  
  ```If data retrieval fails due to an API error, notify the administrator immediately with the specific error message and retry once after 5 minutes.```
  

* **Provide information about the user and context**: Provide the agent with dynamic information about the [current_user](../reference/current-user.mdx), date, and time zone using embedded expressions. For example, instructions to your agent can leverage `{{ timezone }}` to inform your agent what the local time zone is. This embedded expression evaluates to the [IANA time zone name](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime#time_zones_and_offsets).
  
  ```You are a helpful assistant chatting with {{ current_user.name }}. Today is {{ new Date() }} in the {{ timezone }} time zone.```
  

Refer to the [Configuration](../quickstart.mdx#configuration) section of the Quickstart guide for additional instruction guidance.

#### Example instructions
You can use the following examples as templates when creating instructions for Retool Agents.

  

```
You are a data analysis assistant. When analyzing the provided dataset:
1. First identify the key metrics present
2. Calculate relevant statistical measures
3. Present insights in bullet points
4. If you spot any anomalies, highlight them separately
```

  
  

```
You are helping with Retool app debugging. Given the error message:
- First identify the component type involved
- Check for common configuration issues
- Suggest specific fixes
- Provide example code if relevant
Only use features available in the current Retool version.
```

  
 

```
Act as a SQL query optimizer. For any query provided:
1. Analyze the query structure
2. Identify potential performance bottlenecks
3. Suggest specific optimizations
4. Explain the reasoning behind each suggestion
```

  

#### Common issues with instructions
The following table provides some common pitfalls or issues you may encounter when crafting instructions for agents, as well as do's and don'ts for instruction writing.

| Issue                                    | Do                                                                                                      | Don't                                                                            |
| -----------------------------------------| ------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------|
| Vague or ambiguous instructions          | Write specific, clear instructions, that include numbers or percentages if necessary.                       | Write vague or short instructions that don't provide enough context for the model.                                  |
| Providing too much information           | Provide only the context directly relevant to the current task.                                            | Include unnecessary historical context or irrelevant details.                  |
| Lack of error handling guidance          | Point out what the agent should do if it encounters missing data or errors when running.  | Fail to specify how to handle edge cases or errors.           |
| Inconsistent output requirements         | Clearly define output format upfront and maintain consistency.                                          | Fail to specify response format or change format requirements mid-conversation. |
| Assuming prior context                   | Include necessary context in each instruction, even if briefly.                                                 | Refer to information from previous conversations without restating it.       |
| Imprecise tool usage                     | Specify which tool to use for certain tasks.      | Fail to specify which tools the agent should use for specific tasks.             |
| Missing validation requirements          | Include specific validation criteria and acceptable ranges or values.                                         | Fail to specify what makes a response acceptable.                                 |
| Unclear priority order                   | Clearly define the steps an agent should take, and in what order (first, second, third, etc.).                | Fail to specify the order of operations for multiple tasks.                       |
| Lack of response size guidelines         | Specify the amount of bullet points or words an agent should provide in a summary.                        | Fail to specify the desired level of detail or response length                       |

## Reliability

Best practices to ensure the reliability of Retool Agents include error handling, failover mechanisms, and continuous availability strategies.

Implement proper error handling in custom tools and agent instructions, returning errors that the LLM can understand to recover from failure situations.

Use [evals](./evals.mdx) and [chat](../guides/chat-with-agent.mdx) to test agents thoroughly across different scenarios to help reduce failures and errors.

Monitor total runtime and individual agent runs from the Monitor page.

Include thorough descriptions of what was changed and whether the change was major, minor, or a patch.

## Security
Best practices for security considerations when implementing and using Retool Agents include data protection, access controls, and compliance requirements.

Apply the principle of least access when [permissioning agents](../../permissions/guides/agent-permissions.mdx), which follow the same permission model as apps and workflows. Use role-based access control for agent management.

Carefully consider what data you expose through tools, as agents have full access to any data accessible via their connected tools. Use OAuth to authenticate tools where possible so that user data access is scoped to their permissions. Limit access to only what's necessary for the agent's function.

Implement role-based permissions across Retool to ensure that agent builders do not accidentally use resources they should not have access to.

## Sustainability
Best practices on sustainable usage patterns for Retool Agents focus on resource efficiency and environmental impact considerations.

Design prompts, instructions, and tool responses to minimize token consumption by using concise language and limiting data transfer to only what's necessary.

Cache responses when appropriate, use streaming for long-running processes, and implement rate limiting to prevent resource overuse during peak periods.

Schedule periodic reviews of agent usage patterns and resource consumption to identify and address inefficiencies in your implementation.

---

## Evals in Retool Agents

Evals are systematic tests to assess large language models (LLMs) and agents on tasks like reasoning, accuracy, or safety. They use benchmarks or real-world scenarios to ensure models produce reliable, coherent, and ethical outputs.

When running an eval, Retool provides an input to the LLM. A [reviewer](#reviewer) scores the LLM output (0 to 1). Sometimes there are clear expected outputs (for example, [programmatic reviewers](#programmatic) like exact match), and sometimes expected outputs are less clear (for example, [LLM as a judge](#llm-as-a-judge) reviewers like tone detection).

:::info
Eval runs count towards billable agent runtime. For more information about billing, refer to the [Billing and usage](/support/billing-usage/agents) page.
:::

By tracking and scoring the results of your evals, you can test agents to determine if you've introduced breaking changes to an agent, you've improved the agent’s behavior, or if the agent is working as expected.

## Evals tab
import SharedEvals from '/docs/_shared/_agent-evals.mdx';

## Reviewers

*Reviewers* score the correctness of the agent's output, and provide an explanation for the score. During the Eval run, a reviewer accepts the output from the LLM, parameters that vary based on the reviewer type selected, and returns a score beetween 0 and 1.

Retool provides *Programmatic* and *LLM as a Judge* preconfigured reviewers you can use to help evaluate your agent.

### Programmatic

Programmatic reviewers use code to score output based on predefined rules. Use programmatic reviewers when you can clearly define the agent's expected output.

| Reviewer name               | Description                                                                                                                                           | Parameters                                                                                       |
| ----------------------------| ----------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------|
| **Exact match**             | Determines whether two values are the same.                                                                                                           | None                                                                                             |
| **Valid JSON**              | Evaluates the validity of the JSON output.                                                                                                            | None                                                                                             |
| **Valid XML**               | Evaluates the validity of the XML output.                                                                                                             | None                                                                                             |
| **String contains**         | Determines whether a string contains a certain value.                                                                                                 | `searchString` - the search string to look up  `ignoreCase` - ignore capitalization.       |
| **JSON Schema match**       | Determines whether the output of the agent is valid JSON conforming to the schema you specify.                                                        | `JSON Schema` - the format of your JSON schema.                                                  |
| **Levenshtein**            | A similarity score based on the Levenshtein distance between two strings with the formula `1 - (lev / length of longer string) = similarity score`. For example, the distance between `Hello World` and `hello borld` is `1` and the output of the reviewer is `0.91` | None                                                                                             |

### LLM-as-a-judge

LLM-as-a-judge reviewers use an LLM to determine the agent's score based on what you define in the input **Prompt** and **Choice score** between 0 and 1. Use LLM as a Judge reviewers when the agent's expected output is not clearly defined.

| Reviewer name               | Description                                                                                                  | Parameters                              |
| ----------------------------| ------------------------------------------------------------------------------------------------------------ |-----------------------------------------|
| **Tone detection**          | Evaluates the tone of an agent's output based on the user input prompt.                                      | `Prompt`  `Choice Scores`         |
| **Factuality**              | Evaluates whether an output is factual.                                                                      | `Prompt`  `Choice Scores`         |
| **Closed QA**               | Evaluates whether an output answers the input.                                                               | `Prompt`  `Choice Scores`         |

---

## Agents FAQ

This FAQ aims to provide answers to common questions about Agents, and provide you with the information you need to leverage Agents effectively.

## General
Frequently asked general questions about Agents:

Please share feedback in Retool’s [community forum](https://community.retool.com/t/launch-day-retool-agents-is-here/58551).

Reach out to your Enterprise account manager or [contact Retool support](/support).

The following links provide more information about AI and Agents.

- [AI Fundamentals YouTube playlist](https://www.youtube.com/playlist?list=PLqWdQFDVLADmJG9eNFIxe-d6yn227hQ_m)
- [Getting Started with AI in Retool YouTube playlist](https://www.youtube.com/playlist?list=PLqWdQFDVLADlN36WdDyPy5_fl0N2M0NZg)
- [Community forum](https://community.retool.com/t/launch-day-retool-agents-is-here/58551)
- [Builder Talks](https://community.retool.com/t/builder-talks-2-exploring-ai-with-kent-matei/58250)
- [Retool University](https://university.retool.com/p/login) links:
  - [Retool Intro to GenAI](https://university.retool.com/app/portal/courses)
  - [AI Agent Concepts](https://university.retool.com/app/portal/courses)
  - [AI Fundamentals Path](https://university.retool.com/app/portal/courses)
  - [Agents](https://university.retool.com/app/portal/courses)
  - [Lab: Retool Vectors](https://university.retool.com/app/portal/labs)
  - [Lab: AI Action](https://university.retool.com/app/portal/labs)
  - [Lab: Agents](https://university.retool.com/app/portal/labs)

## Data collection and privacy
Frequently asked questions about data collection and how information is used with Agents:

No, Retool uses foundation models. Neither Retool nor the providers of the third-party models will use agent inputs or outputs to train or fine-tune models. Prior to making a third-party model available for use with Agents, Retool ensures contractual safeguards are in place that restrict such providers from using inputs and outputs for their own purposes.

Retool engages other vendors to help provide agents. Vendors that process agent inputs and outputs on Retool’s behalf are [Retool’s sub-processors](/legal/subprocessors) and are also contractually restricted from using inputs and outputs for their own purposes.

No, Retool-managed, open-source models are hosted by Retool’s third-party infrastructure provider. Inputs and outputs will not be shared with the provider of the open-source model.

The data sent to the Retool-managed (cloud-hosted) agents observability API consists of the following:

- UUIDs corresponding to objects in the Retool deployment's Postgres database.
- Success/failure status of agent runs.
- Model type of agent runs.
- Runtime of agent runs.
- Number of LLM tokens used by agent runs.
- Information about the tools used during agent runs. Tool information is limited to UUIDs and the configured tool name and tool parameter names.

No customer data beyond the names of configured tools and tool parameters are sent to this API.

If you have any feedback or concerns about this data, please reach out to your account team.

## AI models and LLM keys
Frequently asked questions about AI models and LLM-provider keys:

Retool-managed keys are available for certain [AI providers and models](../../data-sources/concepts/models). If you use Agents with any of these third-party models, the third-party model will process all agent inputs, including the prompt provided by the internal user of the agent, to initiate performance of a task or to generate output. Please review and comply with any policies published by the provider of the third-party model.

Yes. When you bring your own key to an account with a model provider, the provider’s handling of data while under its control will be governed by the agreement you have with that provider. When output data that is returned by the provider is in Retool’s control, such data will be subject to your agreement with Retool.

## Self-hosted
Frequently asked questions about self-hosted deployments and Agents:

import PY1 from '/docs/_partials/_agent-docker-compose-yaml.mdx';

import PY2 from '/docs/_partials/_agent-evals-self-hosted.mdx';

No agent inputs or outputs are sent back to Retool servers.

Agents is in public beta with the [3.234.0 edge release](../../../releases/edge/) and the [3.253.0 stable release](../../../releases/stable/) for Self-hosted organizations.

---

## Retool Agents overview

Retool Agents is a product that makes it simple for builders to automate work using large-language models (LLMs) by creating *agents*. An agent is a system that can complete or delegate tasks based on LLM reasoning. You can invoke agents using chat and email, evaluate agent performance, and even call them directly from apps and workflows.

## Agent process

Agents call [tools](./tools.mdx)—for example, workflows, functions, or other agents—to gather information and complete or delegate actions. When invoked, an agent:

1. Receives a *task*, or input, as natural language. Tasks are provided as input to agents via written instructions.
2. Uses an LLM to decide whether to respond to the input, get more information, or take action in another system.
3. Provides the result of the tool call back to the LLM, and the LLM reasons in an open-ended cycle called an *agentic loop* without a pre-defined stopping point.

Agentic loop steps can occur up to the maximum number of iterations specified in Retool Agents. A *run* is a single agentic loop defined by the agent, its input, and its output.

An agent acts according to the instructions it is provided. The LLM can only make two decisions: to continue calling tools, or to provide a final answer. It can call an LLM, execute tool calls, and provide the data from the tool calls back to the LLM in a loop until the LLM decides the task provided is complete.

## Invoke an agent {#invoke}

import Invoke from '../../_partials/_invoke-agents.mdx';

## Agent architecture

The architecture of an agent consists of:
* **Orchestration layer**: Manages memory, state, reasoning, and planning.
* **Model**: The LLM that the agent utilizes.
* **Tools**: Bundled capabilities that allow an agent to perform an action (e.g., access an API, a website, etc).

Agents use frameworks like *ReAct* (Reasoning and Acting) to alternate between thinking and taking action.

## When to use agents
Agents work well for open-ended, dynamic tasks, but they are not optimal for well-structured tasks that can be encoded into discrete steps. For well-structured tasks, it may be more beneficial to use agentic workflows. The following diagram is a visual representation of the different types of GenAI and Agentic workflows that can be built using Retool Workflows, vs. the flow of a Retool Agent.

For more information about the differences between agents and agentic workflows, refer to the [Agentic Workflows](../concepts/agentic-workflows) conceptual guide.

## Why Retool Agents
Retool Agents gives builders a central place to create, test, deploy, and monitor AI agents.

Some of the key features that Retool Agents provides are:

* **Intuitive agent-building experience:**
    * Form-based UI for agent configuration.
    * Built-in chat experience.
    * Integration with existing Retool components.
* **Thoughtful tool functionality:**
    * A set of **Core tools** for out-of-the-box functionality to get your agents up and running faster (Retool Email, Retool Storage, Web Search, Code Execution, etc.).
    * Ability to create custom tools.
    * Natural language tool generation with the **Config Assistant** and **Function Generator**.
    * Ability to connect to an MCP server to pull in third-party tools.
* **An evaluation framework:**
    * Ability to test against datasets.
    * Multiple included reviewer types (exact match, LLM as judge, etc.).
    * Ability to score agent runs with an eval.
    * Ability to compare two runs side-by-side.
* **Monitoring and observability:**
    * Visibility into agent runs, token usage, and cost.
    * Visualization of agent behavior.
* **A Human-in-the-Loop (HITL) component:**
    * Consent mechanisms for tool execution.
    * Chat to test.
    * Version history and change tracking.

## Additional resources
To continue learning about agents, refer to the following resources for more information.

* [Retool Agents](https://retool.com/agents)
* [Retool Agents Pricing](../../../data-sources/concepts/models#available-platforms-and-models)
* [Retool Agents FAQ](../concepts/faq)

---

## Tools for Retool Agents

A tool is a logical block that an agent can use to complete an action, like fetching data. Each Retool Agent has at least one tool, or more commonly a set of tools, at its disposal that it can use as it runs. The agent determines which tools to use, and in what order to call them. Within Retool Agents, a tool can be a function, a workflow, or another agent.

## Add Tools
import SharedTools from '/docs/_shared/_agent-tools.mdx';

Retool Agents offers the following core tools:

| Tool name                     | Description                                                                                                                                                                                                                                                                                                         |
| ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Send Email**                | Send an email using Retool Email.                                                                                                                                                                                                                                                                                  |
| **Execute Code**              | Execute an arbitrary JavaScript or Python script. Cannot include custom libraries.                                                                                                                                                                                                                                  |
| **Get Calendar Events**       | Retrieve a list of calendar events from a specified user's Google Calendar.                                                                                                                                                                                                                                         |
| **Create Calendar Event**     | Create a new event on a user's Google Calendar.                                                                                                                                                                                                                                                                     |
| **Delete Calendar Event**     | Delete an existing event from a user's Google Calendar.                                                                                                                                                                                                                                                             |
| **Get Open Time Slots**       | Query one or more Google Calendars to find available time slots between a given time range.                                                                                                                                                                                                                         |
| **Update Calendar Event**     | Update an existing event on a user's Google Calendar. Any provided values will fully replace the existing values for those fields.                                                                                                                                                                                  |
| **Invite to Calendar Event**  | Add one or more attendees to an existing Google Calendar event.                                                                                                                                                                                                                                                     |
| **Create Google Doc**         | Create a new, empty Google Doc with a specified title.                                                                                                                                                                                                                                                              |
| **Get Google Doc**            | Retrieve the full structure and metadata of a Google Doc by its document ID.                                                                                                                                                                                                                                        |
| **Update Google Doc**         | Applies changes to an existing Google Doc using the Google Docs `batchUpdate` API. Supply the unique document ID and a JSON object or array describing the desired updates (insert text, apply styles, add images, etc.). The final state of the document is returned as determined by the provided update requests.|
| **Search Google Docs**        | Retrieve a list of files from the user's Google Drive, optionally filtered or paginated.                                                                                                                                                                                                                            |
| **Create Slides Deck**        | Creates a new Google Slides deck.                                                                                                                                                                                                                                                                                   |
| **Update Slides Deck**        | Updates a Google Slides deck by editing text, bullet points, or replacing media.                                                                                                                                                                                                                                    |
| **Get Slides Deck**           | Retrieves a Google Slides deck from the deck ID and provides metadata such as the title, owner, created and updated times, and the URL.                                                                                                                                                                             |
| **Search Slides Deck**        | Retrieves a list of Google Slides via keyword or filter.                                                                                                                                                                                                                                                            |
| **Share Slides Deck**         | Update the sharing settings a Google Slides deck (make public, change permission roles, or invite collaborators).                                                                                                                                                                                                   |
| **Get File Contents**         | Retrieve the contents of a file by its ID and return the data as a Base64-encoded string.                                                                                                                                                                                                                           |
| **Upload File**               | Upload a file to Retool Storage.                                                                                                                                                                                                                                                                                    |
| **Get File Metadata**         | Retrieve a paginated list of file metadata from Retool Storage.                                                                                                                                                                                                                                                     |
| **Rename File**               | Rename an existing file in Retool Storage.                                                                                                                                                                                                                                                                          |
| **Move File**                 | Move a file to a different folder in Retool Storage.                                                                                                                                                                                                                                                                |
| **Set File Public Access**    | Update the public accessibility of a file in Retool Storage.                                                                                                                                                                                                                                                        |
| **List Folders**              | Retrieve a paginated list of folders from Retool Storage.                                                                                                                                                                                                                                                           |
| **Create Folder**             | Create a new folder in Retool Storage.                                                                                                                                                                                                                                                                              |
| **Rename Folder**             | Rename an existing folder in Retool Storage.                                                                                                                                                                                                                                                                        |
| **Delete Folder**             | Delete a folder from Retool Storage.                                                                                                                                                                                                                                                                                |
| **List Vectors**              | Retrieve a list of vector namespaces from the Retool Vectors resource.                                                                                                                                                                                                                                              |
| **Semantic Search**           | Search for semantically similar records in a specified vector namespace using a natural language query.                                                                                                                                                                                                             |
| **Get Document Chunks**       | Retrieve the individual chunks of a document stored in a document vector namespace.                                                                                                                                                                                                                                 |
| **List URLS**                 | Retrieve a list of indexed URLs stored in a document vector namespace.                                                                                                                                                                                                                                              |
| **Get URL Chunks**            | Retrieve the individual content chunks of an indexed URL stored in a document vector namespace.                                                                                                                                                                                                                     |
| **List Documents**            | Retrieve a list of documents stored in a document vector namespace.                                                                                                                                                                                                                                                 |
| **Search Web**                | Perform a web search and retrieve a list of relevant results based on a natural language query.                                                                                                                                                                                                                     |
| **Get Webpage Content**       | Fetch the contents of a webpage and extract the main text content for analysis or processing.                                                                                                                                                                                                                       |

---

## Retool Agents concept guides



---

## Retool Agents chat

After you configure an agent, you can chat with it to test how it behaves with different inputs. You can use what you learn from your chat threads to make any necessary modifications to the agent configuration or tools. Retool recommends chatting with your agent before deploying changes to make sure the agent behaves how you expect.

:::note
An agent's chat message history is only available to the user that created the chat threads.
:::

## Agent chats
To create a new chat message, select **+ New chat** from the **Chats** tab, enter your message, and watch the agent run. The agent displays *tool chips* in the message thread that detail information about the success or failure of tools.

export function ArcadeEmbed() {
  return (
    
      
    
  )
}

If a tool requires authentication, click **Authenticate** in the **Chats** tab. Follow the prompts to **Authenticate** and **Approve** the tool. 

 Tool authentication required.

To stop an agent chat, click the **Stop** button within the agent's message window. If the agent already started using a tool, in-progress tools may continue running, but no new tasks (i.e. tools or LLM calls) will be scheduled, and the chat thread will cease to populate.

export function StopChat() {
  return (
    
      
    
  )
}

### Current tool panel
As your agent thinks, it displays the current tool it's calling in the message thread, as well as in the panel to the right of the thread. You can show or hide the tool panel depending on your view preference.

export function ArcadeEmbed_hide() {
  return (
    
      
    
  )
}

## Rename a thread
Each new chat thread is automatically named based on the message you input. To rename a chat thread, click on the **...** next to the thread name, and select **Rename thread** from the dropdown.

export function ArcadeEmbed_rename() {
  return (
    
      
    
  )
}

## Add to evals dataset

You can create a test case directly from a chat thread and add it to a previously-created dataset for [evals](../concepts/evals). Select the **...** button at the end of an agent chat thread, and click **+ Add to Eval dataset**. Fill in the required fields on the **Create Test Case** modal, and click **Create**.

export function ArcadeEmbed_eval() {
  return (
    
      
    
  )
}

## Share a thread

import Partials from '/docs/_partials/_agent-share-thread.mdx';

---

## Automatically configure an agent with the Configuration Assistant

You can use the **Configuration Assistant** to automatically write **Instructions** and create **Tools** for your agent. The **Configuration Assistant** is an LLM with knowledge of how agents works and the ability to configure them. You can choose which [AI model](/data-sources/concepts/models) you want the **Configuration Assistant** to use.

:::note

Using the **Configuration Assistant** on an agent with existing instructions or tools overwrites the existing instructions and adds new tools to the list.

:::

Use the following steps to prompt the **Configuration Assistant** to create a vacation planning agent:

1. Create a new agent or navigate to an existing agent. Open the **Configuration** tab.
2. Optionally, configure which AI model you want to use. Enter your prompt in the **Message Configuration Assistant** message field. For example:
  ```
  Create a vacation planner. When given dates, find the best place to go based on the weather that time of year. Search the internet for suggested things to do and places to stay. Email me an itinerary, and put the PTO on my calendar.
  ```
1. After the **Configuration Assistant** thinks, click **Accept** or **Reject** to respond to the proposed plan.
2. Most tools require additional configuration.
   1. Click **Select resource** on [core tools](../tools/use-core-tools) to configure it to access a particular resource. Create a new resource, or select from your existing ones.
   2. Select **Finish up** on custom [custom tools](../tools/create-custom-tools) to create the tool's function logic. You can use the **Function Generator** to provide your function with instructions

export function ArcadeEmbed() {
  return (
    
      
    
  )
}

---

## Configure Agents

import DocCardList from "@theme/DocCardList";

---

## Configure your organization for Agents

import SelfhostedLatest from "@site/src/components/SelfhostedLatest"

An organization admin must complete the steps in the following sections to make sure your organization is ready to use Agents.

:::note

To learn how to create and use agents, visit the [Agents quickstart](../../quickstart.mdx) or the [Agents tutorials](../../tutorials.mdx).

:::

## Configure organization settings

Ensure that Agents is enabled and that it has access to the proper AI models.

1. To ensure your users can access Agents, navigate to **Settings** > **AI**. Verify that the **Retool AI** and **AI Agents** settings are toggled on.
2. Navigate to **Resources** > **Retool AI**. Ensure that at least one AI provider is enabled. You can choose to use Retool-managed keys or self-managed keys.

:::note
Only AI models that support tool creation can be used with Agents. Refer to [AI providers and models](../../../data-sources/concepts/models.mdx) for supported models.
:::

## Self-hosted configuration

Complete the steps in the following sections if your deployment is self-hosted. Skip these steps if you use Retool cloud.

### Requirements

To use Agents on Self-hosted deployments, your organization must:

- Be on Self-hosted Retool version 3.234.0 or later (3.253.0 or later for stable releases).
- Have its own AI model key(s) configured.
- Meet all of the requirements outlined in the [tutorial](../../../self-hosted/tutorials.mdx) for your chosen deployment infrastructure.

### Installation

Agents is supported for the following self-hosted deployment options.

Follow the instructions outlined in the [AWS Fargate and ECS](../../../self-hosted/tutorials/ecs-fargate/cloudformation.mdx) tutorial.

Follow the instructions outlined in the tutorial corresponding to your deployment infrastructure:

- [Amazon EC2](../../../self-hosted/tutorials/ec2.mdx)
- [Azure Virtual Machines](../../../self-hosted/tutorials/azure-vm.mdx)
- [Docker](../../../self-hosted/tutorials/docker.mdx)
- [Google Compute Engine](../../../self-hosted/tutorials/gcp.mdx)

To deploy a Self-hosted Retool instance that contains agents, use the [Retool Helm chart 6.6.0](../../../self-hosted/tutorials/kubernetes/helm#1-add-the-retool-helm-chart-repository) or later.

Configure the `values.yaml` file as follows:

1. In the `image.tag` for the `tryretool/backend` and `tryretool/code-executor-service`, specify the [Docker tag](https://hub.docker.com/r/tryretool/backend/tags) for the version of Retool to install, such as tryretool/backend:.
    :::important
    Agents is available on self-hosted Retool version `3.253.0-stable` or later.
    :::

2. Set agents to `enabled: true`.

For further configuration, you can reference the full `agents:` block as defined in [retool-helm](https://github.com/tryretool/retool-helm/blob/main/values.yaml).

### Additional configuration

Agents on self-hosted deployments rely on [Temporal](../../../self-hosted/concepts/temporal). To deploy agents, you must create two additional [containers](../../../self-hosted/concepts/architecture#containers-and-services) with configuration and networking egress identical to the [workflows-worker](../../../self-hosted/concepts/architecture#workflows-worker) container:

| Container | SERVICE_TYPE Environment Variable [^env] | Networks | Dependencies | Restart Policy |
|-----------|----------------|----------|--------------|----------------|
| `agent-worker` | `WORKFLOW_TEMPORAL_WORKER` | `backend`, `code-executor` | `postgres` | `always` |
| `agent-eval-worker` | `AGENT_EVAL_TEMPORAL_WORKER` |`backend`, `code-executor` | `postgres` | `always` |

[^env]: For a complete list of Agents-related environment variables, refer to the [Environment variables reference](../../../self-hosted/reference/environment-variables/index.mdx).

import PY1 from '/docs/_partials/_agent-docker-compose-yaml.mdx';

### Set up evals 
import PY2 from '/docs/_partials/_agent-evals-self-hosted.mdx';

### Configure AI providers

After installation, navigate to `/resources/retool_ai` and configure one or more AI model providers.

## Choose a change management strategy

Agents can be versioned and protected using Retool's change management features:

- **Source Control**: Organizations on the Enterprise plan can use Source Control to [protect agents](../../../source-control/guides/protect/agents.mdx) and manage changes through Git workflows. Consider creating feature branches when making significant changes to agents.
- **Version History**: All changes to agents are automatically tracked in version history, allowing you to review and restore previous versions.

## Track usage

Organization admins can monitor Agents usage and costs.

### Monitor agent runs

Use the [Monitor](../../quickstart.mdx#monitor) dashboard to track:

- Agent run success or failure rates
- LLM token usage
- Runtime performance
- Tool invocations

### Billing and usage limits

Retool organizations receive up to $50 worth of agent runtime per month. For Self-hosted deployments using self-managed LLMs, this equals 10 hours of free usage per month at $5/hour.

If you exceed the cap, agents will not execute and you'll see a warning message. Contact your account team to discuss additional capacity.

For more information about pricing, refer to the [Model pricing](../../../data-sources/concepts/models.mdx#model-usage-and-pricing) and the [Agents billing and usage](../../../../support/billing-usage/agents) pages.

## Next steps

Once configuration is complete:

1. Create your first agent using the [Agents tutorial](../../../agents/tutorials/agents-tutorial) page.
2. Configure agent [tools](../tools/index.mdx) to connect to your data sources.
3. Set up [permissions](../../../permissions/guides/agent-permissions.mdx) to control who can create and use agents.
4. (Optional) Configure [email triggers](../triggers/trigger-with-email/) or [A2A triggers](../../concepts/a2a.mdx) for automated agent invocation.
5. For answers to common questions about Agents, review the [Agents FAQ](../../concepts/faq.mdx).

---

## Create an agent from a template

Retool Agents offers several pre-defined templates to help you create agents quickly for certain use-cases. 

To create an agent from a template, select **Templates** or **+ New Agent** in the left navigation bar, or select the **+ Agent** button to open the **Create agent** modal that contains the list of templates. 

export function ArcadeEmbed() {
  return (
    
      
    
  )
}

Once you create an agent from template, use the following steps to configure your tools:  

1. Some tools require configuration to connect a [resource](../../../data-sources/tutorials/create-resource#create-a-new-resource) (e.g., Google Docs, or Web Search). Click **Select Resource**.  
2. Use the dropdown to add a new resource or select a previously-created resource.  
3. Optionally select **Apply to all tools that need this type of resource** to apply the selected resource to all tools of the same type within the agent.  
4. Optionally select **Require user confirmation before use**, which prompts users for approval when the tool is triggered. For more information, refer to [User consent handling](../../permissions/guides/agent-permissions.mdx#user-consent-handling).  
5. Click **Save**.  

Certain templates that require [custom tools](./tools/create-custom-tools) are pre-populated with mock data (shown in the example screenshot below). You can [edit the function](./tools/create-custom-tools#edit-function) to update the data it pulls in, and modify it to fit your own business needs. 

  The Competition Scanner template showing mock competitor data from Retool Database.

---

## Run evals and compare them side-by-side

Evals allow you to:
* Test your agent's effectiveness and accuracy.
* Experiment with the output that different models provide.
* Compare two eval runs side-by-side.

For more information about evals, refer to the [Evals](../../concepts/evals) conceptual guide. 

After you've created a datset and test cases, you can create and run an eval. Running an eval of your dataset produces an **Avg score** based on whether the test cases in the dataset passed or failed. 

:::info
Eval runs count towards billable agent runtime. For more information about billing, refer to the [Billing and usage](/support/billing-usage/agents) page.
:::

## Run an eval
To run an eval of your dataset items, from a test case, use the breadcrumbs to go back to the **Datasets** tab. 

From the agent: 

1. Click the **Evals** tab. 
2. Click the **Run** button. 
3. Give the run a **Name**, check the box next to your dataset name, and click **Run eval**. 
4. When the status changes to **Completed**, your eval will produce an **Avg score**. 
5. Click on the run to view the logs for each test case. Clicking on each line item opens the **Test Case Details** panel. 

export function ArcadeEmbed_RE() {
  return (
    
      
    
  )
}

Hovering over the **Score** displays the rationale for a success or failure. In the case of a failed run, navigate back to the test case to examine or correct the expected output. 

## Compare evals
You can compare two runs side-by-side. 

1. On the **Evals** tab, check the box next to two runs and select **Compare**. 
2. If **Eval A** and **Eval B** look correct on the **Compare evals** modal, select **Compare** again. 
3. The run details are shown in side-by-side panels so you can more easily identify failures.  

export function ArcadeEmbed() {
  return (
    
      
    
  )
}

If you have done multiple eval runs, you can select other runs from the dropdowns at the top of the **Compare** page to change the information displayed.

---

## Create datasets to evaluate agent performance

Before you create an eval, you first need to add a dataset. A dataset is a collection of test cases. An agent can have many datasets, and each dataset can have many test cases. It may be beneficial to group test cases into datasets based on use-case (for instance, agent accuracy, or response time).

Within an eval, you can select one or more datasets to evaluate. 

## Create a dataset
To create a dataset, navigate to the agent you want to evaluate.

1. Click the **Datasets** tab, and then click **Add Dataset**. 
2. Provide a **Name** and optionally add a **Description** for your dataset. 
3. Click **Create**. 

export function ArcadeEmbed() {
  return (
    
      
    
  )
}

## Create a test case
*Test cases* provide input and expected output for the evaluations. 

Datasets can have many test cases, and test cases can be one of two **Types**:
* **Tool choice**: Verifies that the agent selects the expected tool, and extracts the expected parameters, based on the specified input.
* **Final answer**: Requires choosing either a **Programmatic** or **LLM as a Judge** reviewer to score the correctness of an agent's output. Use programmatic reviewers when the agent's expected output can be clearly defined (for example, Exact match), and LLM-as-a-Judge reviewers when it's not as clearly defined (for example, Tone detection).

For more information on reviewers, refer to the [Reviewers](../../concepts/evals#reviewers) section of the [Evals](../../concepts/evals) concept page. 

### Tool choice test case
To create a **Tool choice** test case:

1. Click on the dataset, and select the **Add Test Case** button.
2. Select the dataset name from the **Dataset** dropdown.
3. Enter a phrase you want to test in the **Input** field. The example below uses the **Input** phrase `What's the weather in Tokyo?`
4. Select **Tool choice** for the **Type**.
5. In the **Expected tool** dropdown, select the tool you would expect your agent to choose given the input phrase. In the following example the **Expected tool** is `Get weather`. 
6. Enter any **Expected parameters**. In the following example the parameters are the **City**, `Tokyo`, and the current **Date** in `DD-MM-YYYY` format. 
7. Click **Create**. 

export function ArcadeEmbed_TC() {
  return (
    
      
    
  )
}

### Final answer test case
To create a **Final answer** test case:

1. Click on the dataset, and select the **Add Test Case** button.
2. Select the dataset name from the **Dataset** dropdown.
3. Enter a phrase you want to test in the **Input** field.
4. Select **Final answer** for the **Type**.
5. Choose a reviewer from the **Programmatic** or **LLM-as-a-Judge** options.
6. Add the data you want to test with the reviewer (this varies based on the selected reviewer). The example below uses the `String contains` reviewer and searches for the **Substring** `access` based on the **Input** `What's my calendar look like today?`. Since the test case involves a weather agent, it would not be expected that the agent would have access to a calendar tool, and it is likely that the agent will respond with `I do not have access`. 
7. Click **Save** on the **Choose a reviewer** modal. 
8. Click **Create** to add the test case to your dataset. 

export function ArcadeEmbed_FA() {
  return (
    
      
    
  )
}

## Next steps
Once you've created a dataset and test cases, you can create an eval. Check out [Run and compare evals](../evals/compare-eval-runs) to learn how to create an eval from a dataset and compare two evals side-by-side.

---

## Evals



---

## Monitor Retool Agents

The **Monitor** page for Retool Agents has three main components: a global view showing all agents, a detailed individual agent view, and an individual agent run view.

The key benefits of the **Monitor** page are:   
* Reduce time to identify and resolve agent issues.
* Improve understanding of resource usage patterns.
* Enhance ability to optimize agent workflows.
* Improve visibility into cost drivers.
* Provide actionable insights for runtime optimization.

## All agents view 
The **Monitor** page is comprised of several sections that detail information for all agents.

  Monitoring page for all agents.

### The agent sidebar
The agent sidebar includes an expandable list of all agents, small activity graphs showing recent runs, and a search bar. 
Click on agent from the sidebar to access monitoring information for that particular agent, or expand an agent and click on a particular run to access the monitoring information for that agent run. 

Use the agents sidebar to view monitoring information for all agents, a particular agent, or an agent run.

### Time range selector
The time range selector at the top of the page shows runs and errors across all agents for the selected timeframe when hovering over the graph. 

 The time range selector allows you to view the run and error history of all agents or an individual agent.

* The **Live** button turns on real-time live events. If the end of the time-range includes the present, activating **Live** mode pulls in live events, so that you can see agents and tool calls in real-time. 
* The dropdown selection below the **Live** button contains the following timeline view options: **Past 15 minutes**, **Past 1 hour**, **Past 1 day**, **Past 1 week**, **Past two weeks**, **Past 1 month**, or a **Custom** range. 
* Selecting the  or  buttons allows you to scroll through the timeline history based for the time range you've selected. For example, if you select **Past 1 week**, you can go backward or forward in the history a week at a time. 

### Visualizations
The **Agent graph** shows agent-resource interactions in real-time. Agents are displayed as primary nodes, and tools are shown as secondary nodes. Active connections are shown as animated lines during tool execution.

Hover over or click on an agent in the **Agent graph** to display its activity in the **Activity graph**.

 The **Agent graph** and **Activity graph** provide visual representations of agent resource connections and tool calls.

### Usage and cost information
To help track resource consumption across all agents, the **Token usage**, the **Estimated cost**, the **Total runtime** (primary cost metric), and the **Total runs** are displayed beneath the **Agent graph** and **Activity graph**. 

 The usage, cost, and count metrics are displayed underneath the visualizations.

**Agent tool usage**, **Agent total runtime**, and **Agent run count** show global runtime statistics. 

## Individual agent view 
To get to the individual agent view, click on the name of the agent in the agent sidebar. Each agent displays a **Last status** indicator (`SUCCESS`, `MANUALLY_CANCELED`, `FAILURE`, `TIMED_OUT`), a **Last started** timestamp, and a **Last run duration** for the last agent run that occurred. 

  Monitoring page for a single agent.

Each individual agent has the same monitoring dashboard as the all agents view, with the exception of the **Agent total runtime** and **Agent run count** global runtime statistics. 

## Agent run view
Individual agent runs are displayed when expanding the dropdown to the left of the agent name in the agent sidebar. Each individual agent run is represented by the date and time it occurred.

  Monitoring page for a single agent run.

Clicking on the run displays the **Run ID** for that particular agent run, as well as the **Status** (`SUCCESS`, `MANUALLY_CANCELED`, `FAILURE`, `TIMED_OUT`), **Started** timestamp , and **Run duration**. 

The usage and cost information, **Token usage**, **Estimated cost**, **Total runtime**, and **Tool calls**, is shown above the replay of the agent run, and the **Current activity** visual that contains the tool chips.

---

## Connect an MCP server to an agent

import MCP from '/docs/_partials/_agents-mcp.mdx';

An MCP server dynamically provides a list of tools. It is not a one-time static import from the server into Retool. The tool list is fetched whenever Retool connects to the MCP server.

MCP Server is a resource type configured with a URL, but you cannot write queries directly to these resources.

**Key features:**
* Add an MCP server to an agent.
* An agent can retrieve and select tools.
* Agents can invoke one or more of those tools.

## Supported authentication
Retool supports the following authentication options for MCP resources:
* [OAuth 2.0](../../../data-sources/guides/authentication/google-oauth) - Supports multiple flows including:
  * Authorization Code (default with PKCE)
  * Client Credentials
  * Authorization Code with PKCE
* [Basic Auth](../../../data-sources/concepts/authentication#basic) - Username and password authentication
* Bearer Token - Token-based authentication using an API key or access token

## Supported protocol versions
Protocol versions provide a framework to connect MCP resources to clients (such as IDEs, chat interfaces, or other software).

Retool currently supports the following protocol versions:

```JavaScript
export const LATEST_PROTOCOL_VERSION = "2025-06-18";
export const DEFAULT_NEGOTIATED_PROTOCOL_VERSION = "2025-03-26";
export const SUPPORTED_PROTOCOL_VERSIONS = [
  LATEST_PROTOCOL_VERSION,
  "2025-03-26",
  "2024-11-05",
  "2024-10-07",
  ];
```

## Limitations

Retool's MCP implementation has the following limitations:

### Transport protocols
* Retool only supports remote-hosted MCP servers accessible via HTTP/HTTPS. 
* Retool supports the following transport types: Streamable HTTP and Server-Sent Events (SSE).
* Local `stdio`-based MCP servers are not directly supported and must be exposed via an HTTP gateway (see instructions below).

### Timeouts
* The tool execution timeout is up to 2 minutes maximum.
* The tool listing timeout is 5 seconds.

### Protocol version support
* Supported protocol versions are determined by the MCP TypeScript SDK version (currently v1.17.5). 
* Retool automatically negotiates the protocol version with the MCP server during connection. 

## Connect to an MCP server

Retool Agents can connect to open MCP servers via Streamable HTTP or SSE, as well as private MCP servers using basic authentication with a username and password, bearer token authentication, or OAuth 2.0 with a client ID and client secret.

Many MCP servers are built to run locally over `stdio`. These can be exposed to Retool via an HTTP gateway.

:::note
To discover MCP servers that you can connect to, refer to the [official MCP registry](https://blog.modelcontextprotocol.io/posts/2025-09-08-mcp-registry-preview/).
:::

The following sample instructions explain how to use [supergateway](https://github.com/supercorp-ai/supergateway) and [ngrok](https://ngrok.com/) to create a server URL:

First, use supergateway to start an MCP server that is accessible at `http://localhost:8000/sse`.

```bash
npx -y supergateway --stdio "npx " --cors
```

For example, the [Brave Search MCP Server](https://github.com/modelcontextprotocol/servers/tree/main/src/brave-search) can be run via the command:

```bash
BRAVE_API_KEY=... npx -y supergateway --stdio "npx
@modelcontextprotocol/server-brave-search" --cors
```

Next, use ngrok to tunnel your local MCP server to the internet. Generate a strong password to use for authentication.

```bash
ngrok http --basic-auth 'mylongusername:mylongpassword' 8000
```

ngrok provides a temporary URL similar to the following: `https://xyz-123.ngrok-free.app`, which is the server's base URL. ngrok also supports [static domains](https://ngrok.com/blog-post/free-static-domains-ngrok-users).

If you are using supergateway, your MCP server URL will be `https://xyz-123.ngrok-free.app/sse`, which you can then paste into Retool.

## Connect an MCP server to an agent

To connect an MCP server to an agent:

1. Open an existing agent or [create a new one](../../tutorials.mdx).
2. On the **Configuration** tab, click **Add new tool**.
3. Click **Connect to MCP Server**.
4. Select a previously created resource from the **Select MCP Server** dropdown, or choose **Add new resource**.
5. Give the new resource a **Name**.
6. Paste in the URL of your MCP server in the **Server URL** field, and optionally add your authentication credentials.
7. Click **Create resource**.

export function ArcadeEmbed() {
  return (
    
      
    
  )
}

If your MCP server requires authentication, click **Authenticate MCP Server** from the **Connect to MCP server** page, the **Configuration** tab, or click **Authenticate** in the **Chats** tab. Follow the prompts to **Authenticate** and **Approve** the tool.

 Tool authentication required.

The tools exposed by the MCP server will be available as tools on your Retool Agent.

---

## Create a custom tool

import Connectsvg from "/img/icon-packs/bold/interface-geometric-circle-alternate.svg";

Create a custom tool to provide your agent with reusable, complex logic with strongly-typed parameters. Custom tools are especially useful if you have a specific need not covered by one of the [core tools](./use-core-tools.mdx) or other types of tools you can create or import. Custom tools use a canvas of pre-built query blocks that you connect together to interact with resources, transform data, and run additional logic.

Custom tools are very similar to the [multi-step functions](../../../workflows/guides/functions.mdx) that are available in Retool Workflows.

## Features

Key features of custom tools for agents include:

- Strongly-typed parameters.
- A **Function Generator** chat interface that can automatically create a custom function.
- The flexibility and power of workflows without the cost associated with workflow runs.
- An independent control flow.
- Support for all available [block types](../../../workflows/guides/blocks/blocks.mdx).

:::note

Custom tools run on behalf of the currently authenticated user, which is reflected in the `current_user` object.

:::

## Add a custom tool

The instructions in this section explain how to create a custom tool using an example that gets [Retool changelog](/changelog/) entries between a start and end date. This example is also used as part of the [Retool Agents tutorial](../../tutorials/agents-tutorial.mdx#create-a-custom-tool).

1. Create a new agent or navigate to an existing agent. Open the **Configuration** tab.
2. In the **Tools** section, click the **+ Add Tool** button.
3. Click **Create new custom tool**. A tool configuration page opens.
4. In the **Name** setting, enter `fetchFromChangelog`
5. In the **Description** setting, enter `Searches for and filters changelog entries by user-provided date range.`
6. Click **Add parameter**  twice. Click  to update each parameter, and configure them with the following settings:
    | Name          | Type         | Description                                                             | Required |
    |---------------|--------------|------------------------------------------------------------------------ | ---------|
    | `startDate`   | String       | The start date (inclusive) for filtering entries, in YYYY-MM-DD format. | Yes      |
    | `endDate`     | String       | The end date (inclusive) for filtering entries, in YYYY-MM-DD format.   | Yes      |
7. Select **Edit function** in the **Function Logic** section.

### Edit function

You have two options for creating function logic: you can manually assemble blocks to create your desired control flow, or you can use the **Function Generator**, to create a function automatically using your choice of LLM.

To manually assemble blocks that retrieve changelog entries and filter them by date, complete the following steps:

1. Delete the `code1` block. Click and drag  from the Parameters block. Create a  **Resource query** block with the **REST API** resource type to `GET` the JSON feed for the Changelog: `https://docs.retool.com/changelog/feed.json`. Name it `getChangelogEntries`.
2. From `getChangelogEntries`, click and drag  to add a  **Code** block. Replace the default code with the following JavaScript:
    ```javascript
    return getChangelogEntries.data.items
    ```
3. From the  **Code** block, click and drag  to add a **Filter** block with the following **Expression** to use the `moment()` date library to match any posts within the specified range:
    ```json
    moment(value.date_modified).isBetween(moment(params.startDate), moment(params.endDate))
    ```
4. All functions must have a **Response** block. From the **Filter** block, click and drag  to add a **Response** block with a **Status code** of `200` and the following **Return body**:
    ```json
    filter1.data
    ```
5. Click **Done**.
6. On the **Edit Tool** page, click **Save**.

export function ArcadeEmbed() {
  return (
    
      
    
  )
}

Provide the **Function Generator** instructions about what actions you want your function to perform:

1. Paste the following command into the **Function Generator**:
    ```
    Write a function that retrieves the changelog entries from https://docs.retool.com/changelog/feed.json. Return only the items with a date_modified between or on the startDate and endDate.
    ```
2. The **Function Generator** provides a plan that outlines the function it will create. Review the plan, and click ** Accept**.
3. Once the canvas updates with your new function, provide example start and end dates in the **params** block and click **Test Function**.
    :::important
    Depending on the model and prompt you use, you may have varying results and need to troubleshoot the code that's generated.
    :::
4. Click **Done**.
5. On the **Edit Tool** page, click **Save**.

:::note

You can also optionally @ tag any previously-created [resources](../../../data-sources/index.mdx) to indicate that you want to include a [Resource query block](../../../workflows/guides/blocks/resource-query.mdx) that accesses that resource.

:::

## Billing for custom tools

While the functions used by custom tools are functionally similar to workflows, they are billed differently. The time that your custom tool takes to run contributes to the overall billable time for your agent. Refer to the [Billing and usage](/support/billing-usage/agents) guide for more information.

---

## Import tools from another agent

When configuring an agent, you can import tools from another agent. This functionality is useful for reusing complex custom tools without the need to recreate them.

Importing a tool creates a deep copy. You can make changes to the imported tool without impacting the tool's usage in the original agent.

:::note

Users don't need `edit` access to an agent in order to import a tool from it.

:::

Complete the following steps to import a tool from another agent:

1. Create a new agent or navigate to an existing agent. Open the **Configuration** tab.
2. In the **Tools** section, click the **+ Add Tool** button.
3. Select the **Import from other agent** button.
4. From the dropdown, search for or select the agent that you want to import a tool from.
5. From the list of **Available Tools**, select the tools that you want to import.
6. Click **Import 1 Selected**. The number updates to reflect how many tools you are importing.
7. In the **Configuration** tab, you can choose to make edits to your newly imported tool. Any edits to this tool are reflected only in this agent, and the tool in the original agent remains unchanged.

export function ArcadeEmbed() {
  return (
    
      
    
  )
}

---

## Tools



---

## Invoke an agent as a tool

You can configure your agent to use another agent as a [tool](../../concepts/tools.mdx). This is useful for situations in which you have one agent that performs a narrowly-scoped task, and you want another agent that to reuse that capability.

:::note

Using an agent as a tool does not copy the agent—it creates a new trigger for the existing agent. Any changes that you make to an agent that is referenced as a tool persist in all uses of that agent.

:::

For example, a marketing team wants to create an agent that helps them with providing discount codes based on the item catalogue and inventory. If the sales team already has an agent that evaluates whether to send discount emails to customers, the marketing team can use that agent as a tool.

## Features

Key features of agents as tools include:

- Minimize duplicate work by leveraging existing agents.
- Outsource certain decision-making to a more specialized agent.

## Configure an agent as a tool

Complete the following steps to configure an agent as a tool:

1. Create a new agent or navigate to an existing agent. Open the **Configuration** tab.
2. In the **Tools** section, click the **+ Add Tool** button.
3. Select **Use Agent**.
4. On the **Use Agent** page, select the agent that you want to use, or choose to create a new one.
5. The **Name** setting auto-populates with the name of the agent. Rename the tool if you choose, and add a description.
6. Click **Save**.

export function ArcadeEmbed() {
  return (
    
      
    
  )
}

---

## Retool Agents core tools

There are several pre-built **Core tools** that Retool provides with Retool Agents so you can get started building faster. Every Retool organization will be seeded with resources for core tools.

Core tools for Retool Agents.

import SharedCoreTools from '/docs/_shared/_agent-core-tools.mdx';

## Add core tools to an agent
To add a core tool to an agent, sign in to your Retool organization, and select the **Agents** tab in the top navigation bar.

1. Open an existing agent or [create a new one](../../tutorials.mdx).
4. On the **Configuration** tab, click **Add new tool**.
5. Click the **+** on the **Core tools** you want to add, and select **Add tools**.

The agent **Configuration** tab displays the tools you added in the **Tools** section.

export function ArcadeEmbed() {
  return (
    
      
    
  )
}

## Selecting resources
Once you add a tool to your agent, a `Select Resource` dropdown appears. Use this dropdown to choose from existing resources in your organization, or create a new one. To use an existing resource, you need **Edit** permissions for that resource.

If you create a new resource, you have options to share credentials between users and apply the resource you create to all tools that need that type of resource.

export function ArcadeEmbed_SR() {
  return (
    
      
    
  )
}

## Convert a core tool to a custom tool
When you edit a core tool, Retool Agents automatically converts it to a [custom tool](./create-custom-tools), and you can edit the **Function Logic**. You will be prompted with a warning message when converting a core tool to a custom tool. This is a check to ensure you're making an intended change. Converting a core tool to a custom tool doesn't change the way the tool functions until you modify it.

1. Click  to expand the core tool, and click the **Edit** () icon.
2. Click **Continue** on the warning message that pops up.
3. Select **Edit function** in the **Function Logic** section.
4. Update the tool as desired and click **Done**.
5. Click **Save** on the **Edit tool** page to save the updates to your new custom tool.

export function ArcadeEmbed_CT() {
  return (
    
      
    
  )
}

---

## Trigger a workflow as a tool

You can configure your agent to use a workflow as a [tool](../../concepts/tools.mdx). This is useful for users that already have a workflow that they want to leverage.

:::note

Using a workflow as a tool does not copy the workflow—it creates a new trigger for the workflow. Any changes that you make to a workflow that is referenced as a tool persist in all uses of that workflow.

:::

For example, a marketing team wants to create an agent that helps them with providing discount codes based on the item catalogue and inventory. If the team already has a workflow that retrieves the catalogue of clothing, they can use that workflow as a tool for your agent without needing to create a [custom tool](./create-custom-tools.mdx) or new workflow.

## Features

Key features of workflows as tools include:

- Minimize duplicate work by leveraging existing workflows.
- Share a single workflow between several agents.
- Use the [workflow limits](../../../workflows/concepts/limits.mdx), which are longer than those of custom tools.

:::note

When a workflow is called by an agent, Retool triggers the workflow as if it was triggered by the first user with admin permissions in your organization. If the workflow requires access to user-authenticated resource, the workflow is run as if it was triggered by the authenticated user.

:::

## Requirements

Your workflow must meet the following requirements be used in an agent:

- It must be [published](../../../workflows/guides/version-and-publish.mdx).
- It must include a [response block](../../../workflows/guides/blocks/response.mdx).
- It must not have [path parameters or headers](../../../workflows/guides/triggers/webhooks.mdx#).

## Configure a workflow as a tool

Complete the following steps to add a workflow as a tool:

1. Create a new agent or navigate to an existing agent. Open the **Configuration** tab.
2. In the **Tools** section, click the **+ Add Tool** button.
3. Click **Use workflow**.
4. On the **Use workflow** page, select the workflow that you want to use, or create a new one.
5. Enter a **Name** and **Description** for your workflow.
6. If your workflow uses JSON parameters in the trigger block, add corresponding parameters to the tool configuration. Each parameter must have the same **Name** and **Type** as what is used in the workflow. Mark parameters as **Required** if they are necessary for the workflow run.
7. Click **Save** to finalize your changes.

export function ArcadeEmbed() {
  return (
    
      
    
  )
}

## Billing for workflows as tools

Your organization is billed for a workflow run every time an agent calls a workflow as a tool. The time it takes for your workflow to run also counts towards the billable time for your agent. Refer to the [Billing and usage](/support/billing-usage/agents) guide for more information.

:::note

To avoid being billed for a workflow run, consider using a [custom tool](./create-custom-tools.mdx) instead.

:::

---

## Triggers



---

## Set up an A2A trigger

To allow external agents to communicate with Retool agents, you can enable the A2A trigger on your agent's configuration page, and copy the endpoint and API key into your A2A client. An API key is provided for each agent, and must be passed to the client via an `X-Api-Key` header. API keys can be rotated. 

The process to use the Agent-to-agent (A2A) protocol with agents is similar to creating a [webhook trigger in workflows](../../../workflows/guides/webhooks). 

The A2A client then sends messages via common messaging formats like the HTTP+REST or JSON-RPC APIs, and Server-Sent Events (SSE) for long-running streaming updates. 

:::info
Prior to enabling A2A, you must have all the necessary permissions for **Retool AI** and **AI Agents** turned on for your organization. Refer to [Permissions for Agents](../../../permissions/guides/agent-permissions#admin-capabilities.mdx) for more information. 
:::

## Enable the A2A trigger

On your agent's **Configuration** page: 

1. Click the **A2A** trigger. 
2. Toggle the **A2A** switch on. 
3. You can either copy and paste the **Endpoint** and the **API key** separately into your A2A client, or you can click **Copy** next to **Endpoint** and select **Copy as cURL** from the dropdown. This copies a cURL command preformatted with your agent's information and API key to your clipboard. 

The default endpoint provided is the [Get agent card](../../reference/a2a-endpoints.mdx#get-agent-card) endpoint.

export function ArcadeEmbed() {
  return (
    
      
    
  )
}

You can [cURL to test the agent response](../../reference/a2a-endpoints) to different endpoints, or [trigger your agent via an external agent](../../tutorials/a2a-tutorial.mdx). 

## Additional resources
To continue learning about A2A, explore the following resources:

* [Agent-to-agent communication](../../concepts/a2a.mdx)
* [A2A tutorial](../../tutorials/a2a-tutorial.mdx)
* [What is A2A Protocol?](https://a2a-protocol.org/dev/)
* [A2A endpoints](../../reference/a2a-endpoints.mdx)

---

## Trigger agents with email

:::note
Before getting started, reach out to your account manager or support so that they can configure your organization's settings to allow **Email** triggers for agents.
:::

**Email** triggers enable you, or anyone else that has access to the unique agent's email address, to send an email with a user message that triggers your agent. Treat the agent's email address as sensitive information.

:::warning

If the sender's email address matches that of a Retool user with `use` access to the agent, `current_user` reflects that user and the agent can only use tools to which the user has access.

If the sender's email address does _not_ match any Retool user with appropriate permissions, the agent falls back to an organization admin. **Retool strongly recommends that you only allow known Retool users with access to the agent to use email triggers.**

Refer to the `current_user` [reference](../../../reference/current-user) and the [Manage permissions](../../../../../permissions/guides/agent-permissions) page for more information.

:::

The following changes occur when setting up an email trigger:

- The **Trigger** section contains an email address that you can use to trigger your agent.
- An additional set of **Instructions** is added to tell your agent how to respond to and send emails.
- A **Reply to Email** tool is added to the list of **Tools**.

To set up an email trigger for an agent:

1. Create a new agent or navigate to an existing agent. Open the **Configuration** tab.
2. In the **Triggers** section, click **+ Email**.
3. Copy the email address and send your agent an email.

:::note
You can CC other recipients when sending an email to trigger the agent. Recipients will then also receive the agent's response.
:::

export function ArcadeEmbed() {
  return (
    
      
    
  )
}

---

## Configure email triggers for Agents using Mailgun

Self-hosted organizations can implement [email triggers](./) for Agents using [Mailgun](https://www.mailgun.com/), a paid, third-party mail service. 

:::warning

If your Self-hosted environment is behind a VPN, and you want to trigger agents via email, make sure your `api/agents/emailWebhook` endpoint is accessible by Mailgun. Refer to [Mailgun's Webhooks documentation](https://documentation.mailgun.com/docs/mailgun/user-manual/events/webhooks) for the domain that will need access to your webhook.

:::

Setting up a Mailgun account and enabling the environment variables using the process described below allows you to send an email to your agent, which Mailgun forwards to your Self-hosted deployment as a webhook event, which then triggers the agent to run.  

## Requirements

To implement email triggers, you need:

- A verified [Mailgun](https://www.mailgun.com) account.
- A custom domain to use with Mailgun.
- Ingress for Mailgun to send webhook notifications. 
- A webhook endpoint URL reachable by Mailgun. Tools such as [ngrok](https://ngrok.com) can be used to accomplish this. 

## Set up Mailgun

Complete the following steps to configure Mailgun:

1. Add a new custom domain and verify it. Do not use the sandbox domain Mailgun creates automatically upon login. 
2. Create a new [Receiving Route](https://help.mailgun.com/hc/en-us/articles/360011355893-Routes) and toggle on **Store and notify** and enable **Message Retention**. Retool uses Mailgun's `Messages` API to look up the message, so it is important to enable **Store and Notify** and **Message Retention**, otherwise, the following error message will display: `{"message":"Message retrieval disabled for domain"}`. Message retention durations are limited by the Mailgun plan selected. 

    
    Create a new Receiving Route in Mailgun.
     

3. Create an endpoint URL and update any firewall configuration so that it is reachable by Mailgun. You can use tools such as [Ngrok](https://ngrok.com/) to create a URL. For example: `https://.ngrok-free.app/api/agents/emailWebhook`.  
4. In Mailgun, add a new [Webhook](https://documentation.mailgun.com/docs/mailgun/user-manual/events/webhooks) and provide the webhook URL you created for your agent.  

        
    Provide your webhook endpoint URL.
     

          
    The Webhooks page of the Mailgun dashboard.
     

## Environment variables
Set the following environment variables within your deployment's configuration file.

- `MAILGUN_WEBHOOK_API_KEY` - The **HTTP webhook signing key** found in the **Webhooks** section of your Mailgun dashboard. 
- `MAILGUN_API_KEY` - The **Mailgun API key** found in the **API Security** section of your Mailgun dashboard. Refer to [Mailgun's help page](https://help.mailgun.com/hc/en-us/articles/203380100-Where-can-I-find-my-API-keys-and-SMTP-credentials) for more information.
- `AGENT_EMAIL_DOMAIN` - The domain of your email address. For example, if your email is `johndoe@retool.com` then your email domain is `retool.com`.  

## Set up a custom tool for email
The **Send Email** and **Reply to Email** tools are not supported for Self-hosted deployments. To send email or reply to an email on a Self-hosted deployment, create a [custom tool](../../tools/create-custom-tools) to connect to an agent with an email resource like SMTP or Twilio.

---

## Retool Agents how-to guides



---

## Retool Agents documentation

Retool Agents is a platform that lets you encode a business process, connect to external systems of records, make deterministic decisions with code, make non-deterministic decisions with LLMs, include humans in decision making, and take actions by writing data, calling into other systems, or responding directly to customers.

---

## Retool Agents quickstart

import Image from '@site/src/components/Image';

Retool Agents is a product that allows you to build AI agents for various use cases, test, deploy, evaluate, and monitor them within Retool.

This guide serves as an introduction to [Retool Agents](../agents/concepts/overview.mdx). It covers many of the concepts and terminology you will come across as you build agents. After reading this page, you should have a good understanding of the fundamentals for building Retool Agents.

## Page structure
Retool Agents displays three primary pages within the left-hand navigation bar:
* [Home](#all-agents)
* [Monitor](#monitor)
* [Templates](#templates)

Under **Templates** is **+ New Agent**, which opens the [Create agent](#create-agent) modal. And the **Recents** section lists any agents you've recently created or viewed.

Within an agent, the following tabs are displayed:
* [Chats](#chats)
* [Configuration](#configuration)
* [Logs](#logs)
* [Evals](#evals)
* [Datasets](#datasets-and-test-cases)

### All agents
Clicking on the **Agents** tab in the top navigation bar displays the **All agents** page. You can get back to the **All agents** page by clicking **Home** in the left-hand navigation toolbar from other pages within Retool Agents.

The **All agents** page displays all agents, and folders containing agents, for your Retool organization. You can **Sort**, **Filter**, or **Search for agents**, toggle between **Grid view** or **Table view**, **Refresh** the agents list, create a **New Folder** or create a **New Agent**.

  The all agents home page shows folders and agents for your organization.

### Monitor
The **Monitor** page consists of a comprehensive monitoring interface for all Retool Agents.

  Monitoring page for all agents.

Clicking on a specific agent provides a detailed monitoring view of that agent, including an activity graph with a visual representation of your agent's configuration, token usage, cost, as well as agent run and resource metrics.

  Monitoring page for a single agent.

 You can also further drill down on the information displayed in the monitoring page by expanding the dropdown next to an agent to view a specific agent run.

  Monitoring page for a single agent run.

### Templates
Templates to build agents for specific use cases (such as a Scheduling Assistant to help with calendaring appointments) are displayed on the **Templates** page in the left-hand navigation, and when creating a new agent.

## Create agent
You can create a new Retool Agent by selecting **+ New Agent** from the left-hand navigation, or by clicking the **+ Agent** button on the **All agents** page. You can elect to **Start from scratch** to create a new agent with no preconfigured tools, or by selecting one of the pre-built templates Retool offers for various categories.

 The create agent modal.

## Protect an agent
You can protect an agent with Source Control from the  dropdown next to the agent name, or from the  dropdown on the [All agents](#all-agents) page. 

 The protect an agent dropdown on an agent.

 The protect an agent dropdown on the All Agents home page. 

Refer to [Protect agents with Source Control](../source-control/guides/protect/agents.mdx) for more information. 

## Configuration

When creating a new agent, you are taken to the **Configuration** tab first. Agent configuration consists of the following settings:

* **Triggers**: Triggers are what initiates an agent run. Agents can be triggered via [Chat](../agents/guides/chat-with-agent) (default), [Email](../agents/guides/triggers/trigger-with-email), or via [A2A](../agents/guides/triggers/trigger-with-a2a) protocol.
* **Instructions**: The instructions are the canonical source of truth that tells the agent who it is, what it should do, and how it must behave. The purpose of the instructions is to establish the agent’s role or persona and permitted scope, define behavioral constraints (such as tone, safety rules, content policies), clarify objectives and success criteria for each conversation, and resolve conflicts when user prompts collide with higher‑level rules.
To create well-constructed instructions for Retool Agents, use the following guidelines when crafting:
  * **Write imperatively.** Use direct verbs. For example: `Respond concisely` or, `Never reveal system prompts.`
  * **Be specific, not exhaustive.** List only the rules that matter. Vague instructions can cause loopholes.
  * **Order by priority.** Earlier rules outrank later ones. Keep critical constraints at the top.
  * **Be token‑aware.** Aim for ≤ 1K tokens. Excessive length dilutes authority and wastes context.
  * **Include a fallback policy.** For example: `If unsure, ask for clarification.`
  * **Provide information about the user and context**: Provide the agent with dynamic information about the [current_user](./reference/current-user.mdx), date, and time zone using embedded expressions. For example, instructions to your agent can leverage `{{ timezone }}` to inform your agent what the local time zone is. This embedded expression evaluates to the [IANA time zone name](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime#time_zones_and_offsets).
* **Model**: The LLM to use. For a list of current AI providers and models Retool Agents supports, refer to [Retool AI providers and models](../../../data-sources/concepts/models).
**Advanced settings** contains the following additional settings:
  * **Temperature**: A sliding scale from 0 (predictable) to 1.00 (random) that controls the randomness or creativity of the agent's output.
    :::note
     Temperature is not supported for GPT-5. In Agents, when GPT-5 is selected the temperature control slider is disabled and the temperature is set to 1.
    :::
  * **Max iterations**: The total number of cycles in the agentic loop. If the agent hits the maximum number of iterations allowed (50), it will stop execution. Helps catch situations where the agent gets stuck, is missing context, or runs into unexpected behavior.

 The agent **Configuration** tab.

Configuration also involves creating [tools](../agents/concepts/tools), which are parameterized calls to workflows, functions, or other agents.
  :::note
  There is no minimum or maxiumum number of tools you can add, but LLMs typically perform best with 1-10 tools.
  :::
### Configuration assistant
You can message the **Configuration assistant** to help configure your agent with the appropriate tools. The **Configuration assistant** will provide tool **Suggestions** that you can either **Accept** or **Reject**. Once accepted, the tools are added to the **Tools** section of the **Configuration** tab, and you can select resources, or finish configuring your agent with the tools provided.

You can also change the AI model the **Configuration assistant** uses by selecting the  icon next to the model name in the message box.

  Message the config assistant for help configuring tools for your agent.

### Add tools

import SharedTools from '/docs/_shared/_agent-tools.mdx';

## Deploy an agent
To save a specific agent configuration, create a new version of your agent with the **Deploy** feature. The **Deploy** dropdown modal is available at the top of every page within the agent. Select the **Release type** (Major, Minor, or Patch), provide an optional **Description of changes**, and **Deploy** your agent. If you have made modifications to your agent but have not yet deployed those changes `Undeployed changes` is displayed next to the **Deploy** dropdown.

## Version history
Agent change history is saved automatically in the **Agent version** dropdown. When you update an agent, the version history dropdown displays **Modified**, and when you **Deploy** your agent, the major, minor, and patch version history is displayed.

  The version history is displayed next to the agent name at the top of every agent page.

## Invoke an agent

import Invoke from '../_partials/_invoke-agents.mdx';

## Chats
The **Chat** tab allows you to chat with an agent and view the LLM's tool call runs and reasoning so that you can test your agent as you build.

The `message` and `previousMessages` input parameters from the chat are automatically passed to the agent, so you do not need to configure or specify these inputs.

### Share thread

import Partials from '/docs/_partials/_agent-share-thread.mdx';

## Logs
The **Logs** tab displays a detailed list view of the steps the agent took while running. The log details include:

* **Timestamp**: The timezone is stored in UTC and shown in your local timezone.
* **Type**: The start or end of the agent, the LLM, or the tool.
* **Status**: The status of the step. The status listed is either `Completed`, `Running`, `Queued`, or `Failed`. A status may show `Queued` if a tool requires approval or authorization before it can be used.
* **Tool**: The tool the agent called (if applicable to the step).
* **Thoughts**: The agent's reasoning.
* **Input**: The message input.
* **Run**: The `run ID` for that step.

  Agent logs display detailed information about the agent runs.

## Evals

import SharedEvals from '/docs/_shared/_agent-evals.mdx';

---

## A2A endpoints

You can use the following Agent-to-agent (A2A) API endpoints with agents. For more detailed information about A2A, refer to the [Agent-to-agent communication](../concepts/a2a.mdx) conceptual guide.

| Name                                                                | Description                                                                   | Endpoint                                        |
| --------------------------------------------------------------------| ------------------------------------------------------------------------------|-------------------------------------------------|
|  [Get agent card](./a2a-endpoints#get-agent-card)                   | View an agent's information through the agent card.                           | `GET /a2a/:agentId/.well-known/agent-card.json` |
|  [Send message](./a2a-endpoints#send-message)                       | Sends a message to an agent.                                                  | `POST /a2a/:agentId/v1/message:send`            |
|  [Get task](./a2a-endpoints#get-task)                               | Polls an agent to view the output of a specific task.                         | `GET /a2a/:agentId/v1/tasks/:taskId`            |
|  [Send streaming messages](./a2a-endpoints#send-streaming-messages) | Sends a message to an agent and subscribes to real-time updates via SSE.      | `POST /a2a/:agentId/v1/message:stream`          |
|  [Subscribe to task](./a2a-endpoints#subscribe-to-task)             | Streams updates from an agent for an existing task.                           | `POST /a2a/:agentId/v1/tasks/:taskId:subscribe` |
|  [Cancel task](./a2a-endpoints#cancel-task)                         | Requests the cancellation of an ongoing task running on an agent.             | `POST /a2a/:agentId/v1/tasks/:taskId:cancel`    |

### Get agent card
The [agent card](https://a2a-protocol.org/dev/specification/#8-agent-discovery-the-agent-card) describes the agent's identity, capabilities, skills, and interaction requirements.

```curl
curl --url "https://yourdomain.retool.com/api/agents/a2a/agent_id/.well-known/agent-card.json" \
  -H "Content-Type: application/json" \
  -H "X-Api-Key: retool_wk_########"
```

```json
{
    "name":"A2A test",
    "preferredTransport":"HTTP+JSON",
    "description":"",
    "url":"https://yourdomain.retool.com/api/agents/a2a/agent_id",
    "provider":
        {"organization":"youradmin@retool.com",
        "url":"https://yourdomain.retool.com"
    },
    "protocolVersion":"0.3.0",
    "version":"0.0.1",
    "capabilities":{
        "streaming":true,
        "pushNotifications":false
    },
    "defaultInputModes":[
        "application/json"
    ],
    "defaultOutputModes":[
        "application/json"
    ],
    "skills":[

    ]
}"%"
```

### Send message
A [send message](https://a2a-protocol.org/dev/specification/#311-send-message) request is the primary way to initiate agent interactions. Clients can send a message to an agent and receive a task in response.

:::info
The `taskId` in A2A is the `runId` within Retool. When you send a message, for example, via `POST /a2a/:agentId/v1/message:send` the response is an [A2A Task object](https://a2a-protocol.org/dev/specification/#311-send-message) with a top-level ID field of `taskId`.
:::

```curl
curl -X POST \
  "https://yourdomain.retool.com/api/agents/a2a/agent_id/v1/message:send" \
  -H "Content-Type: application/json" \
  -H "X-Api-Key: retool_wk_#####" \
  -d '{
    "message": {
      "messageId": "msg-12345",
      "role": "user",
      "parts": [
        {
          "kind": "text",
          "text": "tell me a joke"
        }
      ]
    }
  }'
```

```json
{
    "id":"054c93a8-b0ed-49ea-915d-418e615cb7b4",
    "contextId":"23a617f6-f7a7-40a8-955c-f17faad5c954",
    "status":{
        "state":"submitted",
        "message":{
            "role":"agent",
            "parts":[
                {"kind":"text",
                 "text":"Task submitted"
                 }
            ],
            "messageId":"status-054c93a8-b0ed-49ea-915d-418e615cb7b4-1764717334957",
            "taskId":"054c93a8-b0ed-49ea-915d-418e615cb7b4",
            "contextId":"23a617f6-f7a7-40a8-955c-f17faad5c954",
            "kind":"message"
        }
    },
    "kind":"task",
    "history":[
        {
            "role":"user",
            "parts":[
                {
                    "kind":"text",
                    "text":"tell me a joke"
                }
            ],
            "messageId":"dd992a34-b2bc-4f4f-b0c1-0e7399da87d2",
            "contextId":"23a617f6-f7a7-40a8-955c-f17faad5c954",
            "taskId":"054c93a8-b0ed-49ea-915d-418e615cb7b4",
            "kind":"message"
        }
    ]
}
```

When testing this endpoint with Retool, you can view the POST in the [log output](../quickstart.mdx#logs), the [monitoring page](../quickstart.mdx#monitor), or you can make use of the get task or streaming endpoints.

### Get task
[Get task](https://a2a-protocol.org/dev/specification/#313-get-task) retrieves the current state (including status, artifacts, and optionally history) of a previously initiated task. This is typically used for polling the status of a task initiated with [message:send](../guides/triggers/trigger-with-a2a.mdx#send-message), or for fetching the final state of a task.

```curl
curl --url "https://yourdomain.retool.com/api/agents/a2a/agent_id/v1/tasks/task_id" \
  -H 'Content-Type: application/json' \
  -H 'X-Api-Key: retool_wk_######'
```

```json
{
    "id":"054c93a8-b0ed-49ea-915d-418e615cb7b4",
    "contextId":"23a617f6-f7a7-40a8-955c-f17faad5c954",
    "status":{
        "state":"completed",
        "message":{
            "role":"agent",
            "parts":[
                {
                    "kind":"text",
                    "text":"Why don't skeletons fight each other? \n\nBecause they don't have the guts!"
                }
            ],
            "messageId":"8dc8c544-f6d9-49a8-b9e3-cecf2433f23d",
            "contextId":"23a617f6-f7a7-40a8-955c-f17faad5c954",
            "taskId":"054c93a8-b0ed-49ea-915d-418e615cb7b4",
            "kind":"message"
        }
    },
    "kind":"task",
    "history":[
        {
            "role":"user",
            "parts":[
                {
                    "kind":"text",
                    "text":"tell me a joke"
                }
            ],
            "messageId":"dd992a34-b2bc-4f4f-b0c1-0e7399da87d2",
            "contextId":"23a617f6-f7a7-40a8-955c-f17faad5c954",
            "taskId":"054c93a8-b0ed-49ea-915d-418e615cb7b4",
            "kind":"message"
        },
        {
            "role":"agent",
            "parts":[
                {
                    "kind":"text",
                    "text":"Why don't skeletons fight each other? \n\nBecause they don't have the guts!"
                }
            ],
            "messageId":"8dc8c544-f6d9-49a8-b9e3-cecf2433f23d",
            "contextId":"23a617f6-f7a7-40a8-955c-f17faad5c954",
            "taskId":"054c93a8-b0ed-49ea-915d-418e615cb7b4",
            "kind":"message"
        }
    ]
}"%"
```

### Send streaming messages
[Send streaming messages](https://a2a-protocol.org/dev/specification/#312-send-streaming-message) is similar to [message:send](../guides/triggers/trigger-with-a2a.mdx#send-message), but it provides real-time streaming of updates during processing.

```curl
 curl -X POST \
  "https://yourdomain.retool.com/api/agents/a2a/agent_id/v1/message:stream" \
  -H "Content-Type: application/json" \
  -H "X-Api-Key: retool_wk_your_api_key" \
  -d '{
    "message": {
      "messageId": "msg-123456",
      "role": "user","parts": [
        {
          "kind": "text",
          "text": "tell me a joke"
        }
      ]
    }
  }'
```

```json
"event":"task
data":{
   "task":{
      "id":"7c185114-3ba7-4742-80d1-1e03af262ffa",
      "contextId":"85ba1598-f940-436f-a964-1bed22b94930",
      "status":{
         "state":"submitted",
         "message":{
            "role":"agent",
            "parts":[
               {
                  "kind":"text",
                  "text":"Task submitted"
               }
            ],
            "messageId":"status-7c185114-3ba7-4742-80d1-1e03af262ffa-1764963458316",
            "taskId":"7c185114-3ba7-4742-80d1-1e03af262ffa",
            "contextId":"85ba1598-f940-436f-a964-1bed22b94930",
            "kind":"message"
         }
      },
      "kind":"task",
      "history":[
         {
            "role":"user",
            "parts":[
               {
                  "kind":"text",
                  "text":"tell me a joke"
               }
            ],
            "messageId":"d61ae646-96d7-45a9-806d-ecac05a2d649",
            "contextId":"85ba1598-f940-436f-a964-1bed22b94930",
            "taskId":"7c185114-3ba7-4742-80d1-1e03af262ffa",
            "kind":"message"
         }
      ]
   }
}"event":"statusUpdate
data":{
   "statusUpdate":{
      "kind":"status-update",
      "taskId":"7c185114-3ba7-4742-80d1-1e03af262ffa",
      "contextId":"85ba1598-f940-436f-a964-1bed22b94930",
      "status":{
         "state":"working",
         "message":{
            "role":"agent",
            "parts":[
               {
                  "kind":"text",
                  "text":"Agent run started, thinking"
               }
            ],
            "messageId":"status-7c185114-3ba7-4742-80d1-1e03af262ffa-1764963461322",
            "taskId":"7c185114-3ba7-4742-80d1-1e03af262ffa",
            "contextId":"85ba1598-f940-436f-a964-1bed22b94930",
            "kind":"message"
         }
      },
      "final":false
   }
}"event":"statusUpdate
data":{
   "statusUpdate":{
      "kind":"status-update",
      "taskId":"7c185114-3ba7-4742-80d1-1e03af262ffa",
      "contextId":"85ba1598-f940-436f-a964-1bed22b94930",
      "status":{
         "state":"working",
         "message":{
            "role":"agent",
            "parts":[
               {
                  "kind":"text",
                  "text":"Why don't skeletons fight each other?\n\nBecause they don't have the guts!"
               }
            ],
            "messageId":"bed394ef-cd1c-46e9-b469-755620575236",
            "contextId":"85ba1598-f940-436f-a964-1bed22b94930",
            "taskId":"7c185114-3ba7-4742-80d1-1e03af262ffa",
            "kind":"message"
         }
      },
      "final":false
   }
}"event":"statusUpdate
data":{
   "statusUpdate":{
      "kind":"status-update",
      "taskId":"7c185114-3ba7-4742-80d1-1e03af262ffa",
      "contextId":"85ba1598-f940-436f-a964-1bed22b94930",
      "status":{
         "state":"completed",
         "message":{
            "role":"agent",
            "parts":[
               {
                  "kind":"text",
                  "text":"Why don't skeletons fight each other?\n\nBecause they don't have the guts!"
               }
            ],
            "messageId":"bed394ef-cd1c-46e9-b469-755620575236",
            "contextId":"85ba1598-f940-436f-a964-1bed22b94930",
            "taskId":"7c185114-3ba7-4742-80d1-1e03af262ffa",
            "kind":"message"
         }
      },
      "final":true
   }
}
```

### Subscribe to task
[Subscribe to Task](https://a2a-protocol.org/dev/specification/#316-subscribe-to-task) establishes a streaming connection to receive updates for an existing task.

```curl
 curl -X POST \
  "https://yourdomain.retool.com/api/agents/a2a/agent_id/v1/tasks/task_id:subscribe" \
  -H 'Content-Type: application/json' \
  -H 'X-Api-Key: retool_wk_#####'
```

```json
{
   "kind":"status-update",
   "taskId":"054c93a8-b0ed-49ea-915d-418e615cb7b4",
   "contextId":"23a617f6-f7a7-40a8-955c-f17faad5c954",
   "status":{
      "state":"completed",
      "message":{
         "role":"agent",
         "parts":[
            {
               "kind":"text",
               "text":"Why don't skeletons fight each other? \n\nBecause they don't have the guts!"
            }
         ],
         "messageId":"8dc8c544-f6d9-49a8-b9e3-cecf2433f23d",
         "contextId":"23a617f6-f7a7-40a8-955c-f17faad5c954",
         "taskId":"054c93a8-b0ed-49ea-915d-418e615cb7b4",
         "kind":"message"
      }
   },
   "final":true
}"%"
```

### Cancel task
[Cancel task](https://a2a-protocol.org/dev/specification/#315-cancel-task) requests the cancellation of an ongoing task. The server will attempt to cancel the task, but success is not guaranteed (e.g., the task might have already completed or failed, or cancellation might not be supported at its current stage).

```curl
 curl -X POST \
  "https://yourdomain.retool.com/api/agents/a2a/agent_id/v1/tasks/task_id:cancel" \
  -H 'Content-Type: application/json' \
  -H 'X-Api-Key: retool_wk_#####'
```

```json
{
   "success":false,
   "message":"workflow execution already completed"
}"%"
```

---

## The Current User object

The `current_user` object can be populated with the details of an admin user or the currently authenticated user. The assignment of the `current_user` object depends on how the agent is invoked. In general, the `current_user` is assigned to the currently authenticated user. However, when you trigger an agent from a workflow, use a workflow as a tool, or trigger an agent by an unknown email, the `current_user` is populated as an admin user.

Refer to the following table for more information about who is assigned to the `current_user` role:

| Trigger                          | Description                                                                                             | Assigned to `current_user`          |
| -------------------------------- | ------------------------------------------------------------------------------------------------------- | ----------------------------------- |
| Agent as tool                    | Triggered by another [agent as a tool](../guides/tools/use-agent-as-tool.mdx).                          | The `current_user` of the triggering agent.               |
| Agent block in custom tool       | Triggered by another agent as a block in a [custom tool](../guides/tools/create-custom-tools.mdx).      | The `current_user` of the triggering agent.              |
| App                              | Triggered by the [Agent Chat](../../apps/reference/components/special-inputs/agent-chat.mdx) component. | The `current_user` of the app. [More information](../../apps/reference/objects/current-user.mdx).      |
| Workflow                         | Triggered by an [Agent block](../../workflows/guides/blocks/agent/index.mdx) in a workflow.                   | The `current_user` of the workflow, which is an admin user. [More information](../../workflows/reference/objects/current-user.mdx). |
| Chat                             | Triggered by the [Chat tab](../quickstart.mdx#chats) in Retool Agents.                                  | The user of the chat.   |
| Email trigger from known user    | Triggered by an email from a Retool user with access to the agent.                                                    | The Retool user who sent the email. |
| Email trigger from unknown email | Triggered by an email that cannot be matched with a Retool user who has access to the agent.            | An admin user.               |

Reference the following examples:
- A workflow uses the Invoke Agent block to call an agent named Vacation Plan, and Vacation Plan calls another agent named Get Weather. The `current_user` of the workflow is an admin user. Therefore, the `current_user` objects of Vacation Plan and Get Weather are both also populated with an admin user.
- An agent called Marketing Assistant is called using the Agent Chat app component. Marketing Assistant calls another agent named Discount Decider. The `current_user` of Marketing Assistant and Discount Decider are both populated with the app's authenticated user.

## Properties

---

## Agents glossary

import SharedGlossary from '../../_partials/_glossary.mdx';

---

## Retool Agents reference



---

## A2A tutorial

To trigger a Retool agent from an external agent via the Agent-to-agent (A2A) protocol, ensure your agent has the necessary configuration and tools to work with your external agent. 

This tutorial walks you through the process to stand up an A2A-compliant agent with [Crew AI](https://docs.crewai.com/en/learn/a2a-agent-delegation) and use A2A to trigger a web research agent.

## 1. Create a Retool agent
Your agent will need a web browsing or URL fetching tool. You can follow the instructions in the [Agents tutorial](../tutorials/agents-tutorial.mdx), or use the instructions below to create a minimal agent example. 
1. Select the **Agents** tab in the top navigation bar.
2. From the **All agents** page, click the **+ Agent** button.
3. Select **Start from scratch** and click **Create**.
4. In the **Agent name** field, enter `A2A test`.
5. In the **Description** field, enter `Retool A2A web research assistant` and click **Create**.
6. On the **Agent Configuration** page, click **Add Tool** and add the **Search Web** and **Get Webpage Content** tools.

:::note
You may be required to set up a resource for web searching if you do not already have one created. Refer to the [Connect to Tavily Web Search](../../data-sources/guides/connect/tavily) guide for detailed instructions.
:::

## 2. Turn on A2A
On your agent's **Configuration** page:
1. Click the **A2A** trigger. 
2. Toggle the **A2A** switch on. 

You will need the **Endpoint** and **API key** for the next step. Refer to the [Set up an A2A trigger](../guides/triggers/trigger-with-a2a#enable-the-a2a-trigger) guide for more information. 

## 3. Create an external agent
The example below uses Crew AI with A2A to create a web research assistant that delegates web research to the Retool agent.

To set up Crew AI, from your terminal or a command line: 
1. [Optional] Run `brew install python@3.12`. 
    :::note 
    You may need to use Homebrew to install a [Python version compatible with Crew AI](https://docs.crewai.com/en/installation) if you are running an older version of Python. This tutorial uses Python 3.12 as an example. If you are already running a version between 3.10 and the current version, you can skip this step. 
    :::
2. Create your virtual environment:  
    ```bash
    python3.12 -m venv venv
    source venv/bin/activate
    ```
3. Install and set up version 1.6.1 of Crew AI. You will need to create an OpenAI API key if you do not have one already. 
    ```bash
    pip install "crewai[a2a]==1.6.1"
    export OPENAI_API_KEY="sk-your-key"
    ```
4. Using your preferred text editor or IDE, create a new Python file. Name the file `my_script.py` and copy and paste in the below code.

    ```python title="my_script.py"

    import sys

    from crewai import Agent, Crew, Task
    from crewai.a2a import A2AConfig
    from crewai.a2a.auth import APIKeyAuth

    # =============================================================================
    # CONFIGURATION - Replace these with your Retool agent details
    # =============================================================================

    RETOOL_AGENT_ENDPOINT = "https://your-org.retool.com/api/agents/a2a/YOUR-AGENT-UUID/.well-known/agent-card.json"
    RETOOL_API_KEY = "retool_wk_your_api_key_here"

    # =============================================================================

    def create_crew(topic: str) -> Crew:
        """Create a simple web research crew."""

        research_coordinator = Agent(
            role="Research Coordinator",
            goal="Gather comprehensive information on topics by delegating web research tasks",
            backstory="""You are an expert research coordinator. You excel at breaking down 
            research questions and delegating web browsing tasks to gather information. 
            You synthesize findings into clear, actionable summaries.""",
            llm="gpt-4o",
            verbose=True,
            a2a=A2AConfig(
                endpoint=RETOOL_AGENT_ENDPOINT,
                timeout=300,
                max_turns=10,
                trust_remote_completion_status=True,
                auth=APIKeyAuth(
                    api_key=RETOOL_API_KEY,
                    location="header",
                    name="X-API-Key",
                ),
            ),
        )

        research_task = Task(
            description=f"""Research the following topic thoroughly: {topic}
            
            Use your web browsing capabilities to:
            1. Find recent and relevant information about this topic
            2. Identify key facts, trends, and insights
            3. Note any important sources or references
            
            Provide a comprehensive summary of your findings.""",
            expected_output="""A detailed research summary including:
            - Key findings and facts
            - Recent trends or developments
            - Important sources referenced
            - Actionable insights""",
            agent=research_coordinator,
        )

        return Crew(
            agents=[research_coordinator],
            tasks=[research_task],
            verbose=True,
        )

    def main():
        # Get topic from command line or use default
        topic = (
            " ".join(sys.argv[1:])
            if len(sys.argv) > 1
            else "latest developments in AI agents"
        )

        print(f"\n{'='*60}")
        print("Web Research Assistant")
        print(f"{'='*60}")
        print(f"Topic: {topic}\n")

        crew = create_crew(topic)
        result = crew.kickoff()

        print(f"\n{'='*60}")
        print("RESEARCH COMPLETE")
        print(f"{'='*60}")
        print(result.raw)

    if __name__ == "__main__":
        main()
    ```
5. Replace the `RETOOL_AGENT_ENDPOINT` and `RETOOL_API_KEY` with the values provided in your A2A trigger (in [Step 2](./a2a-tutorial#2-turn-on-a2a)) and save the file. 

## 4. Trigger your agent with the external agent
From your terminal, run `python my_script.py "What are the top 5 AI models in 2025"` to view the external agent interacting with your Retool agent. 

```python
((venv) )  yourname@Retool  ~/repo/folder  python my_script.py "What are the top AI models of 2025"

============================================================
Web Research Assistant
============================================================
Topic: What are the top AI models of 2025

╭──────────────────────────────────────────────────────────────────────────────────── Crew Execution Started ─────────────────────────────────────────────────────────────────────────────────────╮
│                                                                                                                                                                                                 │
│  Crew Execution Started                                                                                                                                                                         │
│  Name: crew                                                                                                                                                                                     │
│  ID: id_number                                                                                                                                                       │
│  Tool Args:                                                                                                                                                                                     │
│                                                                                                                                                                                                 │
│                                                                                                                                                                                                 │
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯

🚀 Crew: crew
└── 📋 Task: task_id_number
    Status: Executing Task...
╭─────────────────────────────────────────────────────────────────────────────────────── 🤖 Agent Started ────────────────────────────────────────────────────────────────────────────────────────╮
│                                                                                                                                                                                                 │
│  Agent: Research Coordinator                                                                                                                                                                    │
│                                                                                                                                                                                                 │
│  Task: Research the following topic thoroughly: What are the top AI models of 2025                                                                                                              │
│                                                                                                                                                                                                 │
│          Use your web browsing capabilities to:                                                                                                                                                 │
│          1. Find recent and relevant information about this topic                                                                                                                               │
│          2. Identify key facts, trends, and insights                                                                                                                                            │
│          3. Note any important sources or references                                                                                                                                            │
│                                                                                                                                                                                                 │
│          Provide a comprehensive summary of your findings.                                                                                                                                      │
│                                                                                                                                                                                                 │
│  IMPORTANT: You have the ability to delegate this task to remote A2A agents.                                                                                                                    │
│                                                                                                                                                                                                 │
│                                                                                                                                                                                                 │
│                                                                                                                                                                           │
│                                                                                                                                                                                                 │
│  {                                                                                                                                                                                              │
│    "description": "Retool A2A web research assistant",                                                                                                                                          │
│    "skills": [                                                                                                                                                                                  │
│      {                                                                                                                                                                                          │
│        "description": "Perform a web search and retrieve a list of relevant results based on a natural language query.",                                                                        │
│        "id": "tool_id_number",                                                                                                                                            │
│        "name": "Search Web",                                                                                                                                                                    │
│        "tags": []                                                                                                                                                                               │
│      },                                                                                                                                                                                         │
│      {                                                                                                                                                                                          │
│        "description": "Fetch the contents of a webpage and extract the main text content for analysis or processing.",                                                                          │
│        "id": "tool_id_number",                                                                                                                                            │
│        "name": "Get Webpage Content",                                                                                                                                                           │
│        "tags": []                                                                                                                                                                               │
│      }                                                                                                                                                                                          │
│    ],                                                                                                                                                                                           │
│    "url": "https://your-org.retool.com/api/agents/a2a/YOUR-AGENT-UUID/jsonrpc"                                                                                            │
│  }                                                                                                                                                                                              │
│                                                                                                                                                                                                 │
│                                                                                                                                                                          │
│                                                                                                                                                                                                 │
│                                                                                                                                                                                                 │
│                                                                                                                                                                      │
│                                                                                                                                                                                                 │
│                                                                                                                                                                     │
│                                                                                                                                                                                                 │
│                                                                                                                                                                                                 │
│                                                                                                                                                                                                 │
│                                                                                                                                                                                                 │
│                                                                                                                                                                                                 │
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯

🚀 Crew: crew
└── 📋 Task: task_id_number
    Status: Executing Task...
    └── 🔗 Delegating to A2A Agent (https://your-org.retool.com/api/agents/a2a/YOUR-AGENT-UUID/.well-known/agent-card.json)
╭─────────────────────────────────────────────────────────────────────────────────────── 🔗 A2A Delegation ───────────────────────────────────────────────────────────────────────────────────────╮
│                                                                                                                                                                                                 │
│  A2A Delegation Started                                                                                                                                                                         │
│                                                                                                                                                                                                 │
│  Agent ID: https://your-org.retool.com/api/agents/a2a/YOUR-AGENT-UUID/.well-known/agent-card.json                                                                         │
│  Endpoint: https://your-org.retool.com/api/agents/a2a/YOUR-AGENT-UUID/.well-known/agent-card.json                                                                         │
│                                                                                                                                                                                                 │
│  Task Description:                                                                                                                                                                              │
│  Please perform a web search to find recent and relevant information about the top AI models of 2025. I'm interested in identifying key facts, trends, insights, and important sources related  │
│  to this...                                                                                                                                                                                     │
│                                                                                                                                                                                                 │
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯

🚀 Crew: crew
└── 📋 Task: task_id_number
    Status: Executing Task...
    ├── 🔗 Delegating to A2A Agent (https://your-org.retool.com/api/agents/a2a/YOUR-AGENT-UUID/.well-known/agent-card.json)
    └── 💬 Multiturn A2A Conversation (https://your-org.retool.com/api/agents/a2a/YOUR-AGENT-UUID/.well-known/agent-card.json)
        └── 💬 Turn 1
            ├── user 👤 :   "Please perform a web search to find recent and relevant information about the top AI models of 2025...."
            └── A2A test 🤖:  ✓ "Here are the most recent and relevant sources about the top AI models of 2025, along with key facts,..."
╭──────────────────────────────────────────────────────────────────────────────────────── ✅ A2A Success ─────────────────────────────────────────────────────────────────────────────────────────╮
│                                                                                                                                                                                                 │
│  A2A Delegation Completed                                                                                                                                                                       │
│                                                                                                                                                                                                 │
│  Result:                                                                                                                                                                                        │
│  Here are the most recent and relevant sources about the top AI models of 2025, along with key facts, trends, and insights:                                                                     │
│                                                                                                                                                                                                 │
│  - [Top AI Models Ranked (November 2025) – Synergy Online](https://www.synergyonline.com/post/top-ai-models-ranked-november-2025-the-executive-perspective):                                    │
│    - Highlights ChatGPT-5, Claude 4.5, Perplexity AI, Gemini 2.5, DeepSeek, and Grok 4 as leading models.                                                                                       │
│    - Discusses how organizations blend models for creativity, compliance, real-time insights, and operational flo...                                                                            │
│                                                                                                                                                                                                 │
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
🚀 Crew: crew
└── 📋 Task: task_id_number
    Assigned to: Research Coordinator
    Status: ✅ Completed
    ├── 🔗 Delegating to A2A Agent (https://your-org.retool.com/api/agents/a2a/YOUR-AGENT-UUID/.well-known/agent-card.json)
    └── 💬 Multiturn A2A Conversation (https://your-org.retool.com/api/agents/a2a/YOUR-AGENT-UUID/.well-known/agent-card.json)
        └── 💬 Turn 1
            ├── user 👤 :   "Please perform a web search to find recent and relevant information about the top AI models of 2025...."
            └── A2A test 🤖:  ✓ "Here are the most recent and relevant sources about the top AI models of 2025, along with key facts,..."
╭──────────────────────────────────────────────────────────────────────────────────────── Task Completion ────────────────────────────────────────────────────────────────────────────────────────╮
│                                                                                                                                                                                                 │
│  Task Completed                                                                                                                                                                                 │
│  Name: task_id_number                                                                                                                                                     │
│  Agent: Research Coordinator                                                                                                                                                                    │
│  Tool Args:                                                                                                                                                                                     │
│                                                                                                                                                                                                 │
│                                                                                                                                                                                                 │
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯

============================================================
RESEARCH COMPLETE
============================================================
Here are the most recent and relevant sources about the top AI models of 2025, along with key facts, trends, and insights:

- [Top AI Models Ranked (November 2025) – Synergy Online](https://www.synergyonline.com/post/top-ai-models-ranked-november-2025-the-executive-perspective):
  - Highlights ChatGPT-5, Claude 4.5, Perplexity AI, Gemini 2.5, DeepSeek, and Grok 4 as leading models.
  - Discusses how organizations blend models for creativity, compliance, real-time insights, and operational flow.

- [10 Most Popular AI Models of 2025 – Orca Security](https://orca.security/resources/blog/most-popular-ai-models-2025/):
  - Lists the 10 most widely adopted AI models in cloud environments, noting a sharp increase in organizational AI adoption (from 56% in 2024 to 84% in 2025).
  - OpenAI models are particularly dominant in cloud usage.

- [The Best AI Models for Your Business (March 2025) – FlexOS](https://www.flexos.work/learn/best-ai-models):
  - Focuses on business integration, with Gemini Pro and Copilot enhancing Google Workspace and Microsoft 365.
  - Emphasizes custom AI workflows and team collaboration.

- [Top 9 Large Language Models as of December 2025 – Shakudo](https://www.shakudo.io/blog/top-9-large-language-models):
  - Provides a detailed ranking and analysis of the top large language models, including use cases across industries.
  - Offers white papers and case studies for deeper insights.

- [LLM Leaderboard 2025 – Vellum AI](https://www.vellum.ai/llm-leaderboard):
  - Features benchmark results for Claude Sonnet 4.5, Grok 4, and other leading models, with data on adaptability and performance across tasks.

- [YouTube: I Tried Every AI So You Don't Have To — Here are the Best of 2025](https://www.youtube.com/watch?v=-AJoByRGkgU):
  - Video review comparing ChatGPT, Claude, Gemini, and Grok, highlighting their strengths and weaknesses in real-world scenarios.

- [Reddit: Which AI model has the freshest, most reliable knowledge?](https://www.reddit.com/r/AgentsOfAI/comments/1mqxhhj/which_ai_model_has_the_freshest_most_reliable/):
  - Community discussion on knowledge cutoffs and real-time capabilities, with mentions of Claude Opus 4.1 and web search integration.

**Trends & Insights:**
- ChatGPT-5, Claude 4.5, Gemini 2.5, Perplexity AI, and Grok 4 are consistently named as leading models in 2025.
- Organizations are increasingly combining multiple models for specialized tasks (reasoning, real-time data, creativity).
- AI adoption in business and cloud environments is rapidly increasing, with OpenAI models leading in popularity.
- Customization, real-time knowledge, and seamless integration into business tools are key differentiators among top models.

These sources provide a comprehensive overview of the current AI model landscape, including rankings, business applications, and technical benchmarks.
╭─────────────────────────────────────────────────────────────────────────────────── Trace Batch Finalization ────────────────────────────────────────────────────────────────────────────────────╮
│ ✅ Trace batch finalized with session ID: id_number                                                                                                                  │
│                                                                                                                                                                                                 │
│ 🔗 View here: https://app.crewai.com/crewai_plus/ephemeral_trace_batches/id_number?access_code=TRACE-trace_id                                                      │
│ 🔑 Access Code: id_number                                                                                                                                                               │
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
╭──────────────────────────────────────────────────────────────────────────────────────── Crew Completion ────────────────────────────────────────────────────────────────────────────────────────╮
│                                                                                                                                                                                                 │
│  Crew Execution Completed                                                                                                                                                                       │
│  Name: crew                                                                                                                                                                                     │
│  ID: id_number                                                                                                                                                       │
│  Tool Args:                                                                                                                                                                                     │
│  Final Output: Here are the most recent and relevant sources about the top AI models of 2025, along with key facts, trends, and insights:                                                       │
│                                                                                                                                                                                                 │
│  - [Top AI Models Ranked (November 2025) – Synergy Online](https://www.synergyonline.com/post/top-ai-models-ranked-november-2025-the-executive-perspective):                                    │
│    - Highlights ChatGPT-5, Claude 4.5, Perplexity AI, Gemini 2.5, DeepSeek, and Grok 4 as leading models.                                                                                       │
│    - Discusses how organizations blend models for creativity, compliance, real-time insights, and operational flow.                                                                             │
│                                                                                                                                                                                                 │
│  - [10 Most Popular AI Models of 2025 – Orca Security](https://orca.security/resources/blog/most-popular-ai-models-2025/):                                                                      │
│    - Lists the 10 most widely adopted AI models in cloud environments, noting a sharp increase in organizational AI adoption (from 56% in 2024 to 84% in 2025).                                 │
│    - OpenAI models are particularly dominant in cloud usage.                                                                                                                                    │
│                                                                                                                                                                                                 │
│  - [The Best AI Models for Your Business (March 2025) – FlexOS](https://www.flexos.work/learn/best-ai-models):                                                                                  │
│    - Focuses on business integration, with Gemini Pro and Copilot enhancing Google Workspace and Microsoft 365.                                                                                 │
│    - Emphasizes custom AI workflows and team collaboration.                                                                                                                                     │
│                                                                                                                                                                                                 │
│  - [Top 9 Large Language Models as of December 2025 – Shakudo](https://www.shakudo.io/blog/top-9-large-language-models):                                                                        │
│    - Provides a detailed ranking and analysis of the top large language models, including use cases across industries.                                                                          │
│    - Offers white papers and case studies for deeper insights.                                                                                                                                  │
│                                                                                                                                                                                                 │
│  - [LLM Leaderboard 2025 – Vellum AI](https://www.vellum.ai/llm-leaderboard):                                                                                                                   │
│    - Features benchmark results for Claude Sonnet 4.5, Grok 4, and other leading models, with data on adaptability and performance across tasks.                                                │
│                                                                                                                                                                                                 │
│  - [YouTube: I Tried Every AI So You Don't Have To — Here are the Best of 2025](https://www.youtube.com/watch?v=-AJoByRGkgU):                                                                   │
│    - Video review comparing ChatGPT, Claude, Gemini, and Grok, highlighting their strengths and weaknesses in real-world scenarios.                                                             │
│                                                                                                                                                                                                 │
│  - [Reddit: Which AI model has the freshest, most reliable knowledge?](https://www.reddit.com/r/AgentsOfAI/comments/1mqxhhj/which_ai_model_has_the_freshest_most_reliable/):                    │
│    - Community discussion on knowledge cutoffs and real-time capabilities, with mentions of Claude Opus 4.1 and web search integration.                                                         │
│                                                                                                                                                                                                 │
│  **Trends & Insights:**                                                                                                                                                                         │
│  - ChatGPT-5, Claude 4.5, Gemini 2.5, Perplexity AI, and Grok 4 are consistently named as leading models in 2025.                                                                               │
│  - Organizations are increasingly combining multiple models for specialized tasks (reasoning, real-time data, creativity).                                                                      │
│  - AI adoption in business and cloud environments is rapidly increasing, with OpenAI models leading in popularity.                                                                              │
│  - Customization, real-time knowledge, and seamless integration into business tools are key differentiators among top models.                                                                   │
│                                                                                                                                                                                                 │
│  These sources provide a comprehensive overview of the current AI model landscape, including rankings, business applications, and technical benchmarks.                                         │
│                                                                                                                                                                                                 │
│                                                                                                                                                                                                 │
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────
```

The output from the LLM and tool calls will be displayed in your agent's [log output](../quickstart.mdx#logs), or the [monitoring page](../quickstart.mdx#monitor). 

  A2A-triggered agent run

## Wrap up
You should now be able to call an agent in Retool from an external agent using A2A. 

To continue learning about A2A, explore the following resources:

* [Agent-to-agent communication](../concepts/a2a.mdx)
* [What is A2A Protocol?](https://a2a-protocol.org/dev/)
* [Set up an A2A trigger](../guides/triggers/trigger-with-a2a.mdx)
* [A2A endpoints](../reference/a2a-endpoints.mdx)

---

## Retool Agents tutorial

import Connectsvg from "/img/icon-packs/bold/interface-geometric-circle-alternate.svg";

[Retool Agents](../concepts/overview) enable you to create AI agents that can think, reason, and call tools independently. They take actions and complete or delegate tasks that you assign them during configuration.

This tutorial walks you through the process for building an agent that aggregates new Retool changelog posts and release notes for a specified date range and sends you a digest email.

## 1. Create a new agent
To create a new agent, sign in to your Retool organization, and select the **Agents** tab in the top navigation bar.

1. From the **All agents** page, click the **+ Agent** button.
2. Select **Start from scratch** and click **Create**.
3. In the **Agent name** field, enter `Retool Digest`.
4. In the **Description** field, enter `Retool release notes and changelog updates` and click **Create**.

export function ArcadeEmbed_CN() {
  return (
    
      
    
  )
}

## 2. Configure your agent

The **Configuration** tab is where you input the **Instructions** your agent uses to help the AI **Model** you select think and reason as it calls tools.

:::note
When creating a new agent, you can pass references to [current_user](../reference/current-user.mdx), `{{ timezone }}`, and `{{ new Date() }}` into the instructions using embedded expressions. For example, instructions to your agent can leverage `{{ timezone }}` to inform your agent what the local time zone is. This embedded expression evaluates to the [IANA time zone name](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime#time_zones_and_offsets).
:::

For this tutorial, replace the default text in the **Instructions** field with the following:

```
You are an assistant that provides Retool updates. Summarize all of the new release notes and changelog entries at Retool for a given date range and send an email.

1. Collect and summarize:
- Release notes
- Changelog entries

2. Format the email digest as follows:
The subject line: Retool Updates

The Date Range provided by the user.

The body of the email should contain the following sections:
📦 New Releases
- Provide a bulleted list of updates for Edge and Stable Release Notes and include direct links to the full content.
- Links for Edge Release Notes should link to this page: https://docs.retool.com/releases/edge/all
- Links for Stable Release Notes should link to this page: https://docs.retool.com/releases/stable/all
- Each bulleted list item should have a title, a summary of the release notes, and the date the release notes were published.
- If there are no release notes for Edge or Stable for a given week, say `No new items this week`.

📝 Changelog
- Provide a bulleted list of changelog entries, and include direct links to the full content.
- Each bulleted list item should have a title, a summary of changelog item, and the date of the changelog.
- If there are no changelog entries for a given week, say `No new items this week`.

3. Always include:
- Links to the Release Notes: https://docs.retool.com/releases and the Changelog: https://docs.retool.com/changelog at the bottom of every email

4. Always send an email to {{ current_user.email }}

Keep summaries concise and highlight breaking changes or major features.
```
Retool's [Best Practices](../concepts/best-practices) page offers additional guidance about creating clear instructions for your agent to follow.

In the **Model** setting, select the AI model you want to use. For this tutorial, use the Retool-managed Anthropic model: `Claude 3.7 Sonnet`. Results may differ if you select a different model.

You must have [Retool AI](../../../queries/concepts/actions) enabled, and the **Retool-managed Anthropic key** must be enabled by an admin of your organization for Anthropic models to appear in the model selection dropdown.

:::note
You can interact with [AI models](../../data-sources/concepts/models.mdx) using either a Retool-managed key or with your own credentials. Retool recommends using Retool-managed keys for testing and development purposes.

:::

## 3. Connect tools to your agent
There are several kinds of tools that Retool Agents can consume and use. For this tutorial, you're going to add prebuilt tools, and create custom tools.

For more information about tools, refer to the [Tools](../concepts/tools) conceptual guide.

:::note
You can also chat with the **Config Assistant**, which helps you configure your agent by suggesting tools and automatically populating your configuration with them if you accept the suggestion. Depending on the AI model used, you may have varying results, and automatically generated tools may need to be refined.
:::

### Add core tools
Core tools are created by Retool to provide you out-of-the-box tool functionality for common tasks, like updating a Google Calendar or Executing Code.

To add core tools:
1. Click **Add Tool** and click the **+** on the **Send Email** tool and the **Get Webpage Content** tool. A green check mark will appear to notify you that the tools have been selected.
2. Click the **Add 2 tools** button. The number changes depending on how many tools are selected.
3. The **Send Email** and **Get Webpage Content** tools are displayed in the **Tools** section of the **Configuration** tab.

export function ArcadeEmbed_CT() {
  return (
    
      
    
  )
}

### Convert core tool to custom tool
When you edit a core tool, Retool Agents automatically converts it to a custom tool, and you can edit the **Function Logic**. You will be prompted with a warning message when converting a core tool.

1. Click  to expand the **Get Webpage Content** tool, and click the **Edit** () icon.
2. Click **Continue** on the warning message that pops up.
3. Select **Edit function** in the **Function Logic** section.
4. In the  block labeled `getContent`, replace `{{ params.url }}` with the Edge Release Notes text-only URL: `https://docs.retool.com/releases/edge/all`.
5. Add another  block in between `getContent` and `parseHTML`. Choose the **REST API** resource query type, and `GET` the Stable Release Notes text-only URL: https://docs.retool.com/releases/stable/all.
6. Replace the JavaScript in the `parseHTML` block with the following:
    ```javascript
    function cleanHtmlWithHeaderLinks(rawHtml) {
    // 1. Remove , , ,  and their content
    let html = rawHtml
        .replace(//gi, "")
        .replace(//gi, "");

    // 2. Find headers and replace with markdown links
    // Matches Bar or Bar
    html = html.replace(/]*)>([\s\S]*?)/gi, (m, level, attrs, content) => {
        // Try to extract id attribute
        let idMatch = attrs.match(/\sid=["']?([^\s"'>]+)["']?/i);
        let id = idMatch ? idMatch[1] : null;
        // If no id, generate from content
        let text = content.replace(/]+>/g, '').trim();
        let anchor = id || text.toLowerCase().replace(/[^\w\- ]+/g, '').replace(/\s+/g, '-');
        // Markdown header link: [Header Text](#anchor)
        return `\n[${text}](#${anchor})\n`;
    });

    // 3. Mark block-level tags as line breaks
    const blockTags = [
        "address","article","aside","blockquote","canvas","dd","div","dl","dt","fieldset",
        "figcaption","figure","footer","form","hr","li","main","nav",
        "noscript","ol","p","pre","section","table","tfoot","ul","video","br","tr","td","th"
    ];
    const blockRegex = new RegExp(`]*>`, "gi");
    html = html.replace(blockRegex, "\n");

    // 4. Strip all remaining tags
    html = html.replace(/]+>/g, "");

    // 5. Decode numeric entities (decimal & hex)
    html = html
        .replace(/&#(\d+);/g, (_, dec) => String.fromCharCode(dec))
        .replace(/&#x([0-9A-Fa-f]+);/g, (_, hex) => String.fromCharCode(parseInt(hex, 16)));

    // 6. Decode common named entities
    const entityMap = {
        nbsp: " ", lt: "", amp: "&", quot: '"', apos: "'",
        mdash: "-", ndash: "-", hellip: "…", rsquo: "’", lsquo: "‘",
        ldquo: "“", rdquo: "”"
    };
    html = html.replace(/&([a-zA-Z]+);/g, (_, name) =>
        name in entityMap ? entityMap[name] : `&${name};`
    );

    // 7. Collapse whitespace and blank lines
    const text = html
        .split("\n")
        .map(line => line.replace(/\s+/g, " ").trim())
        .filter(line => line.length > 0)
        .join("\n");

    // 8. Merge short lines
    const threshold = 80;
    const lines = text.split("\n");
    const merged = [];

    for (const line of lines) {
        const trimmed = line.trim();
        if (!trimmed) continue;

        if (merged.length === 0) {
        merged.push(trimmed);
        } else {
        const prev = merged[merged.length - 1];
        if (trimmed.length 
      
    
  )
}

### Create a custom tool
In this section, you'll create a custom tool to fetch the Changelog JSON feed. You can make a custom tool by creating a function. For more information about functions, refer to the [Functions](../../../workflows/guides/functions) page.

1. From the **Configuration** tab, click **Add Tool**.
2. Select **Create new custom tool**.
3. In the **Name** setting, enter `fetchFromChangelog`
4. In the **Description** setting, enter `Searches for and filters changelog entries by user-provided date range.`
5. Click **Add parameter**  twice. Click  to update it. Configure the following parameters:
    | Name          | Type         | Description                                                             | Required |
    |---------------|--------------|------------------------------------------------------------------------ | ---------|
    | `startDate`   | String       | The start date (inclusive) for filtering entries, in YYYY-MM-DD format. | Yes      |
    | `endDate`     | String       | The end date (inclusive) for filtering entries, in YYYY-MM-DD format.   | Yes      |

6. Select **Edit function** in the **Function Logic** section.
7. Delete the `code1` block. Click and drag  from the Parameters block. Create a  block with the **REST API** resource type to `GET` the JSON feed for the Changelog: `https://docs.retool.com/changelog/feed.json`. Name it `getChangelogEntries`.
    :::note
    You can also message the **Function Generator** and @ mention the **Changelog** resource. The function generator will automatically create the function and the parameters for your tool, but depending on the model you use you may have varying results and need to troubleshoot the code that's generated.
    :::
8. From `getChangelogEntries`, click and drag  to add a  block. Replace the default code with the following JavaScript:
    ```javascript
    return getChangelogEntries.data.items
    ```
9. From the  block, click and drag  to add a  block with the following **Expression** to use the moment() date library to match any posts within the specified range:
    ```json
    moment(value.date_modified).isBetween(moment(params.startDate), moment(params.endDate))
    ```
10. From the  block, click and drag  to add a  block with a **Status code** of `200` and the following **Return body**:
    ```json
    filter1.data
    ```
11. Click **Done**.
12. On the **Edit Tool** page, click **Save**.

export function ArcadeEmbed_custom() {
  return (
    
      
    
  )
}

## 4. Deploy your agent
Now that you've configured your first agent, it's time to deploy it.

1. On the **Configuration** tab, click the **Deploy** button.
2. Select the **Major** version radio button and add a **Description**.
3. Click **Deploy**.

## 5. Test your agent
To test your agent and ensure it's working as intended, chat with it.
1. Select the **Chats** tab.
2. Message your agent with the following, changing the date as needed. Ensure you include the correct year.
    ```
    What's my weekly digest for April 28th through May 2nd, 2025?
    ```
3. Your agent will display thoughts and provide *Tool chips* in the chat thread and in the side bar as it runs so you can see which tools were called, the parameters that used, and the result. You can toggle **Hide** or **Show thinking** for the tool chips in the side bar, depending on your preference.
4. When your agent has finished running and provided a final answer, you should have an email in your inbox with your weekly digest, and it will also display the results in the message thread, and should look similar to the below.
    ```
    I've sent your Retool Weekly Digest for April 28 - May 2, 2025 to your email. The digest includes:

    One Edge version released on April 30, 2025

    Five changelog entries covering new features and improvements, including:
    Record user sessions with Fullstory (May 1)
    Backend runtime upgrade to Node.js v20.18 (April 30)
    Disable catch-up commits in Source Control (April 29)
    Multi-step functions now generally available (April 28)
    Error reporting for Retool Workflows (April 28)

    Your digest has been delivered successfully. Let me know if you need anything else!
    ```
5. To make your thread public and share it with another user, click **Share thread** and then click **Publish link**.

You can continue chatting with your agent, and check the **Logs** to test any modifications you make to the tools or agent configuration, or you can revert to previous deployed versions from the version history displayed next to the name of your agent.

## Wrap up
At this point, you should be able to configure an agent, add prebuilt and custom tools to an agent, deploy an agent, and chat with an agent to test how it performs.

To continue learning about agents, explore the following resources:

* [Retool Agents](../concepts/overview)
* [Best Practices](../concepts/best-practices)
* [FAQ](../concepts/faq)
* [AI models](../../../data-sources/concepts/models)
* [Tools](../guides/tools)

---

## Retool Agents tutorials



---

## AI in app building

You can use AI in various ways throughout the app building and editing experience.

:::note

To use the following AI features in Retool, **Retool AI** must be enabled in **Settings** > **AI**. On this page, admins can also toggle each individual feature on or off.

:::

## Assist

_Assist_ is an app generation assistant within the [app IDE](./ide.mdx) that helps you interact with and build apps. Assist can perform the following tasks:

- Generate apps from scratch.
- Edit existing apps.
- Tell you about the purpose and functionality of an app you're editing.
- Answer questions about Retool.

[Learn more about Assist](../guides/assist/index.mdx).

## Ask AI

_Ask AI_ is an assistant that is scoped specifically to queries.

import Ask from '../../_shared/_ask-ai.mdx';

[Read more about Ask AI](../../queries/concepts/ask.mdx).

## AI-Generated README

Instead of manually writing a README, you can use AI to generate one. The LLM evaluates the purpose and functionality of the current app and writes a detailed and fully-formatted README that is added as a page in your app.

[Read more about AI README generation](../guides/app-management/app-documentation.mdx).
 
## Queries and components

You can also use AI as part of your app functionality, enabling users of your app to interact with AI.

### AI Actions

import Actions from '../../_shared/_actions.mdx'

[Read more about AI actions](../../queries/concepts/actions.mdx).

### Components

import Chat from '../../_shared/_chat-components.mdx';

[Read more about the chat components](../guides/forms-inputs/chats/index.mdx).

---

## Web app best practices

import Shared from '/docs/_shared/_app-performance-best-practices.mdx';
import Modules from '/docs/_shared/_module-best-practices.mdx';

## Best practices for using modules

---

## Command Palette

import Shared from '/docs/_shared/_command-palette.mdx';

---

## Components

Retool's components are functionally similar to React components. They are designed to be modular and reusable, and have internal state. Each component is represented by a JavaScript object that contains all of its properties and their current values. The following is an example of a Text Input component:

```json
{
  "pluginType": "TextInputWidget2",
  "spellCheck": false,
  "readOnly": false,
  "iconAfter": "",
  "showCharacterCount": false,
  "autoComplete": false,
  "maxLength": null,
  "hidden": false,
  "customValidation": "",
  "patternType": "",
  "hideValidationMessage": false,
  "textBefore": "",
  "value": "Jenny Rosen",
  "_validate": false,
  "required": false,
  "disabled": false,
  "minLength": null,
  "pattern": "",
  "validationMessage": "",
  "textAfter": "",
  "showInEditor": false,
  "showClear": false,
  "tooltipText": "",
  "labelAlign": "left",
  "id": "textInput1",
  "formDataKey": "textInput1",
  "labelCaption": "Enter your username",
  "labelWidth": 33,
  "autoFill": "",
  "placeholder": "Enter value",
  "label": "Username",
  "labelWidthUnit": "%",
  "invalid": false,
  "iconBefore": "",
  "inputTooltip": "",
  "autoCapitalize": "none",
  "loading": false,
  "labelPosition": "left",
  "labelWrap": false,
  "maintainSpaceWhenHidden": false
}
```

Similar components often share characteristics, allowing you to switch a component from one to another with little to no change. This makes it possible to switch components as your needs grow without having to rebuild your UI from scratch.

## Configuration and properties

You can [write JavaScript](../../../queries/quickstart.mdx) almost anywhere in Retool using `{{ }}` and component property values are available globally. This makes it possible to reference other properties when configuring a value.

For example, you can configure a [Text](https://retool.com/components/text) component to display the value of a [Text Input](https://retool.com/components/text-input) component with `{{ textInput1.value }}` in the app IDE. This also makes it possible to control behavior through truthy or falsy values. You can use expressions like a [ternary operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Conditional_Operator) to use different values based on a condition, such as the value of a [Checkbox](https://retool.com/components/checkbox) component.

## Style options

:::note

Components inherit style options from either a parent component (if nested) or the current app theme. You can override style options directly as needed.

:::

You can customize the style of each component, or the [Header and Main frames](../ide.mdx#canvas) in the canvas. Select the component or frame to customize, then use the **Appearance** section in the **Inspect** tab of the right panel. There are different style options, such as color or border radius, depending on the type of component or the frame.

Color is one of the most effective ways to communicate status or severity to your users. You can set custom color options using the color picker or entering a valid [CSS color value](https://developer.mozilla.org/en-US/docs/Web/CSS/color). You can also select a color from one of the current theme's default colors.

As with other settings, you can [write JavaScript](../../../queries/quickstart.mdx) to conditionally set style options. For example, you can customize the background color of a [Table](https://retool.com/components/table) component column based on whether its quantity is less than or greater than `1000` using `{{ currentSourceRow.quantity > 1000 ? '9ed692' : 'd68b7c' }}`.

## Events

Many components trigger events based on certain user interactions, such as clicking a button or selecting a value. [Event handling](../../guides/interaction-navigation/event-handlers.mdx) enables you to trigger queries or perform actions based on the type of event.

Retool's approach with components makes it possible for you to build complex UIs that reflect changes immediately and dynamically adjust based on any conditions you need.

## Methods

The majority of components include JavaScript _methods_ that control their behavior. Component methods make it possible to write complex JavaScript statements to interact with components and change their state in ways that cannot be achieved with references or inline JavaScript using `{{ }}`.

Similar components often share the same methods. For example, `setValue()` is commonly supported by most inputs as a method to set the value of the component.

```js
textInput1.setValue("Hello World");
```

## Presets

A _preset_ is a preconfigured version of an existing component. Retool's component library includes a number of presets for commonly used configurations. For instance, the URL component is a [Text Input](https://retool.com/components/text-input) component that is preconfigured with URL pattern matching and prefix text. The URL component has the same internal state as Text Input, but configured for URL inputs.

## 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 libraries](../../guides/custom/custom-component-libraries/index.mdx) using React and Typescript. Custom components are complex but enable you to develop a component that meets your exact needs.

## Bulk editing

You can select multiple components and edit them at the same time using the Inspector. Select multiple components by clicking and dragging a selection box around them, or by using cmd/ctrl + click.

export function ArcadeEmbed() {
  return (
    
      
    
  )
}

You can bulk edit components of the same type and components of different types. The Inspector shows only settings supported by all of the selected components. Properties that differ between components are identified as `Mixed` in the Inspector.

:::note

Not all properties are available for bulk editing. For example, **Content** settings for Tables and Charts cannot be bulk edited.

:::

---

## Legacy and deprecated components

Retool continually updates UI components for web and mobile apps. In most cases, no user action is required. There is no disruption to your apps and you can benefit from newer functionality whenever it's added.

While Retool makes every effort to develop components in such a way that they can evolve without disruption, there may be instances where it is not possible to update an existing component. There may also be newer functionality that makes an existing component redundant. If this occurs, you need to update existing apps and migrate the older component to the newer version.

## Legacy components

A legacy component is one that has been superseded by a newer, separate component, but still available for use. The newer component provides the same general functionality but works differently. Because of this, Retool continues to support legacy components alongside their newer versions so that customers can become familiar with the newer version and start using it. Going forward, only the newer component receives feature updates. 

Each legacy component remains available for an indefinite period before being deprecated.

## Deprecated components

A deprecated component is one that is no longer supported by Retool. It continues to work in existing apps but should not be used in newer ones.

Deprecated components are hidden by default. Admins can enable the **Deprecated components** option in the **Beta** organization settings allow the use of older components, but this is not recommended.

## Upgrades and migrations

Whenever you add a component to an app, the app uses the version current at the time. For example, if you built an app in 2022 and open it today, it would use the component versions from that time. Any existing apps with legacy or deprecated components will continue to function.

In general, you must migrate from an existing legacy or deprecated component to use a newer version. This requires updating your apps to use the newer component version. Occasionally, Retool may be able to automatically upgrade a component. If this is possible, the **Upgrade component** option will be present in the Inspector.

---

## Custom CSS styles

:::note

Retool recommends using the built-in style and theme options whenever possible. Class names and DOM structure may change as features are added that could break custom style overrides.

:::

You can provide custom CSS to override Retool's default style rules on a per-app basis. Within the app IDE, click the gear icon on the left and select **Custom CSS**.

You can also provide custom CSS that applies to all apps from the **Advanced** section in your organization settings. Navigate to your organization's settings, then click on your **User** menu on the upper-right and select **Settings**.

Retool component class names are unique and use the format `_retool-[component name]`. For example, the class name of the `textInput1` component would be `_retool-textInput1`.

---

## Debug Tools

import Shared from '/docs/_shared/_debug-tools.mdx';

---

## App IDE

The app IDE is the interface you use to build Retool apps. It is made up of these primary areas:

- [Canvas](#canvas): Arrange components to build your app's interface.
- [Navbar](#navbar): Configure, preview, share and manage your app.
- [Left panel](#left-panel): Explore components, queries, transformers, and constants.
- [Right panel](#right-panel): Create and edit components.
- [Status bar](#status-bar): Select the app environment, releases, runtime, and debug apps.

## Canvas

The canvas is where you build the interface for your app. You drag and drop components from the left panel.

The canvas contains frames in which you can place components.

### Canvas max width

To adjust the maximum width of the canvas, open the [App Inspector](#app-inspector) and update the **Max width** setting. This setting accepts pixel and percentage values, or you can set it to `none` so that the app uses 100% of the viewport. On desktops, regardless of the value, an 800px minimum is enforced.

### Grid layout

Canvas frames use a 12-column grid layout. As you arrange or resize components, the grid becomes visible and component edges snap into position. Each frame automatically grows as you add or resize components by adding more rows to the grid.

All frames use the same fixed-height rows to ensure consistent vertical arrangement. The Main and Header frames dynamically adjust column width based on the size of the viewport, with a maximum width of 1560px. The Sidebar uses a single-column grid only so you cannot place components horizontally next to each other.

## Grouping components

Retool also provides flexible components to [group other components together](../guides/layout-structure/), such as [Container](../reference/components/containers-forms/container.mdx) and [Form](../reference/components/containers-forms/form.mdx). These components extend the 12-column grid layout so you can drag components directly into a container. This creates a group of _nested_ components. All components can be nested in this way.

## Navbar

The navbar is where you customize the IDE environment and configure high-level app settings. From here you can:

- Access the Retool navigation menu to browse apps and access other areas of Retool.
- Toggle the visibility of panels to provide more space when working in the canvas.
- Change zoom size to increase or decrease the canvas size.
- Set the name and description of your app.
- Write [app documentation](../guides/app-management/app-documentation.mdx) for users.
- Search and execute actions from the Command Palette.
- Access the [App actions](#app-actions) menu to configure app settings.
- [Preview and share apps](../guides/app-management/share.mdx) with users.

### App actions

The  menu contains various actions and settings for your app. Some available options include:

- [Import or export an app](../guides/app-management/import-export.mdx) as JSON.
- Duplicate an app or [clone it as a module](../guides/layout-structure/modules.mdx).
- Manage [releases and history](../guides/app-management/releases-history.mdx).
- Configure app settings such as [themes](../guides/presentation-styling/themes.mdx) and [custom branding](../../org-users/concepts/branding.mdx).
- [Manage scripts and styles](../../queries/guides/javascript/custom.mdx#configure-custom-libraries).
- Configure [URL query parameters](../guides/app-management/customize-app-urls.mdx).
- Set [custom keyboard shortcuts](../reference/keyboard-shortcuts.mdx).

## Left panel

The left panel contains several tabs for working with components, writing code, managing releases, and changing app settings.

### Add UI tab

The  **Add UI tab** allows you to search for and add components to your app. You can also add modules.

### Pages tab

The  **Pages tab** allows you to view and manages the pages in your app. You can drag and drop pages to reorder them.

### Component tree tab

The  **Component tree** tab contains a hierarchical tree from which you can select, rename, switch, or delete components. You can view and select components regardless of visibility including those within multiview containers, such as [Tabbed Container](https://retool.com/components/container) component views. Selecting a component makes it visible and, where necessary, switches a container's view to make it active.

### Code tab

The  **Code tab** contains all of your app's [resource queries](../../queries/quickstart.mdx#resource-queries), [JavaScript](../../queries/guides/javascript/index.mdx), [transformers](../../queries/guides/javascript/index.mdx), and [variables](../../queries/reference/objects/variable.mdx). Selecting an item in the list opens a code editor where you can make changes. When a query is selected, there are three additional tabs displayed in the code editor.

### Code search

The  **Code Search** tab includes various options for searching through code in your apps. You can search within JavaScript, queries, event handlers, properties, custom CSS, URL parameters, etc. You can also configure case matching or use regular expressions to refine your searches.

#### General

The **General** tab is where you write and configure general settings for a query. Queries can typically **run automatically when inputs change** or **run only when manually triggered**.

You can write [SQL queries](../../queries/quickstart.mdx#resource-queries) using either **SQL mode** for reading data or **GUI mode** to write data using an interactive query builder. You can also configure query event handlers that trigger when a query runs or fails to run successfully.

To protect your data, queries that perform write or delete operations (e.g., **POST** API requests or **GUI mode** SQL queries) are set to manually triggered by default.

#### Response

The **Response** tab allows you to configure actions and conditions once a query is run. You can configure notifications or customize failure conditions.

#### Advanced

The **Advanced** tab expands on the previous tab's settings. You can configure confirmation requests, timeouts, and disable queries under certain conditions. If you configure a query to run automatically on input change, you can specify which inputs to watch. This triggers the query only when a relevant input changes.

All components appear on the desktop layout by default. The mobile layout is enabled when you configure at least one component to show on mobile. Apps automatically switch to the mobile layout when the viewport width is less than 600px wide.

### State tab

The  **State** tab contains details on components, queries, transformers, and global objects in your app. You can see more details from the sidebar by selecting individual objects. For example, component state contains its current value and all its properties.

The bottom of the window also shows the queries that a component depends on, and vice versa, which can be useful for identifying dependencies in your app.

### Releases and history tab

The  **Releases and history** tab allows you to view and create releases. You can also view your app's history, and revert to previous versions.

### App settings tab

The  **App Settings tab** allows you to change general settings like the mobile layout and user menu branding. There are additional sections for:

- Custom components
- Custom CSS
- Preloaded JavaScript
- Libraries
- App theme
- Notifications
- Localization

### Assist tab

import ChatIcon from "/img/icons/apps/assist.svg";

The  **Assist** tab allows you to use AI to generate apps, make changes to existing apps, and ask questions about Retool.

For more information, visit the [Assist](../guides/assist/index.mdx) guide.

## Inspector

The Inspector, located in the right panel, contains app and component settings. This is the panel you most interact with when building apps. When a component is selected, this is where you set values, update component names, create event handlers, etc.

Settings are organized into different sections, such as **Content**, **Interaction**, and **Appearance**. As some components have many configurable properties, some optional and less frequently used settings are grouped into subsections to reduce clutter. For instance, the **Add-ons** section contains settings for prefix and suffix text.

### App Inspector

The App Inspector appears when you don't have a specific component selected. It contains settings related to the app as a whole, such as URL, shortcut, and max width settings. Open the App Inspector with one of the following actions:

- Click on the dark grey border around the canvas.
- Click on blank parts of the Component Tree or Pages panel.
- Clicking on the Esc key when a component on the canvas is selected.  

export function AppInspector() {
  return (
    
      
    
  )
}

## Status bar

The status bar contains options to:

- [Switch environments](../../org-users/guides/configuration/environments.mdx) to change the data in use.
- [View releases](../guides/app-management/releases-history.mdx).
- [Access Debug Tools](./debug-tools.mdx) to troubleshoot issues.
- Get help and submit breakage reports.

---

## Multiplayer

Multiplayer is a feature of the [IDE](./ide.mdx) that enables multiple users to make changes to the same app simultaneously. Similar to collaborative editing tools like Google Docs, users can see what others are working on and changes are reflected in real-time.

Multiplayer is best suited for development teams who work on features independently. For example, a user works on queries while another makes changes to the UI. It is not designed for collaborative prototyping where users are making frequent changes in close proximity.

:::note

Multiplayer on Self-hosted Retool requires additional configuration and infrastructure changes. Refer to the [configuration guide](../../self-hosted/guides/multiplayer.mdx) for instructions.

:::

## How it works

Apps have internal state. Retool uses the concept of plugins to represent each aspect of an app, such as queries and components. Whenever a user makes a change, such as the position of a component, the app state changes. Retool syncs these changes automatically across all users by keeping the app state current.

Multiplayer leverages conflict-free replicated data types (CRDTs) to ensure consistent app state across users. Retool uses WebSockets to continuously stream app state between users. This approach allows the app state to sync across all users in a matter of milliseconds, providing a real-time collaborative experience.

## Conflict handling

CRDTs help reduce the number of conflicts due to the speed at which app state syncs. If a conflict does occur, Retool uses the Last Write Wins (LWW) principle and uses the change with the most recent timestamp. This approach provides a consistent conflict resolution method and avoids the need for users to manually manage conflicts.

## Indicators

There are a number of indicators within the IDE to visualize multiplayer users and edits.

### Avatars

The avatar for each user appears the IDE toolbar. Avatars are also displayed when editing queries or code in the **Code** pane.

### Cursors

Retool displays a cursor for users when making edits on the canvas (e.g., moving a component).

## Change history

Retool also keeps track of only the changes you make. You can safely undo or redo your own changes as these do not apply to other users' changes.

---

## Notifications

There are two core notification types in Retool:

- Notifications displayed when using apps.
- Notifications displayed in the app IDE when editing apps.

## Internal user notifications

App notifications are for displaying important information to users while they interact with your app. They're displayed automatically after queries run, but you can also configure them using [event handlers](../guides/interaction-navigation/event-handlers.mdx) and the [utils.showNotification](../../queries/reference/libraries/utils.mdx#utilsshownotification) method. Internal user notifications are also displayed while editing apps so they can be configured.

## IDE notifications

IDE notifications are displayed automatically while editing apps (e.g., after deleting a component or undoing an edit). These notifications are for app editors and aren't visible to users.

## Global notification settings

Global notification settings are accessed from the **App settings** tab.

Global settings apply to app notifications. You can override these settings at the individual [query](../../queries/guides/advanced-options.mdx#configure-query-specific-notifications) or notification level. For example, you can set the duration on a notification triggered by an event handler to exceed the global value.

---

## Programming paradigms at Retool

import Shared from '/docs/_shared/_programming-paradigms.mdx';

---

## Embedded apps

You can embed Retool apps within your existing web applications with support for user authentication. Your users can then access these Retool apps directly in the applications they use already and seamlessly authenticate. Refer to the [how-to guide](../../guides/app-management/embed-apps.mdx) for more information and instructions.

## Use cases

Retool enables you to build apps that can add new features or tools faster while keeping data access secure. Building additional CRUD functionality, like forms, settings pages, or onboarding flows, can take weeks with only code, but takes far less time with Retool.

## SDKs

You embed apps using Retool's [React](https://www.npmjs.com/package/react-retool) or [JavaScript](https://npmjs.com/package/@tryretool/retool-embed) SDKs.

## Authentication

There are two authentication processes for embedded apps that provide a seamless experience: SSO and custom authentication with Retool API.

### SSO

If your parent application uses [SSO](../../../sso/index.mdx) with an identity provider (IdP) that Retool supports, you can configure your Retool organization to use the same IdP and allow users to seamlessly authenticate with embedded apps. Since authentication is handled by the IdP, both the parent application and Retool organization share the same authentication state.

### Custom authentication with Retool API

:::note

Available on a Business or an Enterprise plan only.

:::

Custom authentication with Retool API lets you use any authentication solution to give users access to embedded apps. This means users don't have to go through Retool’s login flow. Instead, users log in through your product using your in-house solution (or with another mechanism like SMS, AWS Cognito, or Firebase), and seamlessly interact with data through embedded Retool apps. Your product's backend makes an API call to complete authentication with Retool, so the user doesn't have to go through Retool's login flow.

When your web application authenticates a user, using your preferred authentication method, your backend must make a request to Retool to generate an embed URL. This embed URL is a secure, single-use link that can be used anywhere in your product. To generate and use the embed URL to securely embed Retool apps inside your web app, you need to make some small updates to the frontend and backend parts of your codebase. The following diagram illustrates the authentication flow.

## Getting started

To get started embedding a Retool app, [refer to the guide](../../guides/app-management/embed-apps.mdx).

---

## External apps

Use external apps (sometimes referred to as _custom portals_ or _Retool Portals_) to deliver a fully white-labeled Retool app to internal and external users, such as customers, vendors, or partners. Retool can manage user email invitations, signup, permissions, the app experience, and more.

## Use cases

Organizations that build internal apps (e.g., bulk CSV uploads, customer onboarding flows, or revenue dashboards) and want to expose them to external users would be a good fit for these features. You can create a white-labeled experience and invite your external users directly into your organization to access these apps.

## Custom pages

Organizations can customize the user journey by replacing [Retool’s login, reset password, signup, and invitation claims pages with Retool web apps](../../guides/app-management/external-apps/custom-pages.mdx). This makes it possible to build a fully custom experience for external users, provide you with complete control over branding, and develop custom functionality.

## Branding

You can configure organization [branding](../../guides/app-management/external-apps/index.mdx#configure-branding) to customize headers, sign up and login pages, email invites to users, the Retool user menu, and more.

Branding options include organization and domain name, logo, favicon, and accent and header colors.

## Themes

You can create and manage themes to apply a consistent style across your organization.

Refer to the [theming guide](../../guides/presentation-styling/themes.mdx) for more information.

## Landing pages

Organizations can route external users to a specific app using the [landing page](../../guides/app-management/external-apps/landing-pages.mdx) option. When an external user logs in, Retool automatically routes them to the specified app. For example, you could route all external vendors to a vendor dashboard app that contains links to all the apps they might need to use.

## Retool Events

[Retool Events](../../../workflows/guides/retool-events.mdx) allows organizations to build custom automations using [Retool Workflows](../../../workflows/index.mdx). These trigger in response to actions taken in Retool. You can create workflows to:

- Send custom onboarding emails to new users joining your organization.
- Set up alerts and notifications for actions taken within Retool.
- Fully customize transactional emails (e.g., user invite and password reset emails).

## External users

External apps are unique because they enable external users to access your Retool app. External users include those whose email domains fall outside of your recognized internal domains. External users are [billed at a lower rate](https://retool.com/pricing) than builders.

For more information, read more about [external users](../../../org-users/concepts/external-users.mdx).

---

## Share your apps externally

If you've built internal apps in Retool, you can share those tools with customers, partners, or other external users. Retool offers three primary ways to do so. These options let you securely expose Retool apps to internal users while maintaining full control over the user experience. This guide covers all approaches and helps you determine which best fits your use case.

## Public links

If your app does not contain any restricted information, and you want it to be easily accessible, you can generate a public link to the app. This makes the app available, without authentication, to anyone who has the URL.

:::note

If your instance is self-hosted, reach out to your account manager or the [support team](/support) and request access to public links.

:::

When to choose public links:

- You want anyone on the internet to be able to view your app.
- Your app doesn’t expose any user-specific data.

For more information, refer to [Preview and share web apps with users](../../guides/app-management/share.mdx#generate-a-public-link).

## Embedded apps

You can embed your Retool apps directly into your existing web applications. Retool supports both React and JavaScript SDKs to simplify the integration process.

With embedded apps, users can authenticate through the existing authentication system in which your app is embedded, so they don’t need to log in twice. The backend of your app uses the Retool API to generate a secure, single-use embed URL that grants access to the app. You can also pass metadata into each session to personalize the experience and manage access centrally.

Choose embedded apps when:

- You have an existing customer-facing product or web app, and you want to enhance it with new functionality.
- You want to ship new features fast, without building them from scratch.
- You want to modernize a legacy system.
- You want to bring multiple internal workflows into a single interface.

For more information, explore the [embedded apps conceptual overview](./embed.mdx), or [follow the guide to get started](../../guides/app-management/embed-apps.mdx).

## External apps

:::note

Available on a Business or an Enterprise plan.

:::

Retool also lets you fully white-label the Retool experience to deliver web applications to internal users in a controlled, branded environment, like a custom portal. You can create a standalone experience that reflects your brand, complete with custom domains, themes, and customizable product pages like login and user invites. Built-in navigation and permission controls make it easy to link multiple apps together into a unified, seamless interface.

:::note

External users are billed separately and
at lower prices. Find out more on the [external user](../../../org-users/concepts/external-users.mdx)
page.

:::

Choose external apps when:

- You want to launch a customer-facing portal with full control over the end-to-end user journey.
- You need to deliver multiple tools within a single, cohesive, branded interface.
- You don't have an existing product or want to launch a new standalone experience for your internal users.
- You want to pay a lower amount for external users.

For more information, explore the [external apps conceptual overview](./external.mdx), or [follow the guide to get started](../../guides/app-management/external-apps/index.mdx).

## App sharing options comparison

Use the following table to compare the available options for sharing your app:

| Sharing mechanism | Authentication method                                                 | Plan availability                     |
| ----------------- | --------------------------------------------------------------------- | ------------------------------------- |
| Public links      | No authentication supported.                                          | All plans. Must be requested for self-hosted instances. |
| Embedded apps     | Authentication for internal and external users through parent window. | All plans. Business or an Enterprise plan only when using the Retool API for authentication.                            |
| External apps     | Custom, white-labeled authentication for internal and external users. | Business or an Enterprise plan.        |

## Frequently asked questions

### Can my embedded app users log in with their existing username and password from the parent app?

Yes, you can authenticate users through your existing system using one of the following methods.

- **Using the Embed SDK:** Pass your user's authentication into Retool via the Platform API.
- **Using SSO:** Connect Retool to the same identity provider.
- **Other setups:** If you're not using the SDK or SSO, it depends on your architecture and whether authentication can be coordinated between systems. Contact Retool to discuss your options.

### Can I embed Retool apps without using the JavaScript or React SDKs?

Yes, [simple iFrame embeds are supported](../../guides/app-management/embed-apps.mdx#embed-apps-as-an-iframe), but they’re not compatible with authentication via the Retool API. To avoid double login and provide a secure user experience, Retool recommends using SSO with a shared identity provider when embedding with an iFrame.

### How do I embed a public Retool app that doesn't require a login?

If your apps don’t expose any user-specific data, use a [public link](#public-links) in an iFrame.

### Does Retool support connecting to multiple SSO providers?

Yes, Retool supports multiple SSO provider connections for external apps using [Spaces](../../../org-users/tutorials/spaces.mdx) or a multi-instance self-hosted deployment. [Contact the Retool team](https://community.retool.com) to learn more.

### What are the best practices for environments with external apps?

Please refer to Retool's [environment best practices](/education/coe/customer-resources/environments).

### How do I set up permission groups for external apps?

Retool recommends [removing all access](../../../permissions/guides.mdx#configure-access-rules-for-a-permission-group) (**Edit**, **Use**, **Own**) from the **All Users** group and using the [External User](../../../org-users/concepts/external-users.mdx) group as the primary permission group.

:::note

For organizations created after Retool version 3.259.0, the **All Users** group has no permissions by default. [Read more in the changelog entry](/changelog/updated-permissions-for-all-users)

:::

You can also specify a Retool app as a [landing page](../../guides/app-management/external-apps/landing-pages.mdx). Once set, users in that permission group will be directed to that landing page app after they log in.

### How do I hide internal organizational information from external users?

To prevent external users from seeing internal information, go to the **Permissions** page for the relevant permission group. Under **Additional** > **Settings page visibility**, uncheck options like **View account details**, **View users page with emails**, **Audit logs**, and **Usage analytics**. This ensures sensitive internal organizational data remains hidden from external users.

### Can I see examples of customer-facing apps built by Retool customers?

You can explore several examples on the [Customer Stories](https://retool.com/customers) page, which includes samples of external apps, including portals, net-new customer-facing features, and other interfaces designed for external users.

---

## Usability features

Retool includes a number of features to enhance the usability of your apps. These options include additional context or personalization, text and data formatting, notifications, and query confirmations.

## Input components

:::note

Refer to the [Component Library](https://retool.com/components) for a complete reference of all components and the settings they support.

:::

Most input components, such as [Text Input](https://retool.com/components/text-input), have settings that help you provide users with greater context.

- **Placeholder**: A _placeholder_ appears within the input when it doesn't have a value.
- **Label**: A _label_ appears next to the component. You can also include a _caption_ to provide further context.
- **Add-ons**: You can enhance inputs with _prefix_ and _suffix_ text or icons, _tooltips_ to display further context when the mouse hovers over the input, and _helper_ text that appears when an input is in focus.

## Formatted data types

Certain components, such as [Table](https://retool.com/components/table), allow you to format the contents based on the data it contains—such as currencies or email addresses. For example, you can set a column of dates to use the **Date** format. This allows you to customize the date formatting or even adjust the date shown to match the user's local time zone.

### Personalize apps for the current user

Retool apps have access to _global_ variables, such as `current_user`, which allow you to personalize the app. For example, you can include a welcome message that displays the user's name with `**Welcome, {{current_user.firstName}}!**`.

### Formatted text with Markdown and HTML

You can use Markdown or HTML across a number of surfaces to include rich text or images. Retool renders Markdown and HTML where available. Refer to Github's [Basic writing and formatting syntax
](https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax) guide for more information—Markdown in Retool uses the same conventions.

### Trigger notifications

You can set up notifications using [event handlers](../guides/interaction-navigation/event-handlers.mdx) that trigger in response to any supported event. Add an event handler in **Interaction** section, then select **Show notification** as the action.

In addition to the title and description, you can specify the **Type** of notification by selecting from the list or dynamically with JavaScript. This represents the severity of the message and changes the color used. You can also control the duration for which notifications are shown before being dismissed.

You can also trigger notifications with JavaScript using `utils.showNotification()` .

### Query prompts

Queries that write or delete data can display a confirmation message before they run. You can enable this in the **Advanced** tab of a selected query.

---

## Retool Apps concept guides

import Concept from '/docs/_partials/_doctypes/_concept.mdx';

:::tip Get started with web apps

If you are unfamiliar with Retool web apps and want to get started: 

- Read the [quickstart](./quickstart) to learn about the fundamental concepts.
- Follow a [tutorial](./tutorial/) to build your first web app.

:::

---

## Add documentation to web apps

You can add documentation to an app to provide guidance and context for other users. 

## Write a README

README files are an important part of any programming project. Their purpose is to introduce the codebase to new users and help share important project details with collaborators. All Retool apps include a built-in README file for you to edit.

For web apps, access the README in the **Pages** tab. There are two methods to create a README:

- (Cloud instances only) Click **Generate with AI** to automatically generate a README based on the components and logic in your app.
- Click anywhere on the README page and draft your README in Markdown.

When you're done, click **Preview** to view the rendered Markdown.

export function AIReadme() {
  return (
    
      
    
  )
}

The README is only visible to users with **Edit** or **Own** permissions on the app. When other editors open the **README** page, they’ll see the rendered Markdown, along with the last editor to update the text, and a timestamp. Editors can click **Edit** to update the README themselves.

To regenerate the README with AI, delete all the content and click **Generate with AI**.

import Shared from '/docs/_shared/_app-documentation.mdx';

---

## Customize app URLs

You can customize the URL of apps and their pages to create more discoverable links for users. Apps also support query strings and hash properties to sync property values and control behavior. 

## Configure a custom URL

:::info 
Retool Cloud organizations use the domain `{organization}.retool.com` by default. Organizations on a Business or an Enterprise plan can optionally configure a [custom domain](../../../org-users/guides/cloud/custom-domains.mdx).
:::

Apps include a unique identifier in their URL by default. You can specify a custom URL path and access an app at `{domain}/app/{custom-url}` instead. If you're [embedding the app as an IFRAME](./embed-apps.mdx#embed-apps-as-an-iframe), the app can be accessed at `{domain}/p/{custom-url}`.

Open the [App Inspector](../../concepts/ide.mdx#app-inspector). Enter a custom URL path to use for the app, and preview the full URL.

export function AppInspector() {
  return (
    
      
    
  )
}

## Configure page URLs

Each app page has a title and URL that you configure from the Inspector. The page URL is appended to the app URL and allows for deep-linking to specific pages. For example, a page with the URL of `customers` would be available at `https://example.retool.com/app/support-dashboard/customers`.

Open the  **Pages** pane and select a page to view its settings in the Inspector. Enter a custom page URL path to use. The Inspector displays a preview of the URL to the current page. 

Configure page settings.

:::warning

Disabling a custom URL makes all prior references invalid. You can disable a custom URL in **App Settings > Page Settings**.

:::

### Accessing apps with a custom URL

Apps with custom URLs always display the custom URL in the browser's address bar unless you're editing the app or using the mobile view. Even if you access the app from another app, publicly, or from the original link, the custom URL is displayed. 

If you have a custom domain, short links are accessible from both your custom domain and the Retool domain.

---

## Embed web apps

You can embed your Retool web apps within your existing web-based applications, such as an internal metrics dashboard or customer support tool. Retool provides React and JavaScript SDKs to make the process seamless, and lets you authenticate users in a variety of ways to make sure access to data is secure.

:::note

By default, users of embedded apps are billed as builders. If [external apps](./external-apps/index.mdx) are enabled in your organization, users of embedded apps are billed as external users if their email falls outside of the internal domain list. [Learn more about external users.](../../../org-users/concepts/external-users.mdx)

:::

## Prerequisites

Follow these recommendations to ensure a seamless experience.

### Use the same domain

Use the same top-level domain for your Retool organization as the parent app in which you want to embed. For example, `retool.example.com` and `app.example.com`.

- Retool Cloud: Refer to the [custom domain](../../../org-users/guides/cloud/custom-domains.mdx) guide for instructions on configuring DNS settings.
- Self-hosted Retool: Refer to your cloud provider for instructions on configuring your domain, if necessary.

### Use HTTPS for embed URLs

If you intend to use [custom authentication with Retool API](#custom-authentication-with-retool-api), ensure that all URLs use HTTPS.

## Install an SDK

You can embed apps using Retool's [React](https://www.npmjs.com/package/react-retool) or [JavaScript](https://npmjs.com/package/@tryretool/retool-embed) SDKs.

  

The following example demonstrates embedding an app using the React SDK. It includes some additional data that is passed to the embedded app.

```javascript
import React from 'react';
import Retool from "react-retool";

const sampleData = { name: 'Sample data' }
const url = 'your_app_url'

const App = () => {
  return
  
     mockFunction(data)}
    />
  
}
```

See the [react-retool](https://www.npmjs.com/package/react-retool) documentation for more information.

  
  

After including the JavaScript SDK library, you initialize an instance of your Retool app and embed it at the specified location using the `createRetoolEmbed()` function.

```javascript
const container = document.createElement('div')

app.appendChild(container)

const sampleData = { name: 'Sample data' }
const url = ''

embeddedRetool = createRetoolEmbed({
  src: url,
  data: sampleData,
  onData: (data) => {
      mockFunction(data)
  }
});

container.appendChild(embeddedRetool)
```

  

## Pass data between embedded and parent apps

Embedded Retool apps can use the `data` property and **Parent Window** queries to read values from the parent application. They can also use [JavaScript queries](../../../queries/guides/javascript/index.mdx) to pass data back to the parent app using the browser's `postMessage` API.

### Pass data to an embedded app

Data you pass using the `data`  property is made available to the embedded app using **Parent Window** queries. You add this query to your embedded app and reference the data key in the **Selector** field. If you aren't using either the React or JavaScript SDK, the selector can be a CSS property.

### Pass data to the parent app

To pass data to the parent app, add a JavaScript query and use the `postMessage` API to send the data. For example, a query that sends the email address of the `current_user` to the parent app might look like this.

On the parent application side, write a JavaScript statement to listen and handle these messages. The following example uses `window.addEventListener()` to log the received message to the console.

```javascript
window.addEventListener(
  "message",
  function (e) {
    console.log("Parent window received: ", e.data);
  },
  false,
);
```

The code to handle `postMessage` requests depends on your specific implementation.

## Authenticate users in embedded apps

Users within your Retool organization sign in to an embedded app as they would when accessing the app normally. If you currently use Retool authentication (i.e., you manage users directly in Retool), this can result in users needing to sign in to both the parent app and then Retool.

To avoid this double-authentication flow, you can implement SSO using a supported identity provider or a custom authentication flow using Retool API.

### SSO

If your parent application uses [SSO](../../../sso/index.mdx) with an identity provider (idP) that Retool supports, configuring your Retool organization to use the same idP will allow users to seamlessly authenticate with embedded apps. Since authentication is handled by the idP, both the parent application and Retool organization share the same authentication state.

You can also map groups and roles from your idP into Retool to centralize user management. An added benefit of this approach is that all of your users can also sign into the Retool organization on the web using SSO.

:::warning idP trusted origins

Certain Identity Providers like Okta prevent embedding of third-party applications. You may need to add the domain on which you're hosting Retool as a [trusted origin](https://help.okta.com/en-us/content/topics/api/trusted-origins-iframe.htm).

:::

#### Automatically sign in users

You can configure Retool to automatically sign users in without showing the login screen. This occurs whenever a user has an existing, authenticated session on the IdP.

To configure this, navigate to your organization's **Single Sign-On** settings and enable **Trigger Login Automatically**. Self-hosted organizations can also configure this by setting one of the following [environment variables](../../../self-hosted/reference/environment-variables/index.mdx#authentication) to `true` in the `docker.env` file.

- **SAML**: `TRIGGER_SAML_LOGIN_AUTOMATICALLY`
- **OIDC**: `TRIGGER_OAUTH_2_SSO_LOGIN_AUTOMATICALLY`

### Custom authentication with Retool API

:::note

Available on a Business or an Enterprise plan only.

:::

If your parent application uses a different authentication method, such as username and password, you can integrate custom authentication using the Retool API. When a user successfully authenticates, your backend makes an API request to Retool that generates a secure, single-use URL. Opening this URL then authenticates the user without the need for them to sign in again.

To perform custom authentication with Retool API, you need to:

1. [Generate an access token](#1-generate-an-access-token) so you can create sessions for embedding apps.
2. [Create permission groups for your users](#2-create-permission-groups-for-your-users) so you can give them access to apps.
3. [Create an embed URL](#3-create-an-embed-url) to use when embedding Retool apps into your parent app.
4. [Use the embed URL to display the app](#4-use-the-embed-url-to-display-the-app).

The process to generate the URL happens between your web app's and Retool's backend.

#### Authentication flow

When your web application authenticates a user, using your preferred authentication method, your backend must make a request to Retool to generate an embed URL. This embed URL is a secure, single-use link that can be used anywhere in your product. To generate and use the embed URL to securely embed Retool apps inside your web app, you need to make some small updates to the frontend and backend parts of your codebase. The following diagram illustrates the authentication flow.

See the [GitHub repository](https://github.com/tryretool/retool_external_template/tree/main) for a live demo.

#### 1. Generate an access token

To embed apps, you need to create an access token with the **Embed** scope. This token allows you to create sessions for embedding Retool apps.

import Tokens from "/docs/_partials/_tokens.mdx";

You can create API tokens with the **Embed** scope in the **API** settings for your organization.

#### 2. Create permission groups for your users

Permission groups determine the apps users can access. Depending on your use case, you might need to create more than one permission group.

To create a permission group:

1. Navigate to the Permission Groups page at `/settings/permissions`.
2. Click **Create new** in the top right and complete the form to create a group.
3. Optionally add internal users on the **Members** tab by clicking **+ Add new members**. External users are added later, in the [POST request to your embed URL](#3-create-an-embed-url).
4. On the **Apps** tab, enable **Use** access to the apps you want users to have access to.

Make sure to note down the group IDs you want to give access to. You can find a group's ID by hovering over the group, clicking the **•••** menu, and then selecting **Copy group ID**.

![Copying the group ID](/img/docs/accab16f-e7b1-4683-92fc-0cc12425674c.gif)

#### 3. Create an embed URL

The embed URL is a single-use link to a Retool app for an authenticated user. Your backend sends a POST request to `/api/embed-url/external-user` with a few parameters, and Retool returns the URL. This URL is then used by your frontend to display the app.

The table below outlines the parameters you can send, and whether they're required.

| Parameter                | Required | Description                                                                                                                                                                                                                                                                                     |
| ------------------------ | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `landingPageUuid`        | Yes      | A string to specify the page in the Retool app to render. You can find the `landingPageUuid` by opening the app and using the Command Palette (opened with ctrl k) to select **Copy app UUID**. |
| `groupIds`               | Yes      | An array of integers specifying the Retool group IDs to allow.                                                                                                                                                                                                                                  |
| `sessionDurationMinutes` | No       | The session token's duration, in minutes. If unset, defaults to 1440 (1 day). Values from 10 to 43200 (30 days) are accepted.                                                                                                                                                                   |
| `externalIdentifier`     | Yes      | A string or integer unique identifier for the external user. If the user's email is to be used, it must match what is passed in `userInfo.email`.                                                                                                                                                                                                                                    |
| `userInfo`               | No       | User profile information, necessary to leverage user fields in apps such as `{current_user.email}`. Can contain string fields for `email`, `firstName`, and `lastName`. If `email` matches an existing Retool user, the same account will be used.                                                                                                                                                                                                   |
| `metadata`               | No       | An object with additional metadata. Can be at most 1KB of JSON.                                                                                                                                                                                                                      |
| `environment`            | No       | A string to specify an [environment](../../../org-users/guides/configuration/environments.mdx) for the app to run in.                                                                                                                                                                                                 |
| `branch`                 | No       | A string to specify a [branch](../../../source-control/guides/manage-branches.mdx) to be displayed. You can't display both `releaseVersion` and `branch`.                                                                                                                                               |
| `releaseVersion`         | No       | A string to specify a [release](./releases-history.mdx#releases) to be displayed. Defaults to `latest` but you can pass version numbers too (e.g.,`1.0.0`). You can't display both `releaseVersion` and `branch`.                                                                    |

##### POST request examples

Below are a few example POST requests. Because frameworks vary, only cURL and Python examples are provided.

You need to replace `APP_UUID`, `GROUP_IDs`, and `UUID` in the cURL example for it to work. See the [request parameters](#post-request-parameters) section for more information.

import Tabs from "@theme/Tabs";
import TabItem from "@theme/TabItem";

```curl
// Replace APP_UUID, GROUP_IDs, UUID with
curl -X POST "https://retool.example.com/api/embed-url/external-user" \
  -H "Authorization: Bearer retool_01hn417x8zsaqfye58r995re18" \
  -H "Content-Type: application/json" \
  -d  '{ "landingPageUuid": APP_UUID, "groupIds": GROUP_IDs, "externalIdentifier": UUID}'
```

This example accepts a request from the client, builds and sends a POST request to `/api/embed-url/external-user`, receives the embed URL, and returns the URL to the client.

```python
@app.route('/embedUrl', methods=['POST'])
def embed_url():
  data = request.get_json()
  app_uuid = app_name_to_uuid[data['retoolAppName']]

  # userJwt is an example variable that the frontend could pass to your backend and is not required
  token, first_name, last_name, email = jwt.decode(data['userJwt'], secret_key)

  headers = {
    // The RETOOL_API_KEY is the token generated in the first step
    'Authorization': f"Bearer {os.environ['RETOOL_API_KEY']}",
    'Content-type': 'application/json',
  }
  body = {
    'landingPageUuid': app_uuid,
    'groupIds': [12, 13],
    'externalIdentifier': token,
    'userInfo': {
      'firstName': first_name,
      'lastName': last_name,
      'email': email,
    },
    'metadata': {
      'storeId': 5
    }
  }

  options = {
    'method': 'POST',
    'headers': headers,
    'body': json.dumps(body),
  }

  resp = requests.post(f"https://{os.environ['RETOOL_URL']}/api/embed-url/external-user", **options)
  if resp.ok:
    return resp.json()
  else:
    # Handle error
    pass
```

```javascript
router.post('/embedUrl', async (req, res) => {
  const data = req.body;
  const app_uuid = app_name_to_uuid[data.retoolAppName];

  // userJwt is an example variable that the frontend could pass to your backend and is not required
  const secret_key = process.env.SECRET_KEY;
  const decoded = jwt.decode(data.userJwt, secret_key);
  const { token, firstName, lastName, email } = decoded;

  const headers = {
    'Authorization': `Bearer ${process.env.RETOOL_API_KEY}`,
    'Content-type': 'application/json',
  };

  const body = {
    'landingPageUuid': app_uuid,
    'groupIds': [12, 13],
    'externalIdentifier': token,
    'userInfo': {
      'firstName': firstName,
      'lastName': lastName,
      'email': email,
    },
    'metadata': {
      'storeId': 5
    }
  };

  try {
    const response = await fetch(`https://${process.env.RETOOL_URL}/api/embed-url/external-user`, {
      method: 'POST',
      headers: headers,
      body: JSON.stringify(body),
    });

    if (!response.ok) throw new Error('Response not OK');

    const jsonResponse = await response.json();
    res.json(jsonResponse);
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

```

:::note

If you need to make sure users can only see data specific to them, see the [Control access and app behavior with metadata](#control-access-and-app-behavior-with-metadata) section.

:::

#### 4. Use the embed URL to display the app

On your frontend, use the [Retool React component](https://www.npmjs.com/package/react-retool) or the [Retool Embed SDK](https://www.npmjs.com/package/@tryretool/retool-embed) to render the embed URL. Below is a sample request your frontend would make to your backend.

```javascript
const RetoolWrapper = ({ retoolAppName, userJwt }) => {
  const [retoolEmbedUrl, setRetoolEmbedUrl] = useState("");

  useEffect(() => {
    const options = {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ userJwt, retoolAppName }),
    };
    fetch("/api/embedUrl", options)
      .then((res) => res.json())
      .then((data) => {
        setRetoolEmbedUrl(data.embedUrl);
      });
  }, [retoolAppName]);

  return (
    retoolEmbedUrl && (
      
        
      
    )
  );
};
```

```javascript
const container = document.createElement('div')
app.appendChild(container)

const getEmbedUrl = async () => {...}

getEmbedUrl().then((retoolEmbedUrl) => {
    embeddedRetool = createRetoolEmbed({
        src: retoolEmbedUrl,
        style: "border: 1px solid blue; height: 100%; width: 100%;",
        data: {dataValue: false},
        onData: (data) => {
            mockFunction(data)
        }
    });
    container.appendChild(embeddedRetool)
});
```

#### 6. Control data access with metadata

You can pass information in the `metadata` object to dynamically control the data users see and behavior within an application. These variables are added to the `current_user.metadata` object when the embed URL is generated.

Because [Prevent Query Spoofing](../../../queries/quickstart.mdx#query-variable-spoofing-prevention) is turned on, the metadata associated with the current user cannot be spoofed. Retool will reject any requests if a bad actor alters any metadata values passed in via the POST request above.

#### 7. Testing

When generating the embed URL, Retool may create an additional user depending on the data that is passed into your POST request.

To avoid creating an extra user while testing, pass in the email associated with your Retool account in the `email` property of the `userInfo` object, as well as the IDs of the permission groups you’re in.

If an email is specified and it matches that of an existing Retool user, Retool will update the user, its metadata, and permission groups. Otherwise it will create a new user if this email does not exist.

If no email is specified, Retool updates the user if the `externalIdentifier` matches, or creates a new user if the `externalIdentifier` does not exist.

## Embed apps as an IFRAME

If you cannot use an SDK to embed apps, you can still embed an app as an IFRAME. You can do this with an embed code, or with a public link.

This method lacks some of the features available in the SDK but can allow users to authenticate by signing in.

1. Make sure that your app is published, and not in your **Drafts** folder.
2. Open your app, and click **Share**.
3. Click  **Copy embed code**.

If you don't need to add authentication because your apps do not surface user-specific data, you can [enable public links](./share.mdx#generate-a-public-link). You can share this link directly with users or embed the app in other web apps. These apps can be accessed by anyone on the internet and always reflect the production environment of the app.

:::note

If your instance is self-hosted, reach out to your account manager or the [support team](/support) and request access to public links.

:::

import Link from '../../../_shared/_public-links.mdx';

---

## Build custom pages

Organization admins can replace Retool’s login, reset password, signup, and invitation claim pages with Retool apps. This allows you to customize the entire user journey, which is valuable if you white-label Retool for external users outside your organization.

This guide covers how to set Retool applications as your custom pages and use user actions to control the user lifecycle (login, reset password, claiming an invite, etc.).

## Configurable custom pages

You can replace the following pages with Retool apps:

- Log in
- Confirm reset password
- Claim invitation
- 403 Error page
- 404 Error page

Each of these pages has a distinct route necessary for the user flow to work. For example, from an invite email, Retool needs to know which URL to open (e.g., that of a Retool-managed page or Retool app). Other flows, like how users input their passwords, or sign up for Retool, can be entirely defined by you.

## Generate custom pages

You can use either an existing app or a new app to use as a custom page. Retool recommends creating a new page through the following flow, because it generates template apps that are preconfigured with UI elements and the necessary [user actions](./user-actions.mdx).

1. Go to **Settings** > **Branding**.
2. From the **Preview** section, confirm the dropdown is set to the page you want to modify, such as **Log in**.
3. Click **Manage**.
4. Select **Create a new app**.
5. Set a custom URL for your app. Retool automatically generates the public link at `[your_domain].com/p/[custom_url]`.
6. Navigate to the **Apps** page, and locate your app.

:::warning

New apps created from this flow are placed in the root folder (found under **Published** on the Apps landing page). Ensure you restrict access to these apps by moving them to a more privileged folder, such as the **External apps** folder.

:::

## Customize custom pages

This section covers how to build custom custom pages using Retool apps.

:::warning

Because these apps deal with sensitive information, restrict their access to admins and developers with only the most privileged access.

:::

The [template apps generated](#generate-custom-pages) from **Branding settings** work with no additional configuration. Password reset and invite links automatically use the public, custom URL set.

These template apps also come prebuilt with [user actions](./user-actions.mdx). User actions are an easy-to-use wrapper on top of Retool’s public APIs for:

- Logging in
- Logging out
- Claiming invites
- Sending password reset links
- Confirming reset passwords

User actions are used the same as any other Retool query, except that they can only be called from within apps.

### Build your login page

The login page serves as the default landing page for unauthenticated users. When users log out of Retool, they are automatically redirected to this page, though you can still access the Retool-managed login page at `[your_domain].retool.com/auth/login`. You can use this single page with user actions to support login, password reset, and signup.

When building your login page, use the **Login** user action.

The **Login** user action supports the following login methods:

- Email and password
- Google SSO
- SAML SSO, when already configured in **Settings > SSO**
- OIDC SSO, when already configured in **Settings > SSO**

To ensure users are routed to the Retool home page or a workspace, add an event handler. When you specify `/` as the path, Retool routes the user to the home page, or if they have a [workspace](./landing-pages.mdx) set for their permission group, to their workspace app.

### Build password reset and invite flows

You can use the **Send reset password email** user action to support password reset flows. You can use these actions within your same login app, e.g., by adding a new view to your [container](../../layout-structure/container.mdx#multiple-views), or by creating a dedicated page for password resets and linking to it from your login page.

The **Send reset password email** user action automatically validates the password reset token and email address passed as URL parameters. You can similarly build additional apps and functionality for claiming invites and confirming password resets using the **Claim invitation** and **Confirm password reset** flows.

### Build a signup flow

You can use the [Retool API](../../../../api/index.mdx) to build a self-service signup flow for your users.

In the example login page above, when a user completes the onboarding questionnaire, the form triggers a query calling `POST /user_invites` with the form inputs in the `metadata` field, as well as the default permission groups.

When the user is created upon claiming the invitation, the `companyName` value will be stored on the user object. As an editor, you can access this value by referencing `{{ current_user.metadata.companyName }}`.

## Assign custom pages

For each custom page you created, ensure that each app has a [public link](../share.mdx#generate-a-public-link). Then, return to the **Settings** > **Branding** page, and complete the following steps:

1. From the **Preview** section, confirm the dropdown is set to the page you want to modify, e.g., **Login**.
2. Select **Manage**.
3. Select the app that you want to use as the custom page.

## Security

Because these apps touch the core user lifecycle, it's important to build these applications with security best practices in mind. User actions do not make Retool any less secure, since these endpoints do not require authentication and are publicly accessible—anyone can attempt to reset the password for any email address.

Retool relies on the security of the reset password and invitation process, meaning the links are only accessible from the user’s or admin's email inbox.

---

## Create external apps

You can use [external apps](../../../concepts/share-externally/external.mdx) to white-label your instance and deliver web applications to external users in a controlled, branded environment. This guide covers how to enable external apps (sometimes called Retool Portals) and cutomize the experience for [external users](../../../../org-users/concepts/external-users.mdx), such as customers, vendors, and partners.

## Enable external apps

In your organization settings, navigate to **Settings** > **External apps**, and click **Enable external apps**.

### Set your internal domain list

In order for Retool to determine whether a user is internal or external (and bill accordingly), you must specify an internal domain list. By default, the domains of all admins within your organization are included. Add additional domains as needed.

Once you agree to the terms, Retool automatically creates an **External Users** permission group. This group cannot be deleted, and users with domains outside of the internal domain list are automatically added to it.

export function InternalDomain() {
  return (
    
      
    
  )
}

## Configure branding

Customize branding options for your organization, including the organization name, logo and favicon, and accent and header colors.

These options allow you to remove Retool branding and replace it with your own to customize the experience for both internal and external users. Click **Configure branding and pages** on the **External apps** page, or navigate to **Settings** > **Branding** to get started.

### Set your organization name

On the **Branding and customization** page, click **Edit** to change your organization name. Next, enable **Remove extra references to Retool**. This replaces Retool with your organization's name throughout the product experience, such as on the login page.

### Configure a custom domain

You can configure a custom domain for Cloud or Self-hosted Retool:

Retool Cloud organizations initially start with a URL in the format: `[organization].retool.com`. To change your Retool domain, first follow the instructions in the [Configure a custom domain for Retool Cloud](../../../../org-users/guides/cloud/custom-domains.mdx) guide.

If your organization uses [Self-hosted Retool](../../../../self-hosted/index.mdx), configure your custom domain in your cloud provider's settings.

Navigate back to **Settings** > **Branding**, and click **Edit** in the **Domain name** section. Enter the domain name you configured. After your domain is verified, users can log into your organization at `[mydomain.com]/auth/login`.

Log out and back in using your custom domain, and continue.

### Customize logo and favicon

You can replace Retool's logo and favicon with your own by uploading a logo image in `.jpg` or `.png` format, or a favicon in `.ico` format. You can also enable **Show logo in app headers** to automatically include your logo in the Header frame of all apps, if present.

### Accent and header colors

Set the **Accent color** and **Header background color** to match your company branding. These colors apply to login pages, email pages, and the header for the app editing experience.

### Customize emails

To remove all references to Retool in [Retool Emails](../../../../data-sources/quickstarts/retool-email.mdx), enter a **From name** and a **Reply-to address**. Emails are sent from `email.retool.com` by default. You can alternatively set up [Retool Events](../../../../workflows/guides/retool-events.mdx) to send emails from your own email provider with Retool Workflows.

### Create a theme

Create a [default, organziation-wide theme](../../presentation-styling/themes.mdx) that applies to all apps in your organization.

## Configure permissions

Configure permissions for external users by managing the **External Users** permission group, or by adding external users to other groups. By default, the **External Users** group has **Use** access to the **External apps** folder and all apps inside of it. In the group's additional settings, the Retool app created from the above step is also automatically set as the group's landing page.

:::note

To ensure that external users can only navigate between Retool applications and not access native parts of Retool, the **All Users** permission group must also have default permissions set to **Define specific access**. This ensures that new users only inherit the permissions based on the groups they belong to.

For organizations created after version 3.259.0, the **All Users** group does not have universal access permissions by default. [Read more in the changelog entry](/changelog/updated-permissions-for-all-users).

:::

Refer to the [external users documentation](../../../../org-users/concepts/external-users.mdx) for more information.

## Add Retool Events

If your organization is on an Enterprise plan, you can also configure [Retool Events](../../../../workflows/guides/retool-events.mdx) to help customize bespoke user experiences when new events occur, like **User Created** or **Login Attempted**.

## Next steps

Next, follow the other guides in this section to fully white-label your external apps:

1. [Create a landing page.](./landing-pages.mdx)
2. [Create custom pages.](./custom-pages.mdx)
3. [Utilize user actions.](./user-actions.mdx)

---

## Build a landing page

Admins can configure landing pages for specific [permission groups](../../../../permissions/guides.mdx). Users without **Edit** access who are members of a group with an associated landing page are automatically routed to a specific Retool app when they log in. For example, you can route all Customer Support representatives automatically to a Customer Support Retool app that contains links to all the apps they might need to use.

## Create a landing page

First, create a new app and place it in the **External apps** folder. This folder was automatically created when you [enabled external apps](./index.mdx#enable-external-apps).

An example app might look like this:

This example app contains links to other Retool apps. You can filter which links are available for the user based on their permission groups using JavaScript. For example, you can use the **Hidden** property to hide certain tiles if the current user isn't in the `Pharmacist Manager` permission group:

```
!current_user.groups.includes("Pharmacist Manager")
```

## Configure a landing page

Navigate to **Settings** > **External apps**. Find the **Create a landing page** section, and select your app from the dropdown.

## Landing page routing

Users with **Edit** access to any apps are routed to the Retool homepage when they log in, even if a landing page is set for their groups. Users who are members of multiple groups with landing pages can switch between landing pages using their profile menu in the top right hand corner of the page.

---

## Configure user actions for users

Use the **User Action** resource to write queries in custom page apps that enable users to authenticate. Users can also perform additional actions, such as resetting their password or claiming an invitation. The **User Action** resource can also retrieve a list of apps and folders for which the user has access.

## Create a resource query

You create user action queries in the same way as other resource queries:

1. Click  in the app IDE to open the **Code** panel.
2. Click **+** and select **Resource query**.
3. Select the **User Action** resource.

## Select the action to perform

The **User Action** resource supports the following actions:

- Log in.
- Log out.
- Claim invitation.
- Send password reset email.
- Confirm reset password.
- Get user apps and folders.

Supported actions for the **User Action** resource.

The query displays additional options for you to configure when selecting an action.

### Log in

The **Log in** action authenticates the user. The **User Action** resource supports username and password credentials and any configured single sign-on (SSO) identity provider. Select the method to use for authentication.

SSO authentication is handled by your identity provider and no additional configuration is needed. If you select **Username and password**, reference input component values for these credentials.

For example, if your app has an [Email](../../../reference/components/text-inputs/email.mdx) component (`usernameInput`) and a [Password](../../../reference/components/text-inputs/password.mdx) component (`passwordInput`), you would reference these values using `usernameInput.value` and `passwordInput.value` respectively.

### Log out

The **Log out** action logs the current user out. The user must log in again.

### Claim invitation

The **Claim invitation** action enables invited users to create an account. The action automatically validates the invite token that is included as a URL parameter in the invite link.

The action includes a number of options the user provides to claim their invite. You must add the necessary input components and reference their values so the user can provide this information.

| Input field     | Description                                                                                                            |
| --------------- | ---------------------------------------------------------------------------------------------------------------------- |
| First name      | The first name of the user.                                                                                            |
| Last name       | The last name of the user.                                                                                             |
| Password        | The password to use.                                                                                                   |
| User attributes | Key-value pairs of additional information you can store for users. Learn more about [user attributes](../../../../org-users/guides/user-management/user-attributes.mdx). |

Configure the **Claim invitation** action.

### Send password reset email

You can use the **Send reset password email** action to support password reset flows. You can use these actions within your same login app, e.g., by adding a new view to your [container](../../../../apps/guides/layout-structure/container.mdx#multiple-views), or by creating a dedicated page for password resets and linking to it from your login page.

The **Send reset password email** user action automatically validates the password reset token and email address passed as URL parameters.

### Confirm reset password

Use the **Confirm reset password** action to allow users to change their password after requesting a password reset.

This action automatically validates the password reset token and email address that were included as URL parameters in the reset password link. Reference a Password component for the **New password** option so that the user can input their new password.

### Get user apps and folders

The **Get user apps and folders** action retrieves a list of all apps and folders for which the authenticated user has access. This is useful for providing users with a list of relevant apps to access.

The query results include two arrays of objects, `apps` and `folders`, with details about apps and folders. You can reference either of these arrays from a component that supports mapped options. For example, you could use the [Navigation](../../../../apps/reference/components/navigation/navigation.mdx) component to automatically display a list of all accessible apps for the current user.

A Navigation component with mapped options from available apps.

## Configure event handlers

User action queries do not automatically run. You must configure [event handlers](../../interaction-navigation/event-handlers.mdx) to trigger them in response to an [event](../../../reference/event-handlers.mdx#events), such as clicking a button.

---

## Import and export apps

import Shared from '/docs/_shared/_import-export-apps.mdx';

## Import other apps as pages

You can import legacy single-page apps or pages from existing multipage apps as pages in your new app. This eliminates the need for rebuilding an app from scratch. Click **+** >  **Copy page from app**, and select the app you want to import. If you are importing a page from an existing multipage app, select which page you want to use.

export function CopyApp() {
  return (
    
      
    
  )
}

When you import an existing app as a page:

- Header and Sidebar frame conflicts must be handled. You can choose to retain the current app's existing frames or override them with the incoming app.
- All code is page-scoped.
- Any app settings are ignored. 

If you override the Header or Sidebar frames using the imported app, value reference errors may occur. You must verify and update any code or components that rely on values within these frames, such as accounting for globally scoped and page-scoped value changes.

:::info 
Apps have a global Header and Sidebar frame. If you have a collection of apps that share the same Header or Sidebar frame content using a module (e.g., shared navigation), import each app and create the necessary Header or Sidebar frame content.
:::

---

## App management



---

## Localize web apps

## Set up internationalization

To localize your Retool apps, you must first set up internationalization in your organization settings. Visit the [Internationalization guide](../../../org-users/concepts/internationalization.mdx) to learn more.

## Enable localization

You must enable localization from the app IDE in **App Settings > Localization**. By default, localization is not enabled to preserve app performance.

When enabled, your application use translations uploaded at the organization level. To improve performance and make sure your app is only using the translations it needs, you can specify specific files to use in the app. When selected, you can preview the locales these files support—this is why it's important you keep the file names consistent.

## Access translations

Once localization is enabled, translations are available in the `{{ retoolContext.translations }}` object. Note that only translations in your current browser locale are shown. You can preview different locales in the **App Settings** tab, or change your browser locale (e.g., go to `chrome://settings/languages` on Chrome)  to see translations for different languages.

The current user's locale can be referenced through `{{ current_user.locale }}`.

### Localize your app

Retool provides helpful utility functions through a built-in `i18n` JavaScript library, which leverages [i18next](https://www.i18next.com/).  This means interpolation, plurals, date, and number formatting are automatically available.

#### Switch locales

To allow end-users to switch locales in your app, or test your localization, use the built-in utility function `utils.changeLocale` to simulate the app in different locales. Pass in the [two-character BCP 47 language tag](https://developer.mozilla.org/en-US/docs/Glossary/BCP_47_language_tag) as a parameter. For example, use `utils.changeLocale(de)` to change the language to German.

You can add a Select component to allow users to switch locales. This can be implemented adding an event handler which calls `utils.changeLocale` when a locale is selected.

### Formatting

You can use built-in functions provided by [i18next](https://www.i18next.com/) for formatting.

#### Static text

Translate static text by calling `{{ i18n.t('your_translation_key') }}`. See the [i18next documentation](https://www.i18next.com/translation-function/essentials#overview-options) for additional options, like fallback text.

#### Dynamic data

If you're translating dynamic data returned from a query, Retool recommends using [interpolation](https://www.i18next.com/translation-function/interpolation) in your translations.

For example, let's take a translation JSON uploaded for `en-US`:

```JSON
{
  "viewingExpenses": "{{viewing}} out of {{total}} expenses"
}
```

In your Retool application, use:

 ```Javascript
 {{ i18n.t('viewingExpenses', { viewing: filteredData.value.length, total: allData.value.length}) }}
```
#### Currency

You can format numbers and currencies with built-in currency translations.

```JSON
 {
  "formatCurrency": "{{val, currency(USD)}}"
 }
```

In your Retool application, use:

 ```Javascript
 // Item is a value returned from a query
 {{ i18n.t('formatCurrency', { val: item }) }}
```

See [i18next documentation](https://www.i18next.com/translation-function/formatting#number) on formatting currencies for more.

#### Dates

Specify the interpolation below to format dates.

```JSON
 {
    "formatDate": "{{val, datetime}}"
 }
```

In your Retool application, you can use either `i18n` or the built in [moment](https://momentjs.com/) library for datetime translations.

 ```Javascript
 // Using i18n
{{ i18n.t('formatDate', { val: item.created_at })}}

// Using moment
{{ moment(item.created_at).locale(current_user.locale).format("LL") }}
```

### Components

Retool supports localization of most components with the following languages:

* English (EN)
* Spanish (ES)
* French (FR)
* Portuguese (PT)
* Japanese (JA)
* German (DE)
* Danish (DA)

When localization is enabled, the default value for supported components is automatically set to `{{ i18n.default }}`. This only applies to new components added to apps—existing component default values are not changed.

The following do not currently support localization:

* Retool Mobile and mobile components
* The Rich text Editor component
* The Microphone component
* The Scanner component
* The Auth Login component
* The Stripe Card Form component
* Custom components

---

## Version and release apps

import Shared from '/docs/_shared/_releases-history.mdx';

---

## Preview and share web apps with users

import Share from "../../../_shared/_share.mdx";

---

## Configure URL parameters

You can include URL query strings and hash parameters when linking to an app page and reference their values, such as passing a search term or setting the current view of a container. 

:::tip 
Retool also includes a number of [built-in URL parameters](../../reference/url-parameters.mdx) to control certain characteristics of an app.
:::

## Configure URL parameters

You can include any hash or query string parameters within an app's URL. URL parameters are page-scoped and only accessible from the page specified in the URL. If no page is specified, URL parameters are available within the app's default page. 

You can configure page-specific URL parameters that reference values and update the URL whenever they change. This allows users to link to an app's current state, such as a selected table row, or sync values between different pages and apps.

Open the  **Pages** pane and select a page to view its settings in the Inspector. You can set each page's URL search and hash parameters to reference any globally or page-scoped value. Any changes to referenced values are then automatically reflected in the current URL and in the `url` object.

You cna also configure URL parameters programmatically using the [utils.setUrlParameters](../../../queries/reference/libraries/utils.mdx#method-setUrlParameters) method.

### Search query parameters

You can provide search query parameters by appending `?` to the URL and using the format `key=value`. Use the `&` symbol as a separator to provide multiple key-value pairs.

```
https://example.retool.com/app/users?role=viewer&team=workplace&q=chic
```

### Hash parameters

You can also provide hash parameters by appending `#` to the URL using the format `key=value`. Use the `&` symbol as a separator to provide multiple key-value pairs.

```
https://example.retool.com/app/users#enabled=true
```

If you need to include both types of parameters, hash parameters must be after query strings.

```
https://example.retool.com/app/customer-dashboard?q=chic&role=viewer&team=workplace#enabled=true
```

## Reference URL parameters

You can use URL parameters in the app by referencing the [url](../../reference/objects/url.mdx) object. Both `hashParams` and `searchParams` contain key-value pairs that represent their respective parameters within the current URL.

```
{
  "hashParams": {
    "enabled": true
  },
  "searchParams: {
    "role": "viewer",
    "q": "chic",
    "team": "workplace"
  }
}
```

For example, you would reference these parameters using `{{ url.hashParams.enabled }}` and `{{ url.searchParams.q }}`. You can use these almost anywhere within the app, such as populating an input component or query parameter.

You can also include URL query parameters when configuring the **Open page** or **Open app** [event handlers](../interaction-navigation/event-handlers.mdx).

Configure URL parameters when linking to other pages or apps.

## Persist URL parameters

You can chose whether all "Go to page" event handers should have the **Persist URL Params** setting enabled by default. When enabled, [search query parameters](#search-query-parameters) and [hash parameters](#hash-parameters) persist during page navigation and when switching pages with the “Go to Page” event handlers.

Turn on this setting by navigating to the [App Inspector](../../concepts/ide.mdx#app-inspector), and enable **Persist URL Parameters**.

If enabling this setting would override a specific “Go to Page” handler’s settings, a confirmation popup appears, asking the user to confirm the override.

---

## Configure your organization for Assist

An organization admin must complete the steps in the following sections to make sure your organization is ready to use Assist.

:::note

To learn how to prompt Assist, visit the [prompting guide](./prompt.mdx).

:::

## Configure organization settings

Ensure that Assist is enabled and that it has access to the proper resources. These steps differ slightly depending on your plan.

1. To ensure your users can access Assist, navigate to **Settings** > **AI**.
   1. Verify that the **Retool AI** and **Assist Tab** settings are toggled on. 
   2. Choose whether to show the Assist chat on the apps landing page.
2. Navigate to **Resources** > **Retool AI**. Ensure that _both_ OpenAI and Anthropic are enabled in the **Retool managed** section.

1. To ensure your users can access Assist, navigate to **Settings** > **AI**.
   1. Verify that the **Retool AI** and **Assist Tab** settings are toggled on. 
   2. Choose whether to show the Assist chat on the apps landing page.
2. Navigate to **Resources** > **Retool AI**. Ensure that _both_ OpenAI and Anthropic are enabled in the **Retool managed** section.

Configure role-based access controls

Organization admins on the Business and Enterprise plans can use Retool's [role-based access controls](../../../permissions/guides/roles-permissions.mdx) to restrict a [group's](../../../permissions/quickstart.mdx#permission-groups) permissions for Assist to **Ask** mode only or to prevent Assist usage entirely. For example, admins might wish to restrict Assist usage to user groups who do not have access to sensitive data or PII. Alternatively, admins could restrict access to all user groups except for a pilot group that has approval to test AI tools.

By default, all builders have access to Assist and both **Ask** and **Build** mode through a preconfigured role. This role is called `Assist Role` or `Editor`, based on when your organization was created. Organization admins can edit these roles, or they can create new roles with more restrictive permissions using the following steps:

1. Navigate to **Settings** > **Roles & Permissions**. 
2. Select an existing role, or click **Create Role** to create a new custom role.
3. Locate the **Assist** section.
   1. Select **Use Assist with Ask mode only** to restrict the role from being able to use **Build** mode.
   2. Deselect both settings to restrict the role from being able to access Assist at all.
4. Click **Save changes**.
5. If you're creating a new role, reopen the role and switch to the **Assignment** tab. Click **+ Add group assignment** to assign a group to this role.
6. Click **Save changes**.

export function RBAC() {
  return (
    
      
    
  )
}

1. To ensure your users can access Assist, navigate to **Settings** > **AI**. 
   1. Verify that the **Retool AI** and **Assist Tab** settings are toggled on.
   2. Optionally, [configure a single model provider](#optional-configure-model-provider).
   3. Choose whether to show the Assist chat on the apps landing page.
2. Navigate to **Resources** > **Retool AI**. Ensure that each AI provider that you will use is enabled. You can choose to use Retool-managed keys or self-managed keys.
3. If required, configure your [roles and permissions](#configure-role-based-access-controls) to limit Assist access to certain groups.

Optional: Configure model provider

Retool designed Assist to use two AI providers by default: OpenAI and Anthropic. Using both providers enables Assist to optimize for speed and quality.

However, some customers may need to consolidate AI services with a single model provider. To accommodate these customers, admins on the Enterprise plan can configure Assist to work with a single model provider.

Retool currently supports the following options for Assist model providers:

:::note
If **Anthropic + OpenAI** is selected, Retool will first attempt to use the Anthropic resource for Anthropic requests. If the Anthropic resource is not enabled, Retool will attempt to send requests to Anthropic through Amazon Bedrock.
:::

- (Default) Anthropic + OpenAI.
- Only Anthropic.
- Only Amazon Bedrock.
- Only Azure OpenAI.

:::note

To enable the use of Amazon Bedrock or Azure OpenAI as a single model provider, reach out to your account manager.

:::

1. Navigate to **Settings** > **AI**.
2. Under **Assist Tab**, change the **Choose Assist provider** setting to your selected single model provider.

  AI settings page

:::note

App generation results may not be optimized when using a single model provider. Retool recommends setting **Choose Assist provider** to **Default: (Anthropic + OpenAI)**.

:::

Configure role-based access controls

Organization admins on the Business and Enterprise plans can use Retool's [role-based access controls](../../../permissions/guides/roles-permissions.mdx) to restrict a [group's](../../../permissions/quickstart.mdx#permission-groups) permissions for Assist to **Ask** mode only or to prevent Assist usage entirely. For example, admins might wish to restrict Assist usage to user groups who do not have access to sensitive data or PII. Alternatively, admins could restrict access to all user groups except for a pilot group that has approval to test AI tools.

By default, all builders have access to Assist and both **Ask** and **Build** mode through a preconfigured role. This role is called `Assist Role` or `Editor`, based on when your organization was created. Organization admins can edit these roles, or they can create new roles with more restrictive permissions using the following steps:

1. Navigate to **Settings** > **Roles & Permissions**. 
2. Select an existing role, or click **Create Role** to create a new custom role.
3. Locate the **Assist** section.
   1. Select **Use Assist with Ask mode only** to restrict the role from being able to use **Build** mode.
   2. Deselect both settings to restrict the role from being able to access Assist at all.
4. Click **Save changes**.
5. If you're creating a new role, reopen the role and switch to the **Assignment** tab. Click **+ Add group assignment** to assign a group to this role.
6. Click **Save changes**.

export function RBAC2() {
  return (
    
      
    
  )
}

## Self-hosted configuration

Complete the steps in the following sections if your deployment is self-hosted. Skip these steps if you use Retool Cloud.

### Configure environment variables

:::important

The following section is required only if your instance is self-hosted and you use [Retool-managed keys](../../../data-sources/concepts/models.mdx#retool-managed-and-self-managed-models). If you are using your own key, you can skip this section.

:::

import Partial from "../../../_shared/_retool-managed-self-hosted.mdx";

### Configure AI proxy

:::important

The following section is required only if your instance is self-hosted and you [use your own key](../../../data-sources/concepts/models.mdx#retool-managed-and-self-managed-models). If you are using a Retool-managed key, you can skip this section.

:::

If you use a proxy to make calls to LLM providers (such as [LiteLLM](https://www.litellm.ai/) or [LLM Proxy Server](https://pypi.org/project/llm-proxy-server/)), you must to configure the models for each provider that Assist uses.

Retool uses the following models for app generation with Assist:

| Provider       | Models                                                                                                |
| -------------- | ----------------------------------------------------------------------------------------------------- |
| OpenAI         | GPT-5.1GPT-5GPT-5-miniGPT-5-nanoGPT-4.1GPT-4.1-nano                          |
| Anthropic      | Claude Sonnet 4.5Claude Sonnet 4Claude Haiku 4.5 |
| Amazon Bedrock | Claude Sonnet 4.5Claude Sonnet 4Claude Haiku 4.5 |
| Azure OpenAI   | GPT-5.1GPT-5GPT-5-miniGPT-5-nanoGPT-4.1GPT-4.1-nano                          |

:::note

Enterprise customers can [select a single model provider](#configure-model-provider), but it is not possible to configure which models Assist uses. You must configure your proxy to use all the models associated with your chosen provider.

:::

## Choose a change management strategy

Assist includes an integrated versioning strategy, which identifies an initial version of your app and creates updated versions that it saves to history as users prompt. Users can view the versions in the  **Releases and history** tab.

As an admin, consider whether you would like to recommend a particular change management strategy to isolate Assist changes:

- **Releases**: Organizations on the Team, Business, and Enterprise plan can use [releases](../app-management/releases-history.mdx) to manage changes. Users can create a "before" version of the app, and roll back changes to that version if necessary.
- **Source Control**: Organizations on the Enterprise plan can use Source Control to [protect apps](../../../source-control/guides/protect/apps.mdx) before prompting Retool to make changes. Consider advising that users create a new branch to experiment with Assist.

## Track usage

Organization admins can monitor Assist usage in **Settings** > **Plans & Billing** under the **Assist billing** tab.

  Assist usage interface.

For more information about usage and billing, refer to the [pricing page](https://retool.com/pricing).

---

## AI-assisted app generation with Assist

Prompt Assist with natural language to generate apps, make changes to existing apps, and ask questions about Retool.

This guide provides an overview of Assist's features and frequently asked questions. Visit the [Configuration](./configure.mdx) or [Prompting](./prompt.mdx) guides to get started.

## Features

The following sections include information about Retool's app generation capabilities and limitations.

With Assist, Retool can create or edit apps:
  - Create all the UI and logic elements needed to create a functional multipage app, including components, pages, queries, code, and event handlers.
    
    Assist can _add_ the following components to the canvas. Retool can _edit_ any component on the canvas.
    - Buttons
      - Button Group
      - Button
      - Close Button
      - Dropdown Button
      - Link List
      - Link
      - Outline Button
      - Split Button
      - Toggle Button
    - Charts
      - Bar Chart
      - Heat Map
      - Line Chart
      - Pie Chart
      - Scatter Chart
      - Sparkline
      - Sunburst Chart
      - Treemap
    - Containers and forms
      - Collapsible Container
      - Container
      - Form
      - JSON Schema Form
      - Link Card
      - Stack
      - Stepped Container
    - Custom
      - HTML
      - IFrame
    - Data
      - Filter
      - JSON Explorer
      - Key Value
      - Reorderable List
      - Table
    - Date and time inputs
      - Calendar Input
      - Date Range
      - Date Time
      - Date
      - Day
      - Month
      - Time
      - Year
    - Integrations
      - AuthLogin
      - Mapbox Map
    - Navigation
      - Breadcrumbs
      - Navigation
      - Tabs
    - Number inputs
      - Currency
      - Editable Number
      - Number Input
      - Percent
      - Phone Number Input
      - Range Slider
      - Rating
      - Slider
    - Presentation
      - Alert
      - Avatar
      - Calendar
      - Circular Image
      - Divider
      - Event List
      - Icon Text
      - Icon
      - Image Grid
      - Image
      - PDF
      - Progress Bar
      - Progress Circle
      - QR Code
      - Spacer
      - Statistic
      - Status
      - Tags
      - Text
      - Timeline
      - Video
    - Repeatables
      - Container List View
      - Grid View
      - List View
    - Select inputs
      - Cascader
      - Checkbox Group
      - Checkbox Tree
      - Checkbox
      - Listbox
      - Multiselect
      - Radio Group
      - Segmented Control
      - Select
      - Switch
    - Special inputs
      - Agent Chat
      - Annotated Text
      - File Input
      - Signature
      - Timer
    - Text inputs
      - Email
      - JSON Editor
      - Password
      - Rich Text Editor
      - Text Area
      - Text Input
      - URL
    
  - Make changes to an existing multipage app, including existing components and queries.
  - (Cloud instances only) Write and edit [READMEs](../app-management/app-documentation.mdx#write-a-readme), or use an existing README for context about your app.
  - Intelligently apply [app-level or organization-level theming](../../guides/presentation-styling/themes.mdx) based on your input.
  - Remember conversation context so that you can iterate with follow-up requests and revert changes.

With Assist, Retool can interact with resources:
  - Generate, edit, and run queries for the most commonly used resources.
    
    - Analytics  
      - Databricks  
      - Datadog  
      - Google Search Console  
    - API  
      - GraphQL  
      - OpenAPI  
      - REST API  
      - SOAP API  
    - CRM  
      - BigID  
      - Close  
      - Front  
      - Salesforce  
    - Database  
      - AlloyDB  
      - Amazon DynamoDB  
      - Amazon Redshift  
      - BigQuery  
      - CosmosDB  
      - Google Cloud Datastore  
      - Google Sheets  
      - Microsoft SQL Server  
      - MongoDB  
      - MySQL  
      - Oracle Database  
      - PostgreSQL  
      - Presto  
      - SAP Hana  
      - Snowflake  
      - Vertica  
    - Development  
      - AWS Lambda  
      - CircleCI  
      - Firebase  
      - GitHub  
      - Google Maps  
      - gRPC  
    - Finance  
      - Stripe  
    - Messaging  
      - Microsoft Teams  
      - OneSignal  
      - SendGrid  
      - Slack  
      - SMTP  
    - Object and file store  
      - Amazon S3  
      - Google Cloud Storage  
    - Project management  
      - Asana
      - Google Calendar
      - Jira  
      - Notion
    
  - Generate a [Retool Database](../../../data-sources/quickstarts/retool-database.mdx) schema and populate the table with sample data.
  - Infer the intended resource from the prompt contex and surface it so that you can add it to the [Assist data access list](./prompt.mdx#prepare-resources).
  - Interact with resources using the permissions of the [current_user](../../reference/objects/current-user.mdx).

Assist can answer questions, including:
  - Answer questions about Retool, and provide links to documentation and the [Community Forum](https://community.retool.com/) for more information.
    :::note

    This capability is only supported on Retool Cloud. It is not yet available for self-hosted organizations.

    :::
  - Answer questions about an existing app, including about the purpose of the app and how it works.
  - Use the dedicated **Ask** mode to provide a safe, read-only environment to explore the app, troubleshoot, and ask questions.

In response to a user prompt, you cannot currently use Assist to perform certain tasks:

- Operate on other objects in your organizations, such as workflows, agents, folders, or mobile apps. Assist also can't modify organization settings, like permission groups.
- Create or edit [modules](../../guides/layout-structure/modules.mdx) or [custom components](../../guides/custom/custom-component-libraries/index.mdx).
- Style custom and legacy component, write custom CSS, or independently style dark and light modes.
- Generate apps that are mobile-responsive.
- Accept inputs that are not formatted as natural language (such as PDFs, spreadsheets, or images).
- Manage [releases](../../guides/app-management/releases-history.mdx).
- Leverage [preloaded or built-in JavaScript libraries](../../../queries/guides/javascript/custom.mdx).
- Selectively roll back some changes and keep others. Instead, [use the Assist checkpoints](./prompt.mdx#manage-changes) to revert the app back to a previous version.
- Add certain less common components to the canvas. Assist can add most components to the canvas, but there are a some less-common components that Assist cannot currently add to the canvas. If you prompt Assist to add one of these components, Assist can provide recommendations for other components to use.
  
    - Avatar Group  
    - Bounding Box  
    - Bubble Chart  
    - Color Input  
    - Comment Thread  
    - Editable Text  
    - Editable Text Area  
    - File Button  
    - File Dropzone  
    - Funnel Chart  
    - LLM Chat  
    - Looker  
    - Microphone  
    - Mixed Chart  
    - Multiselect Listbox  
    - Page Input  
    - Pagination  
    - Plotly JSON Chart  
    - Sankey Chart  
    - Scanner  
    - Stacked Bar Chart  
    - Steps  
    - Stripe Card Form  
    - Switch Group  
    - Tabbed Container  
    - Tableau  
    - Toggle Link  
    - Waterfall Chart  
    - Wizard
  
- Generate or edit queries for certain less common resources.
  
  - Amazon Athena
  - Amazon SQS
  - Amazon SNS
  - CouchDB
  - Elasticsearch
  - Google Analytics
  - Google Docs
  - Google Slides
  - Hubspot
  - Kafka
  - RethinkDB
  - Retool RPC
  - Retool AI
  - Tavily
  - Twilio
  

## Frequently asked questions

Refer to this section for commonly-asked questions.

### What AI models are required?

Organizations on the Free, Team, and Business plans must have *both* OpenAI and Anthropic AI providers enabled in the **Retool AI** resource, and they must use Retool-managed keys.

Admins on the Enterprise plan can [configure Assist to work with a single provider](./configure.mdx?plan=ent#optional-configure-model-provider). The currently supported options are OpenAI, Anthropic, Amazon Bedrock, and Azure OpenAI. Enterprise customers are also permitted to use self-managed models. For more information, refer to [Retool AI providers and models](../../../data-sources/concepts/models.mdx).

### Is there any charge for using Assist?

Usage of Assist is free for a limited time. Refer to the [Usage and billing](/support/billing-usage/assist) documentation for more information.

### My Retool deployment is Self-hosted. Can I use Assist?

Assist is available to all cloud organizations. Assist is available to self-hosted organizations on edge version 3.283 and later, and stable version 3.284.0 and later.

### Is Assist available for mobile apps, workflows, or agents?

No, Assist is currently available for web apps only.

### Does Retool store my prompts?

Yes, Retool stores your prompts and Assist's responses in an internal database. For more information, refer to the [Assist Beta Terms](/legal/assist-beta-terms#3-proprietary-rights).

### Can I enable Assist for a subset of my users, instead of all?

Retool does not currently support enabling Assist for only a subset of users in an organization. Instead, you can create a new [space](../../../org-users/tutorials/spaces.mdx) and invite only the users who you want to have access to Assist, and [enable Assist](./configure.mdx) in that space. Each space has its own user accounts, settings, and permissions.

### How do I provide Retool with my feedback or ask questions?

Please provide feedback in one of the following formats:

- Create a new topic on the [Community Forum](https://community.retool.com).
- Leave feedback in the Assist interface:
  - Click the  **Feedback** button at the top of the **Assist** tab to leave general feedback about Assist.
  - Label output with the  **Thumbs up** or  **Thumbs down** reaction, and submit feedback to Retool. 
  - As part of this feedback, Retool also receives your chat history and your user email address so that Retool can follow up with any questions.

---

## Prompt with Assist

import ChatIcon from "/img/icons/apps/assist.svg";

When you're ready to use Assist, open a new or existing app and locate the  **Assist** panel at the bottom of the [left panel of the IDE](../../concepts/ide.mdx#left-panel). You can also use cmd/ctrl i to toggle the panel.

:::note

This guide outlines the Assist interface, prompting experience, and best practices. For admins who need to configure their organization to use Assist, visit the [Configuration guide](./configure.mdx).

:::

The following video provides a demo and best practices for prompting Assist:

## Assist interface

:::note

The first time you use Assist, you may be prompted to accept some [Terms and Conditions](/legal/assist-beta-terms).

:::

The Assist interface has several important controls:

- The welcome screen includes a series of examples that demonstrate prompts that you could use with Assist.
- Write your prompt in the input box at the bottom of the panel. 
  - [Add resources](#prepare-resources) with the  icon.
  - Assist is in [**Build** mode](#build-and-ask-mode) by default. Click  to switch to **Ask** mode.
  - Click  or press Enter/Return to submit your prompt.
- Use a series of icons at the top of the panel to manage your Assist interactions:
  -  **Feedback**: Leave general feedback about Assist. As part of this feedback, Retool also receives your chat history and your user email address so that Retool can follow up with any questions. 
  -  **View history**: View and manage chat threads. You can view, rename, and delete chat threads.
  -  **New thread**: Create a new chat thread.
  -  **Usage**: View and manage usage. Refer to the [pricing page](https://retool.com/pricing) for information about Assist pricing. This icon is not present for users on an Enterprise plan. Instead, admins in these organizations can view usage information in their organization settings.
- Scroll, or use the up and down keys on your keyboard to navigate the message history.

export function Tour() {
  return (
    
      
    
  )
}

## Prepare resources

You retain full control over your resources, and Assist cannot create or modify them independently. Before prompting with Assist, [create or configure all resources](../../../data-sources/quickstarts/resources.mdx) that you want your app to use.

:::note

When configuring your resource, be as detailed as possible in the **Description** setting on the **Resource configuration** page. This information is passed to Assist, and it provides valuable context on when or how the resource should be used.

:::

When working with apps, Assist uses a **Resource access** list that identifies which resources in your organization it can use to write and modify queries. Assist can suggest adding other resources to this list, but you control what the **Resource access** list contains. Add resources to this list while prompting by **@** mentioning them or describing them. You can also add them explicitly.

To manually add resources to the **Resource access** list:

1. Open the  **Assist** panel in the app you want to create or edit.
2. Click  **Resource access** in the input field to open the **Assist resource access** window.
3. Identify the resources that you want to use while prompting. 

export function ResourceContext() {
  return (
    
      
    
  )
}

Once you're done, the  **Resource access** icon shows the number of resources you added. You can add or remove resources at any point. 

## Build and Ask mode

Retool supports two modes for prompting Assist:

* **Ask**: Assist cannot modify your app. Use this mode to brainstorm solutions, explore the app without making changes, and ask questions about Retool functionality.
* **Build**: Assist can create and edit apps. Use this mode when you want Assist to modify the app on your behalf. **Build** mode also includes all the abilities and knowledge of **Ask** mode.

  Toggle between **Ask** and **Build** mode.

Assist defaults to using **Build** mode. You can switch between modes at any time.

:::note

Organization admins can configure permission groups to have access to **Ask** and **Build**, or just **Ask** using role-based access controls. [Learn more](./configure.mdx#configure-role-based-access-controls).

:::

## Configure theme

You can also set or change your app theme using Assist. The experience of doing so depends on your organization's plan.

During app generation, Assist guides you through selecting a theme for your app. Choose between light or dark mode and select an accent color.

You can also set an app theme in your prompt. Be very clear and specific about your design intent. For example, "Create a dark mode theme with blue accent colors", or "Update the theme to match the Acme brand colors and style."

export function AppTheme() {
  return (
    
      
    
  )
}

The Business and Enterprise plans support [organization-level theming](/apps/guides/presentation-styling/themes#create-organization-level-themes). Assist will help you set a theme for your app in one of the following ways:

- If you haven't created any organization-level themes, Assist shows a theme selector to help you choose an app-level theme.
- If you have organization-level themes, but no default, Assist shows a list of your organization-level themes to choose from, or you can choose an alternative theme.
- If you have organization-level themes with a default set, Assist uses the default automatically.

You can also set an app theme in your prompt. Be very clear and specific about your design intent. For example, "Create a dark mode theme with blue accent colors", or "Update the theme to match the Acme brand colors and style."

export function OrgThemes() {
  return (
    
      
    
  )
}

## Prompting experience

Use the text input at the bottom of the **Assist** panel to enter your natural language prompt. When prompting, enter the **@** symbol to explicitly reference a resource, query, or component that you want Assist to use. Refer to the [best practices](#prompting-best-practices) for more guidance on how to prompt Assist.

Click  or press Enter/Return to submit your prompt.

Once you prompt, Assist proceeds through several planning steps. Assist might ask clarifying questions about your prompt, such as whether you'd like to connect Retool Database or use client-side mock data.

  Assist's initial planning phase.

Once Assist is done planning, it proceeds with creating or editing the code, components, and styling that you requested.

Assist asks you for permission to run and save queries. You can choose to be prompted every time, or you can auto-approve queries.

  Configure how you want to approve query runs.

When it's done building, Assist sometimes provides options for what to do next. You can select one of these options, or you can write another prompt.

  Choose what you'd like to do next.

The amount of information sent to the AI provider (the _context window_) increases as you build. Assist automatically summarizes long threads in order to prevent errors from a context window that is too large.

  Assist summarizes long threads.

## Manage changes

Assist includes an integrated versioning strategy, which identifies an initial version of your app and creates updated versions that it saves to [history](../app-management/releases-history.mdx#history) as you prompt. You can view the versions in the  **Releases and history** tab.

At any time while prompting, use the  **Revert to version** button to undo the changes made after that point. This makes it easy to roll back changes if you change your mind.

If you are on a Business or an Enterprise plan, you can also click  **Create release** to [create a new release](../app-management/releases-history.mdx) incorporating those changes.

Your organization admin may alternatively recommend using [Source Control](../../../source-control/index.mdx) for change management. 

## Prompting best practices

Consider the following best practices for writing effective [Assist](./index.mdx) prompts.

### Reference content from your organization

You can **@** mention resources, code, and components in your prompt. For example: `Update the data source for @supportTable to utilize the @Customer Support Tickets Database resource.`

### Give as much context as possible

Be as specific as you can about the type of app you want to create or the edits you want to make. The more detail you include about what you’re trying to build, the better Assist can do on the first pass. For example, instead of `Build me a dashboard of movies`, try `Build me a dashboard of movies released in the 1980s with search and filters by genre. I want to be able to add movies and provide my own rating.`

### Iterate and refine

If the first output isn't exactly what you expected, add more info or adjust your prompt. You can also manually edit your app and come back to Assist to continue where you left off. Working with Assist is designed to be an iterative process.

### Ask Assist to explore

If you're editing an existing app, ask Assist to explore what's currently on the page without making any changes. If you're introducing a new resource, ask Assist to tell you what information is available using that resource.

### Be specific about resource nuances

Databases (Retool Database, Postgres, MySQL, etc.) can be queried or modified using similar patterns, so Assist can reliably generate SQL queries. On the other hand, API request structures and specifications can vary widely between endpoints. Giving details about how your system is configured, or pasting API documentation into your prompt helps Assist understand the structure.

When configuring your resource, be as detailed as possible in the **Description** setting on the **Resource configuration** page. This information is passed to Assist, and it provides valuable context on when or how the resource should be used.

### Use references when available

When you can, paste schema details, API docs, or example data into your prompt. This helps Assist understand the shape of your data and the actions you want to take.

---

## Build custom React components

:::note Custom components libraries

Custom components libraries are distinct from the [legacy custom component feature](../legacy-custom.mdx). Retool does not recommend creating custom components using the legacy feature.

:::

If Retool's built-in components don't work for your use case, you can build custom components in React. Custom components can leverage public or private npm packages, as they are written locally in your development environment and then deployed to Retool. After deploying, you can drag and drop components into apps as you would any other component.

To allow custom components to interact with your Retool app, Retool provides a [TypeScript API](./typescript-api.mdx). This API lets you define events that trigger event handlers and add properties to the Inspector for your component.

Custom components are contained within libraries, and each library has a unique name. Custom components deployed to Retool are automatically shown in the Component Library, in a section that matches the library's label.

## Prerequisites

To build a custom component library, you need:

- [Node.js](https://nodejs.org/) v20 or later installed in your development environment.
- [Admin permissions](../../../../permissions/guides/configure-permission-groups#default-permission-groups) in Retool.
- [Optional] If you are running self-hosted Retool, setting the [`ALLOW_SAME_ORIGIN_OPTION`, and `SANDBOX_DOMAIN` environment variables](../../../../self-hosted/guides/origin-sandbox.mdx) is recommended.

The following steps guide you through the development flow to create and use a custom component. The example component contains a **Name** property and displays a message with the provided name. The code will look something like this:

```javascript
export const HelloWorldComponent: FC = () => {
  // Allows the builder to specify a "name" property on each component they build with.
  // The builder can then pass data from their Retool app into the component by setting a value
  // for the "name" property.
  const [name, setName] = Retool.useStateString({
    name: "name",
  });

  return (
    
      Hello {name}!
    
  );
};
```

After building the component, you can drag it to the canvas like any other component.

## 1. Clone the template repository

Clone the [custom-component-collection-template](https://github.com/tryretool/custom-component-collection-template) GitHub repository.

```bash
git clone https://github.com/tryretool/custom-component-collection-template new-custom-component
```

```bash
git clone git@github.com:tryretool/custom-component-collection-template.git new-custom-component
```

## 2. Install dependencies

Change your directory to `new-custom-component` and run:

```bash
npm install
```
:::tip 

It's possible to use almost any `npm` packages that support being run in the browser, including from private `npm` repositories.

:::

## 3. Log in to Retool

Log in using the following command. You need to provide an API access token with read and write scopes for **Custom Component Libraries**. See the [Retool API authentication documentation](../../../../org-users/guides/retool-api/authentication.mdx#create-an-access-token) for instructions on generating an access token.

```bash
npx retool-ccl login
```

## 4. Create a component library

Create a component library for your component.

```bash
npx retool-ccl init
```

This command guides you through picking a name and description for your library. It then adds those and other metadata to your local `package.json` file and calls Retool to construct the library.

## 5. Rename your component

Rename the `HelloWorld` component as needed. All components exported from `src/index.tsx` are synced to Retool.

## 6. Start dev mode

Use [dev mode](./retool-ccl.mdx#dev-mode) to test changes as you build your component:

```bash
npx retool-ccl dev
```

This syncs changes to Retool every time you change a file. You can then test component changes in any Retool app.

## 7. Add components to the canvas

Select the custom component from the **Add components** panel, and drag it into your app. The name of the custom component is the same as the name of your exported React component. The component is displayed in a section titled with the label of your library. You may need to refresh the page for new components to show up in the **Add components** panel.

## 8. Develop your component

Write code for your component in your preferred editor. See the [TypeScript API](./typescript-api.mdx) section for more information about how to develop your component.

## 9. Deploy your component

When you're done creating your component, deploy it with the following command:

```bash
npx retool-ccl deploy
```

This pushes an immutable version of the component to Retool.

Now your component library is ready for production use. If you want to use your component library in public apps, then you need to go to **Settings > Custom Component Libraries** and set it to be public as well. This will expose the component library to anyone who has access to the public app URL.

If you want to [avoid interactively](./retool-ccl.mdx#non-interactive-mode) providing arguments while running `npx retool-ccl`, you can instead provide them on the command line.

## 10. Switch component versions

To pin your app to the component version you just published, navigate to the **Custom Component** settings in your Retool app and change `dev` to the latest version. This may require you to refresh the page to see the newly published version.

## Examples

Refer to the `custom-component-examples` [GitHub repository](https://github.com/tryretool/custom-component-examples/tree/main/src) for custom component examples.

## Limitations
There are some limitations on how custom component libraries can be used. 

- Custom component libraries aren't supported in Retool Mobile.
- Custom component library descriptions cannot be edited.
- Individual library revisions cannot be larger than 10MB, or 30MB in dev mode.
- Custom component libraries only load JavaScript and CSS files at runtime. Any other file included in the revision bundle is ignored.
- [Node.js](https://nodejs.org/) v20 or later is required for your development environment.
- [Admin permissions](../../../../permissions/guides/configure-permission-groups#default-permission-groups) in Retool are required.
- Custom components must be written in React and Typescript.
- When you [download a page](../../../../queries/guides/generate-pdfs.mdx) in PDF format, custom components are not included.

:::note
For Retool Cloud organizations, there is a 5GB limit on the total size of all custom component libraries within an organization. If you need to deploy new revisions and have reached this limit, you can free up space by visiting the **Custom Components** settings page and deleting some existing libraries.

:::

---

## Use Retool custom component library

When developing custom components, Retool recommends you use the Retool custom component library (`retool-ccl`) utility's `dev` function (dev mode). Dev mode updates your component in Retool as you make edits, which means you don't have to constantly deploy new versions and update your app in Retool. 

Each developer (identified by the access key supplied at `npx retool-ccl login`) has their own `dev` revision of the component library.

## Dev mode
Run the following command to use dev mode. The first time you run this command for a library it creates your `dev` version in Retool.

```bash
npx retool-ccl dev
```
This command continues to run and watch for file changes. Whenever you save your component files, they're automatically reflected in your Retool app. 

:::note

When you're ready to publish an app with a custom component, be sure to change your app to use a published version of the component instead of a `dev` version.

:::

The `dev` mode branch to use is determined by the creator of the access token being used to authenticate with retool-ccl. For example, if user **admin@yourdomain.com** uses an access token they created to sign in with retool-ccl, each time they use dev mode it will create/update the dev mode version on the **admin@.yourdomain.com** branch.

Multiple developers can safely use `dev` mode with the same library concurrently by using separate `dev` mode branches. Ensure that each developer has their own unique access token to work on the same library at the same time and prevent overwriting other changes.

Once you create a `dev` version, you can use it in a Retool app for testing during development of a custom component. You can also switch between any published or `dev` version from **••• App settings** > **Custom Components**. Each `dev` version is listed with the access token creator's email address.

## Update your component

To update your component, run `npx retool-ccl dev`, and then navigate to your app's **Custom Component** settings to change the version back to `dev`. When you're finished, follow the same steps to deploy your component and pin the app to the latest version.

:::warning

To delete a library, sign in to your Retool organization and navigate to **Settings > Custom Component Libraries**. Removing a custom component library deletes all components in the library from your app.
:::

## Debug your components

As you build a custom component, you may need to troubleshoot issues that arise. When using dev mode, Retool includes source maps for your components, so you can use your browser's step-by-step debugging tools to debug any issues that come up.

:::note

You can add the `-v` option to the `deploy` and `sync` commands to print more detailed errors for help with debugging.

:::

## Expand your library

You can add multiple custom components to your library by exporting more React components from `src/index.tsx` (only components exported from this file are detected). You can also create multiple libraries by creating a separate TypeScript project and following the steps in the [Create a custom component](./index.mdx) guide.

## Use custom component libraries across multiple instances or Spaces

If you deploy applications to [multiple instances](../../../../self-hosted/concepts/multi-instance-deployment.mdx) or to [Retool Spaces](../../../../org-users/tutorials/spaces.mdx), additional steps are required to use custom component libraries across them. When deploying the same custom component library to multiple instances or Spaces, choose a primary instance or Space for your components. This is the only instance or Space that you should ever [log in](./index.mdx#3-log-in-to-retool) or [deploy](./index.mdx#9-deploy-your-component) your components to.

To use your components on other instances or Spaces, sync them from your primary instance or Space with the following command:

```bash
npx retool-ccl sync
```

This command guides you through configuring a sync target, and then copies over the library and all available versions. When new versions are available in the primary instance or Space, re-run the sync command to copy them to any others. Any applications that reference these components automatically use the recently synced version.

## Send custom HTTP headers or cookies 

If you have a self-hosted Retool deployment, you can use `npx retool-ccl` to send additional custom headers or cookies with each HTTP request. This can be helpful if there is an application load balancer that:

 - Is between where you develop your custom components and the deployment instance.
 - Blocks HTTP requests that are missing certain authentication headers or cookies. 

To send custom HTTP headers or cookies, use the `--header 'header-name: header_value'` option when using `npx retool-ccl`.

Every command supports this option. You can also include it multiple times for each header to include.

```bash
npx retool-ccl login --header 'header-name: header_value' --header 'header-name-2: header_value_2'
```

When using the `sync` command, include `--target-header` to specify headers for the target instance of Retool. `--header` sends headers to the origin instance of Retool.

```bash
npx retool-ccl sync --header 'origin-header-name: value_1' --target-header 'target-header-name: value_2'
```

You can also send multiple cookies using a header name of `Cookie`.

```bash
npx retool-ccl deploy --header 'Cookie: cookie_one=cookie_value_one; cookie_two=cookie_value_two'
```

## Non-interactive mode

Some uses of `npx retool-ccl` involve asking the user for arguments, such as the access token they want to use. It is possible to instead provide these arguments in the original invocation of the command, if you want to avoid providing them interactively. For example, you can deploy a new revision by running the following command, without needing to run `npx retool-ccl login` first. 

```bash
npx retool-ccl deploy --url http://yourretoolinstance.com --access-token retool_01...8cp
```

Provide access tokens and URLs using the arguments `--access-token` and `--url`. For the `sync` command, you can also use `--target-access-token` and `--target-url` for the target Retool instance.

All arguments can also be provided as environment variables, including access token and URL arguments, using the format `RETOOL_CCL_${argument_name_capitalized}`. For example:

```bash
RETOOL_CCL_URL=http://yourretoolinstance.com RETOOL_CCL_ACCESS_TOKEN=retool_01...8cp npx retool-ccl deploy
```

---

## Use TypeScript API to configure custom components

You can use the TypeScript API to add properties, pass data, and set up event handlers for your custom components. You can also enhance your custom component library, designate a primary instance or Space for deployment, and include custom headers or cookies in self-hosted Retool deployments.

## Add properties to custom components
You can add properties to custom components that pass data to and from the Retool app using the [TypeScript API](#typescript-api). 

```javascript
export const HelloWorldComponent: FC = () => {
  // Allows the builder to specify a "name" property on each component they build with.
  // The builder can then pass data from their Retool app into the component by setting a value
  // for the "name" property.
  const [name, setName] = Retool.useStateString({
    name: "name",
  });

  return (
    
      Hello {name}!
    
  );
};
```

The above component has a `name` property that any builder can then pass data into, like any other Retool component. For example, you can assign the value of `name` to be `{{query[0].name}}`, which sets `name` to be the result of a query in your Retool app. In the below example, `{{query[0].name}}` resolves to `Daniel`, which the custom component then displays.

![Setting name to query[0].name](https://d3399nw8s4ngfo.cloudfront.net/docs/18682e16bb803fb0f728ce286c67047f-1800.webp)

To pass data from the component back to your Retool app, you can use the `setName` method to set the value of `name` from within your custom component, and then reference this value in the rest of your Retool app using `{{yourCustomComponentId.name}}`.

## TypeScript API

Custom components can interact with the Retool app they are added to. They can fetch their state from Retool, set their default size when dragged onto the canvas, or tell Retool that one of their events has fired. There are separate TypeScript methods for each of these actions.

### Pass data into a custom component

The following `useState` functions allow you to pass data from your Retool app into your custom component. Each function is for a different type of data.

```javascript title="Method signature"
/**
  * This method allows you to add boolean state to your component.
  * Like any other component in Retool, custom components can have their own state, which you can then edit using the inspector.
  *
  * @param {string} name The name of the state used internally, and the label that will be used in the Inspector to identify it.
  *   This should be an alphanumerical string with no spaces.
  * @param {boolean} [initialValue] The initial value for the state when the component is dragged onto the canvas.
  * @param {('text' | 'checkbox' | 'hidden')} [inspector] What kind of Inspector will be used when a builder is editing this state.
  * @param {string} [description] What will be displayed in the tooltip of the Inspector for this state.
  * @param {string} [label] An override for the label used in the Inspector for this state.
  *
  * @return {[boolean, (newValue: boolean) => void]} The value of the state, and a function to update it.
  */
function useStateBoolean({
  name,
  initialValue,
  inspector,
  description,
  label,
}: {
  name: string
  initialValue?: boolean
  label?: string
  description?: string
  inspector?: 'text' | 'checkbox' | 'hidden'
}): readonly [boolean, (newValue: boolean) => void]

/**
  * This method allows you to add number state to your component.
  * Like any other component in Retool, custom components can have their own state, which you can then edit using the Inspector.
  *
  * @param {string} name The name of the state used internally, and the label that will be used in the Inspector to identify it.
  *   This should be an alphanumerical string with no spaces.
  * @param {number} [initialValue] The initial value for the state when the component is dragged onto the canvas.
  * @param {('text' | 'hidden')} [inspector] What kind of Inspector will be used when a builder is editing this state.
  * @param {string} [description] What will be displayed in the tooltip of the Inspector for this state.
  * @param {string} [label] An override for the label used in the Inspector for this state.
  *
  * @return {[number, (newValue: number) => void]} The value of the state, and a function to update it.
  */
function useStateNumber({
  name,
  initialValue,
  inspector,
  description,
  label,
}: {
  name: string
  initialValue?: number
  label?: string
  description?: string
  inspector?: 'text' | 'hidden'
}): readonly [number, (newValue: number) => void]

/**
  * This method allows you to add string state to your component.
  * Like any other component in Retool, custom components can have their own state, which you can then edit using the Inspector.
  *
  * @param {string} name The name of the state used internally, and the label that will be used in the Inspector to identify it.
  *   This should be an alphanumerical string with no spaces.
  * @param {string} [initialValue] The initial value for the state when the component is dragged onto the canvas.
  * @param {('text' | 'hidden')} [inspector] What kind of Inspector will be used when a builder is editing this state.
  * @param {string} [description] What will be displayed in the tooltip of the Inspector for this state.
  * @param {string} [label] An override for the label used in the Inspector for this state.
  *
  * @return {[string, (newValue: string) => void]} The value of the state, and a function to update it.
  */
function useStateString({
  name,
  initialValue,
  inspector,
  description,
  label,
}: {
  name: string
  initialValue?: string
  label?: string
  description?: string
  inspector?: 'text' | 'hidden'
}): readonly [string, (newValue: string) => void]

/**
  * This method allows you to add enumeration state to your component. This is state that can have a value drawn from a limited set of strings.
  * Like any other component in Retool, custom components can have their own state, which you can then edit using the Inspector.
  *
  * @param {string} name The name of the state used internally, and the label that will be used in the Inspector to identify it.
  *   This should be an alphanumerical string with no spaces.
  * @param {T} enumDefinition An array of string literals describing the possible enum values. The strings must be alphanumeric with no spaces.
  * @param {T[number]} [initialValue] The initial value for the state when the component is dragged onto the canvas.
  * @param {{[K in T[number]]: string}} [enumLabels] Alternative labels to use for enums when displaying them.
  * @param {('segmented' | 'select')} [inspector] What kind of Inspector will be used when a builder is editing this state.
  * @param {string} [description] What will be displayed in the tooltip of the Inspector for this state.
  * @param {string} [label] An override for the label used in the Inspector for this state.
  *
  * @return {[T[number], (newValue: T[number]) => void]} The value of the state, and a function to update it.
  */
function useStateEnumeration({
  name,
  enumDefinition,
  initialValue,
  enumLabels,
  inspector,
  description,
  label,
}: {
  name: string
  initialValue?: T[number]
  enumDefinition: T
  enumLabels?: {
    [K in T[number]]: string
  }
  inspector?: 'segmented' | 'select' | 'hidden'
  description?: string
  label?: string
}): readonly [T[number], (newValue: T[number]) => void]

/**
  * This method allows you to add serializable object state to your component.
  * Like any other component in Retool, custom components can have their own state, which you can then edit using the Inspector.
  *
  * **NOTE:** Due to the internal mechanism used, the setter will apply new values as a partial update of the current state, similar to `Object.assign()`.
  *
  * @param {string} name The name of the state used internally, and the label that will be used in the Inspector to identify it.
  *   This should be an alphanumerical string with no spaces.
  * @param {SerializableObject} [initialValue] The initial value for the state when the component is dragged onto the canvas.
  * @param {('text' | 'hidden')} [inspector] What kind of Inspector will be used when a builder is editing this state.
  * @param {string} [description] What will be displayed in the tooltip of the Inspector for this state.
  * @param {string} [label] An override for the label used in the Inspector for this state.
  *
  * @return {[SerializableObject, (updates: SerializableObject) => void]} The value of the state, and a function to update it.
  */
function useStateObject({
  name,
  initialValue,
  inspector,
  description,
  label,
}: {
  name: string
  initialValue?: SerializableObject
  inspector?: 'text' | 'hidden'
  description?: string
  label?: string
}): readonly [SerializableObject, (updates: SerializableObject) => void]

/**
  * This method allows you to add serializable array state to your component.
  * Like any other component in Retool, custom components can have their own state, which you can then edit using the Inspector.
  *
  * @param {string} name The name of the state used internally, and the label that will be used in the Inspector to identify it.
  *   This should be an alphanumerical string with no spaces.
  * @param {SerializableArray} [initialValue] The initial value for the state when the component is dragged onto the canvas.
  * @param {('text' | 'hidden')} [inspector] What kind of Inspector will be used when a builder is editing this state.
  * @param {string} [description] What will be displayed in the tooltip of the Inspector for this state.
  * @param {string} [label] An override for the label used in the Inspector for this state.
  *
  * @return {[SerializableArray, (newValue: SerializableArray) => void]} The value of the state, and a function to update it.
  */
function useStateArray({
  name,
  initialValue,
  inspector,
  description,
  label,
}: {
  name: string
  initialValue?: SerializableArray
  inspector?: 'text' | 'hidden'
  description?: string
  label?: string
}): readonly [SerializableArray, (newValue: SerializableArray) => void]
```

```javascript title="Example"
export const HelloWorldComponent: FC = () => {
  // Allows the builder to specify a "name" property on each component they build with.

  const [showBorder, _setShowBorder] = Retool.useStateBoolean({
    name: "showBorder",
    initialValue: false,
    label: "Show Border",
    inspector: "checkbox",
  });

  return (
    
      Hello!
    
  );
};
```

### Trigger event handlers

The `Retool.useEventCallback` method allows you to notify Retool of component events, which can then be used to trigger event handlers within Retool.

```javascript title="Method signature"
/**
  * Defines an event callback for your component. While building with the component you will be able to create event handlers that are triggered
  * whenever this event callback is called.
  *
  * For example, you could create an event callback which is triggered whenever a user clicks on a button in your component.
  *
  * The event cannot contain any data or context.
  *
  * @param {string} name The name of the label that the event callback will be given in the Inspector.
  */
function useEventCallback({ name }: { name: string }): () => void

```

```javascript title="Example usage"
export const ButtonComponent: FC = () => {
  // Allows the custom component to inform Retool when a click event
  // happens on the component. You can then add event handlers to any ButtonComponent
  // that is triggered when onClick is called.
  const onClick = Retool.useEventCallback({ name: "click" });

  return (
    
       A button 
    
  );
};
```

#### Passing data with an event

Data from a custom component can't be included inside an event. To access this kind of data in an event handler, set state in the component before firing the event (in this example `text`), and then access that state as you normally would in the event handler.

```javascript title="Accessing component data in an event handler"
export const CustomInput: FC = () => {
  const [text, setText] = Retool.useStateString({
    name: "text",
    inspector: "hidden",
  });

  const clickEvent = Retool.useEventCallback({ name: "click" });
  return (
    
       {
          setText(change.target.value);
        }}
      />
       clickEvent()}>submit
    
  );
};
```

In your event handler, you can then access the value of `{{componentId.text}}`.

![Event handler that references the text property](https://d3399nw8s4ngfo.cloudfront.net/docs/929f79aa890fc2f3089465aeb03045cd-1800.webp)

### Set the component size

`Retool.useComponentSettings` allows you to set the size of the component when it is first dragged onto the canvas.

```javascript title="Method signature"
 /**
  * Allows configuration of various settings on your component.
  *
  * @param {string} defaultWidth Sets the default width in columns of the component when you drag it onto the canvas.
  * @param {string} defaultHeight Sets the default height in rows of the component when you drag it onto the canvas.
  */
function useComponentSettings({
  defaultWidth,
  defaultHeight,
}: {
  defaultWidth?: number
  defaultHeight?: number
}): void
```

```javascript title="Example usage"
export const HelloWorldComponent: FC = () => {
  // Sets the default height and width when HelloWorldComponent is
  // dragged onto the canvas.
  // dragged onto the canvas. Note that the rows are much shorter than the columns.
  Retool.useComponentSettings({
    defaultHeight: 50,
    defaultWidth: 5,
  });

  return Hello!;
};
```

---

## Getting started with the IFrame component

The [IFrame component](../../reference/components/custom/iframe.mdx) provides a content area in which you can embed a web page via URL.

import WebPlayground from '/docs/_partials/_components/_web-playground.mdx';
 

:::note

Use an [HTML](../../reference/components/custom/html.mdx) component to include custom HTML and CSS instead of an embedded web page. You can also use a [Custom Component](./custom-component-libraries/index.mdx) to include custom React, HTML, and JavaScript.

:::

## Features

Key features of the IFrame component include:

- Permissions to allow or deny downloads.
- Form submissions.
- Microphone and camera access.
- Customizeable style.

## Specify content options

The **Content** section of the Inspector contains settings that control the content of the IFrame component. Add the web page **URL**, and configure tooltips in the **Add-ons** section.

You can use embedded expressions to dynamically populate all or part of the URL. For example, you could use an IFrame component to display the Retool Self-hosted release documentation. If you had a Select component that enabled a customer to select "Stable" or "Edge", you could set the IFrame component **URL** to `https://docs.retool.com/releases/{{ select1.selectedItem.value }}`.

### Set HTTP headers

If you are embedding a web page that you host yourself, you might need to set [HTTP headers](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers) in order for your content to appear properly.

Retool recommends applying the following settings:

```
Access-Control-Allow-Origin: https://.retool.com;
Content-Security-Policy: frame-ancestors https://.retool.com;
```

## Configure user interaction

The **Interaction** section of the Inspector contains settings that control user interaction. The IFrame component does not support [event handlers](../interaction-navigation/event-handlers.mdx), but it supports a variety of interaction options that you can turn on or off depending on your needs.

import FxSvg from '/img/icons/fx.svg';

To dynamically set these values, toggle  next to the checkbox and use JavaScript to set a truthy value.

Many sites require the **Storage and cookies** setting to be enabled. Toggling on this component enables the `allow-same-origin` flag on the IFrame component. If your deployment is self-hosted, you must also [configure your environment variables appropriately](../../../self-hosted/guides/origin-sandbox.mdx#update-environment-variables).

:::note

If your web page requires authentication, Retool cannot handle this authentication process. You can allow authentication via URL (using a temporary password), or users must authenticate the web page inside the component.

:::

import Appearance from "/docs/_shared/_appearance.mdx";

**Show top bar** displays the title of the IFrame component and UI controls that refresh the page or open it in full screen.

:::note

The **Fullscreen** and **Top level navigation** settings must also be enabled in order for the top bar UI controls to function.

:::

---

## Custom and embedded content



---

## Configure custom keyboard shortcuts for web apps

In addition to Retool's built-in [keyboard shortcuts](../../reference/keyboard-shortcuts.mdx), you can define custom keyboard shortcuts on a per-app basis. Custom shortcuts can be a single character, combination of keys, or a sequence of key presses. You configure the action to trigger with [JavaScript](../../../queries/quickstart.mdx) and can use many of the available methods in Retool to control components, queries, and more.

Click the gear icon in the left panel to open the **App Settings** tab and select **Custom shortcuts**.

### Define shortcut keys

To add a shortcut, navigate to the [App Inspector](../../concepts/ide.mdx#app-inspector) and click **+** in the **Custom shortcuts** section. Specify the shortcut keys to use and the JavaScript to run.

export function CustomShortcut() {
  return (
    
      
    
  )
}

#### Available shortcut keys

A custom shortcut can include almost any keyboard character or symbol, such as a or $, and optional modifier keys, such as ctrl.

:::note

Shortcuts that use a single uppercase letter or secondary character, such as `A` or `@`, automatically require Shift—you don't need to include it in the shortcut.

:::

You can include modifier keys in custom shortcuts. Modifier keys are commonly used for keyboard shortcuts but are not required. Modifiers, named keys, and reserved symbols must be explicitly named when used in a shortcut.

| Modifier    | Key                                              |
| ----------- | ------------------------------------------------ |
| `shift`     | Shift (PC and Mac)                    |
| `ctrl`      | ctrl (PC and Mac)                     |
| `alt`       | Alt (PC) or Option (Mac)   |
| `mod`       | ctrl (PC) or Command (Mac) |
| `command`   | Command (Mac only)                    |
| `super`     | Windows (PC only)                     |
| `backspace` | Backspace (PC and Mac)                |
| `space`     | Space (PC and Mac)                    |
| `esc`       | Escape (PC and Mac)                   |
| `plus`      | + (PC and Mac)                        |

Certain modifiers automatically change based on the user's platform (e.g., `mod`) or you can explicitly define modifiers be the same (e.g., `ctrl`).

#### Key combination or sequence

You can define a combination or sequence of keys to trigger a shortcut.

- **Combination**: Separate the keys with `+` (e.g., `alt+5`). You must press the keys simultaneously to trigger the shortcut. Key combinations do not require a modifier key and you could use a combination of character keys.
- **Sequence**: Separate keys with a space (e.g., `w a s d`). You must press the defined keys in sequential order to trigger the shortcut.

:::note

Sequence or combination shortcuts that contain Command or ctrl will apply anywhere in an app. Other modifier keys such as Shift, Alt, or Option will not trigger inside inputs, as they are needed for special characters and capitalization.

:::

### Configure JavaScript actions

You can [write JavaScript](../../../queries/quickstart.mdx) actions for custom shortcuts that interact with almost every aspect of an app. The following examples trigger actions using Retool's built-in [JavaScript utility methods](../../../queries/reference/libraries/utils.mdx).

| Keys     | JavaScript                                                 | Shortcut                                                                              |
| -------- | ---------------------------------------------------------- | ------------------------------------------------------------------------------------- |
| `j`      | `utils.confetti()`                                         | j to show confetti.                                                        |
| `J`      | `utils.showNotification({title: 'Hello, World!'})`         | Shift j to display a notification.                              |
| `ctrl+j` | `utils.copyToClipboard(moment().format('dddd'))`           | ctrl j to copy the current day to the clipboard.                |
| `r e`    | `utils.openUrl('https://retool.com', { newTab: 'true', })` | r then e to open [retool.com](https://retool.com) in a new tab. |

You can also use write shortcuts with complex logic to conditionally perform actions or chain together multiple actions. For example, you can define a single shortcut to cycle through all views in a Tabbed Container component.

```javascript title="Cycle through views"
tabbedContainer1.views.length === tabbedContainer1.currentViewIndex + 1
  ? tabbedContainer1.setCurrentViewIndex(0)
  : tabbedContainer1.showNextVisibleView();
```

---

## Build custom components

:::danger Use custom component libraries

This method for building custom components is deprecated. Use [custom component libraries](./index.mdx) feature for developing custom components.

:::

If Retool's built-in components don't work for your use case, you can build your own custom component in React or JavaScript.

:::note You may not need a custom component

Check Retool's [Component Library](https://retool.com/components) before you start to build a custom component to see if any existing components are suitable. If your component serves a common use case, consider submitting a feature request to Retool.

:::

## Overview

A custom component consists of:

- A place to put the HTML, CSS, and JavaScript which governs the appearance and behavior of the component. You enter this in the **IFrame Code** field of the Inspector, and Retool puts this code in an iFrame in the outer Retool app.
- An interface for passing data back and forth between the Retool app and the custom component code.

You can also enable additional custom component features, such as the ability for your component to download files, in the **Interaction** menu of the Inspector.

## Interface

The following methods and objects are used to pass data and state between your Retool app and your custom component. These methods are available for both React and non-React JavaScript custom components.

| Name           | Type        | Parameter | Description                                                                                                                                                                                                                                                                                           |
| -------------- | ----------- | --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `triggerQuery` | function    | string    | Triggers the specified Retool query.                                                                                                                                                                                                                                                                  |
| `model`        | JSON object | N/A       | Represents the data to pass from your app to your custom component.                                                                                                                                                                                                                                   |
| `modelUpdate`  | function    | object    | Updates the custom component's `model` to the specified fields. This allows the custom component to update its own state, which is available to the rest of the Retool app. You can think of `modelUpdate` as analogous to [setState in React](https://react.dev/reference/react/Component#setstate). |

## Connect interface for React components

Connect your React component to Retool by calling `Retool.connectReactComponent(customComponentName)`. This exposes the `triggerQuery`, `model`, and `modelUpdate` variables. You can then access them in your component.

## Connect interface for non-React components

For non-React components, Retool also exposes the `subscribe` method, in addition to `triggerQuery`, `model`, and `modelUpdate`.

To access `triggerQuery`, `modelUpdate`, and `subscribe`, use the `window.Retool` prefix.

```javascript
function buttonClicked() {
  window.Retool.triggerQuery("query1");
}
```

You can only access `model` and its keys within `window.Retool.subscribe`.

```javascript
window.Retool.subscribe(function (model) {
  console.log(model.name);
});
```

## Minimum custom component with React

`boilerplate.html` below shows the minimum code needed to create a functional custom component with React.

```html title="boilerplate.html"

  const MyCustomComponent = ({ triggerQuery, model, modelUpdate }) => (
    Hello, Retool!
  );
  const ConnectedComponent = Retool.connectReactComponent(MyCustomComponent);
  const container = document.getElementById("react");
  const root = ReactDOM.createRoot(container);
  root.render();

```

## Minimum custom component with JavaScript

`boilerplate_basic.html` shows an example of a custom component without React.

```html title="boilerplate_basic.html"

  
    
      function updateName(e) {
        window.Retool.modelUpdate({ 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 || "";
      });
    
    
    Trigger Query
    
  

```

```json title="model.json"
{
  "name": "Default name"
}
```

## Pass data to your custom component

To pass the data from components or queries to your custom component, set the data as the value of the custom component object in the **Model** field. In the following example, `model.name` updates whenever the value of `textInput1` updates.

```html title="inflow.html"

  const MyCustomComponent = ({ triggerQuery, model, modelUpdate }) => (
    Hello, {model.name}!
  );
  const ConnectedComponent = Retool.connectReactComponent(MyCustomComponent);
  const container = document.getElementById("react");
  const root = ReactDOM.createRoot(container);
  root.render();

```

```json title="model.json"
{
  "name": {{textInput1.value ? textInput1.value : 'World'}}
}
```

## Pass data to your app from your custom component

You can also access data from your custom component in other components and queries in your app. In the following example code, `modelUpdate` sets the value of `model.name` to the custom component's `Input` value when it updates, similar to `setState` in React. The value can be accessed in your app using `{{ customComponent.model.name }}`.

```html title="outflow.html"

  body {
    border: 5px solid red;
  }
  #react {
    display: flex;
    justify-content: center;
    align-items: center;
  }

  const { Input } = window["material-ui"];
  const MyCustomComponent = ({ triggerQuery, model, modelUpdate }) => (
     modelUpdate({ name: e.target.value })}
    />
  );
  const ConnectedComponent = Retool.connectReactComponent(MyCustomComponent);
  const container = document.getElementById("react");
  const root = ReactDOM.createRoot(container);
  root.render();

```

## Trigger queries from your custom component

Use the `triggerQuery` function to trigger a query from your custom component.

In the following example, `searchProducts` is triggered when the user clicks the Search button and optionally enters a search term, handled by the `onClick` event listener of the `Button` component. The component's `model` and `modelUpdate` default to `%` in order to return all products when no search term is present.

```html title="triggerquery.html"

  body {
    border: 5px solid red;
  }
  #react {
    display: flex;
    justify-content: center;
    align-items: center;
    height: 100%;
  }
  .button {
    margin-left: 1em;
  }

  

  const { Input, Button } = window["material-ui"];
  const MyCustomComponent = ({ triggerQuery, model, modelUpdate }) => (
    
      
          modelUpdate({ name: e.target.value ? e.target.value : "%" })
        }
      />
       triggerQuery("searchProducts")}
      >
        Search
      
    
  );
  const ConnectedComponent = Retool.connectReactComponent(MyCustomComponent);
  const container = document.getElementById("react");
  const root = ReactDOM.createRoot(container);
  root.render();

```

```sql title="searchProducts.sql"
SELECT
  *
FROM
  products
WHERE
  name ILIKE {{ '%' + customcomponent1.model.name + '%' }}
```

```json title="model.json"
{
  "name": "%"
}
```

## Quirks vs no-quirks rendering 

Custom components are rendered inside of an iframe. When HTML documents, such as iframes, are rendered in a browser, this is done using [quirks or no-quirks mode](https://developer.mozilla.org/en-US/docs/Web/HTML/Quirks_Mode_and_Standards_Mode). The rendering behavior is slightly different with each mode.

Custom components are rendered in no-quirks mode by default. You can switch between modes on a per-custom component basis by toggling "Enable quirks rendering mode" in the IDE.

---

## Getting started with charts

:::info

This guide covers a set of new chart components, which are distinct from the legacy [Chart](./legacy/charts.mdx) component. The new chart components are available on Retool Cloud and on Self-hosted Retool version 3.111 and later.

:::

Chart components allow you to visualize data as graphs and charts. There are various types of charts and graphs available, and each type offers customization options. 

Retool also provides a Plotly JSON Chart that you can customize to fit your needs. All charts are built using [Plotly.js](https://plotly.com/javascript/) version 2.34.

## Chart types

Retool provides the following chart types:

- [Bar Chart](../../reference/components/charts/bar-chart.mdx)
- [Bubble Chart](../../reference/components/charts/bubble-chart.mdx)
- [Funnel Chart](../../reference/components/charts/funnel-chart.mdx)
- [Heat Map](../../reference/components/charts/heat-map.mdx)
- [Line Chart](../../reference/components/charts/line-chart.mdx)
- [Mixed Chart](../../reference/components/charts/mixed-chart.mdx)
- [Pie Chart](../../reference/components/charts/pie-chart.mdx)
- [Plotly JSON Chart](../../reference/components/charts/plotly-json-chart.mdx)
- [Sankey Chart](../../reference/components/charts/sankey-chart.mdx)
- [Scatter Chart](../../reference/components/charts/scatter-chart.mdx)
- [Sparkline](../../reference/components/charts/sparkline.mdx)
- [Stacked Bar Chart](../../reference/components/charts/stacked-bar-chart.mdx)
- [Sunburst Chart](../../reference/components/charts/sunburst-chart.mdx)
- [Treemap](../../reference/components/charts/treemap.mdx)
- [Waterfall Chart](../../reference/components/charts/waterfall-chart.mdx)

## Specify content options 

In the **Content** section of the Inspector, add your data source. Series-based charts enable you to add one or more series, each with a different data source. Series-based charts include Bar Chart, Bubble Chart, Funnel Chart, Line Chart, Scatter Chart, Sparkline, and Waterfall Chart.

### Choose your data

You can use [queries](../../../queries/index.mdx), [transformers](../../../queries/guides/transformers.mdx), or [JavaScript](../../../queries/concepts/javascript.mdx) to build charts.

You can choose to manually add your data, or select a query or transformer in the **Data source** field, which automatically populates the chart with data.

Once you select the data source, you can choose what fields to display. Some charts have **X data** and **Y data**, and others have **Labels** and **Values**.

### Group and aggregate data

Retool supports two transformation types:

- **Group by**: Group the data by a specific dimension.
- **Aggregation**: Summarize the y-values for a specific x-value.

:::note

These transformations are available for series-based charts only.

:::

#### Group by

You can aggregate serial data for a dynamic number of groups. For example, you can create charts that visualize data over time and group that data, such as grouping revenue by product or headcount by team. Use the **Group by** property to choose a field from your data source to group and display this kind of data.

To see an example of this, create a JavaScript [transformer](../../../queries/guides/transformers.mdx) using the code below, which represents a list of employee expenses. Make sure to save and run it before continuing.

```javascript
const data = [
  { "Name": "David Wilson", "Category": "Travel", "Amount": 450.00, "Date": "2024-09-02" },
  { "Name": "Sarah Johnson", "Category": "Technology", "Amount": 475.00, "Date": "2024-09-05" },
  { "Name": "James Moore", "Category": "Meals", "Amount": 56.00, "Date": "2024-09-06" },
  { "Name": "Emily Davis", "Category": "Travel", "Amount": 300.00, "Date": "2024-09-11" },
  { "Name": "James Moore", "Category": "Travel", "Amount": 275.00, "Date": "2024-09-22" },
  { "Name": "Daniel Lee", "Category": "Meals", "Amount": 60.00, "Date": "2024-09-22" },
  { "Name": "Laura Taylor", "Category": "Technology", "Amount": 250.00, "Date": "2024-09-24" },
  { "Name": "James Moore", "Category": "Meals", "Amount": 45.00, "Date": "2024-09-30" },
  { "Name": "Laura Taylor", "Category": "Meals", "Amount": 85.00, "Date": "2024-09-31" }
];

return data;
```

Add a Line Chart component to the canvas. Select the primary series, and set the **Data source** to the transformer you created. Set the **X data** to `Date` and the **Y data** to `Amount`. Set **Group by** to `Category`. The chart now shows three lines, one for each of the categories. If you add more data points to the transformer, the chart updates automatically.

#### Aggregation

In addition, you can use the following methods to aggregate data when you have multiple Y values for a single X data point.

| Method             | Description                                            |
| ------------------ | ------------------------------------------------------ |
| Sum                | Returns the summation of all numeric values.           |
| Average            | Returns the average of all numeric values.             |
| Count              | Returns the quantity of items for each group.          |
| Count distinct     | Returns the quantity of distinct items for each group. |
| Min                | Returns the minimum numeric value for each group.      |
| Max                | Returns the maximum numeric value for each group.      |
| Median             | Returns the median of all numeric values.              |
| Mode               | Returns the mode of all numeric values.                |
| Standard deviation | Returns the standard deviation of all numeric values.  |
| First value        | Returns the first numeric value for each group.        |
| Last value         | Returns the last numeric value for each group.         |

:::note

You can edit the `data` value for a Plotly JSON Chart directly to use the `filter` and `sort` transformation methods.

:::

### Include add-ons

The **Add-ons** setting enables you to add additional information to your chart component.

Common add-ons include:

- **Title**: Add a title that appears at the top of the chart.
- **Toolbar**: Add a configurable list of controls that allow a user to interact with the chart by selecting components, zooming in or out, and other functions.
- **Legend**: Add a legend to the chart and configure where the legend appears.
- **Data labels**: For Pie Chart, Treemap, and Sunburst Chart, add labels to display on categories and subcategories.
- **Tooltip**: For Pie Chart, Treemap, and Sunburst Chart, add labels that appear when you hover over categories and subcategories.

:::note

Each component has a slightly different set of add-ons.

:::

### Axis tick formats

import FxSvg from '/img/icons/fx.svg';

Use the **Axis controls** setting to customize tick labels. Click on the axis you want to edit, and Retool provides various appearance settings to customize the ticks. For full control over the tick labels, click the  button and input a valid d3-format specifier. 

Refer to [d3-time-format](https://github.com/d3/d3-time-format#locale_format) for a list of date-time formatters and [d3-format](https://github.com/d3/d3-format#api-reference) for a list of numerical formatters. Here are a few examples of commonly used formats.

| Specifier | Example                    | Description                                                              |
| --------- | -------------------------- | ------------------------------------------------------------------------ |
| `%I:%M%p` | 12:00 PM                   | Uses `%I` for 12-hour clock hour, `%M` for minute and `%p` for AM or PM. |
| `Q%q %Y`  | Q2 2020                    | Uses `%q` for quarter of the year and `%Y` for the year with century.    |
| `%`       | _Input_: 0.05 _Output:_ 5% | `%` multiplies by 100, and then decimal notation with a percent sign.    |

### Add multiple y-axes

Series-based charts support multiple y-axes. This is useful when working with both absolute numbers and percentages, you might want to show additional y-axes on your chart. For example, you could chart the total number of visits to a website, and the percentage of those visits split between new and existing users. The left y-axis would map to a count, and the right one would map to a percentage.

The Mixed Chart component automatically includes two y-axes, with some sample data to demonstrate the abilities of the chart.

Alternatively, you can add an additional y-axis to other kinds of series-based charts using the **Axis controls** section of the inspector. Click **+** to add a right y-axis to a chart, and select the new axis to edit axis-specific settings.

## Configure user interaction

When you select points on a chart, Retool exposes the underlying data in the `.selectedPoints` array property on the chart. This means you can filter, transform, or update your app by referencing the `chartName.selectedPoints` array property in any component, query, or JavaScript function.

For example, to use the `x` values of selected points on a Scatter Chart to filter a query, you can write `{{ scatterChart1.selectedPoints.map(d => d.x) }}`. Here is an example in a [BigQuery](../../../data-sources/guides/integrations/database/bigquery.mdx) SQL query:

```sql title="BigQuery"
SELECT id, event, timestamp, value, trigger
FROM big_query.event_table
WHERE timestamp IN UNNEST( {{chart.selectedPoints.map(d => d.x) }})
```

Retool also exposes event handlers on charts for the following events:

- Select
- Clear
- Legend click
- Legend double click
- Hover
- Unhover

import Appearance from "/docs/_shared/_appearance.mdx";

## Advanced use cases

A few more advanced use cases require additional configuration or knowledge of Plotly.

### Plotly JSON Chart

If you have an advanced use case that isn't covered by one of the chart presets, you can use the **Plotly JSON Chart** component to build charts with raw Plotly JSON structured data. Retool exposes chart information (chart type, aggregation methods, axes names, etc.) as JSON passed to the `data` and `layout` properties. 

:::note

Retool provides a repository of [data visualization examples](https://github.com/tryretool/data_visualizations) on GitHub that you can reference and duplicate.

:::

The **Data** setting in Retool populates the Plotly `data` property. Use this to define a list of traces (datasets) to chart. The schema of each trace is dependent on the type.

In the **Appearance** section, the **Layout** setting in Retool populates the Plotly `layout` property. Use this property to control positioning and to set the title, axes, colors, and legend.

Visit the [Plotly API reference](https://plotly.com/javascript/reference/index/) to read more about the data structure that the Plotly library operates on.

The following examples show the **Data** and **Layout** values for a bar chart.

Common **Data** options:

```json
[
  {
    "name": "id",
    "x": ["2020-01-01","2020-01-02","2020-01-03","2020-01-04","2020-01-03"],
    "y": [4,8,3,12,7],
    "type": "bar",

    "marker": { // Used to customize how data points are displayed on chart.
      "color": "#3c92dc",
      "size": 3,
     "symbol": "circle"
    },
    "line": {  // Used to customize how data lines are drawn on chart.
      "color": "#3c92dc",
      "width": 3
    },
    "transforms": [
      {
        "type": "aggregate",
        "groups": ["2020-01-01","2020-01-02","2020-01-03","2020-01-04","2020-01-03"],
        "aggregations": [
          {
            "target": "y",
            "func": "sum",
            "enabled": true
          }
        ]
      }
    ]
  }
]

```

Common **Layout** options:

```json
{
  "title": "My Chart Title",
  "font": {
    "family": "Inter" // Default font for all chart labels.
  },
  "xaxis": {
    "title": "Date", // Text label for x-axis.
    "type": "-", // X-axis type.
    "tickformat": "YYYY-MM", // d3-format specifier. If empty or "", Plotly will attempt to guess format.
    "automargin": true,
    "fixedrange": true
  },
  "yaxis": {
    "title": "Count",
    "tickformat": "", // d3-format specifier. If empty or "" Plotly will attempt to guess format.
    "automargin": true,
    "fixedrange": true
  },
  "showlegend": true,
  "legend": {
    "xanchor": "left",
    "x": 1.05,
    "y": 0.5
  },
  "hovermode": "x unified",
  "margin": {
    "l": 75,
    "r": 25,
    "t": 80,
    "b": 75
  }
}
```

Paste these values into the Plotly JSON Chart component to test it out.

---

## Data



---

## Getting started with the Key Value component

Key Value components display key-value data and are often used to display data sourced from other components. For example, it's common to configure a Key Value component to display detailed information from a selected row in a table. Try out the app below for an example.

## Display key-value pairs

Retool seeds Key Value components with demo data when you add them to the canvas. You can also set the component's **Data** to any JavaScript object. This includes query results, table data, or JSON data.

## Edit properties and appearance

After you pass data to the Key Value component, Retool infers properties and their formats. These properties are initially listed in the **Dynamic** section, and you can save properties to customize and reorder them. You can also hide properties, configure their format, change their labels, and more.

There are also several appearance settings to choose from. You can change the label position, group layout, height, etc.

---

## Visualize data with the Chart component

:::info

This is a legacy component. Retool recommends using the [new chart components](../charts.mdx).

:::

The [Chart component](https://retool.com/components/chart) allows you to visualize data as graphs and charts. It's built using [Plotly.js](https://plotly.com/javascript/), so you can also customize charts to fit your needs.

## Demo

Check out the demo to see the Chart component in action.

## Choose your data

You can use [queries](//queries) and [transformers](../../../../queries/guides/transformers.mdx), or [JavaScript](../../../../queries/concepts/javascript.mdx) to build charts. You can reference an array or an object in the **Data source** field for a chart and Retool automatically detects the data type and transforms it for graphing. If your data source is an array and you enable **Use JavaScript**, Retool's built-in `formatArrayAsObject` function wraps the data source reference.

After you select a data source, Retool parses the data and populates the chart. By default, the first non-numeric column populates the **X-axis** but you can change this and other properties like **Chart type**, **Group by**, etc.

## Supported data structures

The Chart component requires data in a tabular format. This can either be an array of objects, where every object field is a column, or as an object where each key represents a column and the value is an array of values for that column.

```json title="Array of objects: typical data response from an API"
[
  { "date": "2022-03-01", "animal": "dog", "count": 4 },
  { "date": "2022-03-01", "animal": "cat", "count": 6 },
  { "date": "2022-04-01", "animal": "frog", "count": 10 },
  { "date": "2022-04-01", "animal": "cat", "count": 3 },
  { "date": "2022-04-01", "animal": "dog", "count": 2 }
]
```

```json title="Object with array fields: typical data  format returned by a SQL query"
{
  "date": [
    "2022-03-01",
    "2022-03-01",
    "2022-04-01",
    "2022-04-01",
    "2022-04-01"
  ],
  "animal": ["dog", "cat", "frog", "cat", "dog"],
  "count": [4, 6, 10, 3, 2]
}
```

## Group data

One common use case is to chart time series data for a dynamic number of groups. For example, grouping revenue by product or headcount by team. Use the **Group by** property to choose a field from your data source to group and display this kind of data.

To see an example of this, create a transformers using the code below. Make sure to save and run it before continuing.

```javascript
const data = [
  { date: "2022-03-01", animal: "dog", count: 1 },
  { date: "2022-03-01", animal: "cat", count: 6 },
  { date: "2022-04-01", animal: "frog", count: 10 },
  { date: "2022-04-01", animal: "cat", count: 3 },
  { date: "2022-04-01", animal: "dog", count: 2 },
  { "date": "2022-03-01", "animal": "bird", "count": 4 },
  { "date": "2022-04-01", "animal": "bird", "count": 2 },
  { "date": "2022-03-01", "animal": "frog", "count": 6 },
  { date: "2022-05-01", animal: "dog", count: 7 },
  { date: "2022-05-01", animal: "cat", count: 4 },
  { date: "2022-05-01", animal: "frog", count: 1 },
  { "date": "2022-05-01", "animal": "bird", "count": 8 },
];

return data;
```

Add a Chart component to the Canvas and set its **Data source** to the transformer you created. Change the chart type to **Line chart** and set **Group by** to `animal`. The chart now shows the number of animals over time. If you add more data points to the transformer, the chart updates automatically.

## Transformation types

Retool supports two transformation types:

- **Group by**: Group the **data** by a specific column.
- **Aggregation**: Summarize the values for a group.

  You can use the following methods to aggregate data.

| Method | Description                                           |
| ------ | ----------------------------------------------------- |
| count  | Returns the quantity of items for each group.         |
| sum    | Returns the summation of all numeric values.          |
| avg    | Returns the average of all numeric values.            |
| median | Returns the median of all numeric values.             |
| mode   | Returns the mode of all numeric values.               |
| rms    | Returns the rms of all numeric values.                |
| stddev | Returns the standard deviation of all numeric values. |
| min    | Returns the minimum numeric value for each group.     |
| max    | Returns the maximum numeric value for each group.     |
| first  | Returns the first numeric value for each group.       |
| last   | Returns the last numeric value for each group.        |

:::note

You can also edit the Plotly JSON for a Chart component directly to use the `filter` and `sort` transformation methods.

:::

## Customize the layout

Edit the chart's layout using the **Interaction** section in the right panel. Select **UI Form** to edit these settings directly or **Plotly JSON** to configure the layout using [Plotly layout JSON](https://plotly.com/javascript/reference/layout/) values.

### Axis tick formats

Use the **X-axis tick format** and **Y-axis tick format** fields to customize tick labels using a valid d3-format specifier. For a full list of numerical values, use d3-format specifier. See [d3-time-format](https://github.com/d3/d3-time-format#locale_format) for a list of date-time formatters and [d3-format](https://github.com/d3/d3-format#api-reference) for a list of numerical formatters. Here are a few examples of commonly used formats.

| Specifier | Example                    | Description                                                              |
| --------- | -------------------------- | ------------------------------------------------------------------------ |
| `%I:%M%p` | 12:00 PM                   | Uses `%I` for 12-hour clock hour, `%M` for minute and `%p` for AM or PM. |
| `Q%q %Y`  | Q2 2020                    | Uses `%q` for quarter of the year and `%Y` for the year with century.    |
| `%`       | _Input_: 0.05 _Output:_ 5% | `%` multiplies by 100, and then decimal notation with a percent sign.    |

## Add interactivity

When you select points on a chart, Retool exposes the underlying data in the `.selectedPoints` array property on the chart. This means you can filter, transform, or update your app by referencing the `chart.selectedPoints` array property in any component, query, or JavaScript function.

For example, to use the `x` values of selected points to filter a query, you can write `{{ chart.selectedPoints.map(d => d.x) }}`. Here is an example in a [BigQuery](../../../../data-sources/guides/integrations/database/bigquery.mdx) SQL query:

```sql title="BigQuery"
SELECT id, event, timestamp, value, trigger
FROM big_query.event_table
WHERE timestamp IN UNNEST( {{chart.selectedPoints.map(d => d.x) }})
```

## Advanced use cases

Retool exposes chart information (chart type, aggregation methods, axes names, etc.) as JSON passed to the `data` and `layout` attributes. If you have an advanced use case, you can enable **Plotly JSON** to edit JSON directly.

The **Data** field in Retool populates the Plotly `data` attribute. Use this attribute to define a list of traces (datasets) to chart. The schema of each trace is dependent on the type.

The **Layout** field in Retool populates the Plotly `layout` attribute. Use this attribute to control positioning and to set the title, axes, colors, and legend.

Visit the [Plotly docs](https://plotly.com/javascript/reference/index/) to read more about the data structure that the Plotly library operates on.

Common `data` options:

```json
[
  {
    "name": "id",
    "x": ["2020-01-01","2020-01-02","2020-01-03","2020-01-04","2020-01-03"],
    "y": [4,8,3,12,7],
    "type": "bar",

    "marker": { // Used to customize how data points are displayed on chart.
      "color": "#3c92dc",
      "size": 3,
     "symbol": "circle"
    },
    "line": {  // Used to customize how data lines are drawn on chart.
      "color": "#3c92dc",
      "width": 3
    },
    "transforms": [
      {
        "type": "aggregate",
        "groups": ["2020-01-01","2020-01-02","2020-01-03","2020-01-04","2020-01-03"],
        "aggregations": [
          {
            "target": "y",
            "func": "sum",
            "enabled": true
          }
        ]
      }
    ]
  }, ...}
]

```

Common `layout` options:

```json
{
  "title": "My Chart Title",
  "font": {
    "family": "Inter" // Default font for all chart labels.
  },
  "xaxis": {
    "title": "Date", // Text label for x-axis.
    "type": "-", // X-axis type.
    "tickformat": "YYYY-MM", // d3-format specifier. If empty or "", Plotly will attempt to guess format.
    "automargin": true,
    "fixedrange": true
  },
  "yaxis": {
    "title": "Count",
    "tickformat": "", // d3-format specifier. If empty or "" Plotly will attempt to guess format.
    "automargin": true,
    "fixedrange": true
  },
  "showlegend": true,
  "legend": {
    "xanchor": "left",
    "x": 1.05,
    "y": 0.5
  },
  "hovermode": "x unified",
  "margin": {
    "l": 75,
    "r": 25,
    "t": 80,
    "b": 75
  }
}
```

### Add multiple y-axes

If you're working on a chart that shows absolute numbers and percentages, you might want to show additional y-axes on your chart. For example, you could chart the total number of visits to a website, and the percentage of those visits split between new and existing users. The left y-axis would map to a count, and the right one would map to a percentage. You can do this by editing the Plotly JSON directly with the following steps.

#### 1. Add another y-axis to the Plotly Layout JSON

Select the chart and click **Plotly JSON** in the right panel. In the existing JSON, there's an entry for an existing y-axis with a `"yaxis"` key. Add a second entry named `"yaxis2"`, and configure it to show up on the right side of the graph using `side: right` and `overlaying: y`.

```json title="y axis JSON"
  "yaxis2": {
    "title": {
      "text": "an axis title",
      "standoff": 12,
      "font": {
        "size": 12
      }
    },
    "overlaying": "y",
    "side": "right",
    "type": "linear",
    "automargin": true,
    "fixedrange": true,
    "zerolinecolor": "#DEDEDE"
  }
```

You can also add some optional fields to format the axis as a percentage and restrict the range to `[0,1]`.

```json
"tickformat": "%",
"range": [0,1],
```

#### 2. Assign datasets to the axes

For each dataset that you want to assign to the additional y-axis, add this property:

```json
"yaxis": "y2"
```

## Additional resources

Check out these external resources for more information on building charts and using Plotly.

- [Data Visualization examples](https://github.com/tryretool/data_visualizations) on GitHub
- [Plotly.js](https://plotly.com/javascript/) documentation

### Building an operations dashboard

### Customer case study with JetFuel

---

## Legacy



---

## Configure columns in the Table component

:::tip 

Click  in the **Columns** settings to display a pop-out editor for columns. 

:::

Each table column has a designated format for its values which control how values are presented or edited. Retool attempts to infer this from the data source and sets the format automatically. 

To change a column's format:

1. Click on the column in the **Columns** section of the Inspector.
2. Change **Format** to the preferred format to use. 

export function ArcadeFormat() {
  return (
    
      
    
  )
}

The Table component supports the following column formats.

| Format               | Description                                                                  |
| -------------------- | ---------------------------------------------------------------------------- |
| **Avatar**           | An avatar with image and text.                                               |
| **BigInt**           | An integer value that represents a number too large for the **Number** type. |
| **Boolean**          | A checkbox that represents a boolean value.                                  |
| **Button**           | A clickable button.                                                          |
| **Currency**         | An amount in a specified currency.                                           |
| **Date**             | A date.                                                                      |
| **Datetime**         | A date and time.                                                             |
| **Email**            | An email address.                                                            |
| **HTML**             | Rendered HTML content.                                                       |
| **Icon**             | An icon.                                                                     |
| **Image**            | An image.                                                                    |
| **JSON**             | JSON data.                                                                   |
| **Link**             | A URL link.                                                                  |
| **Markdown**         | Rendered Markdown content.                                                   |
| **Multiline string** | A string of plain text that wraps to multiple lines.                         |
| **Number**           | A number.                                                                    |
| **Percent**          | A percentage amount that represents a number in the range 0-1.               |
| **Phone number**     | A phone number.                                                              |
| **Progress**         | A progress bar that represents a number in the range 0-100.                  |
| **Rating**           | A rating that represents a number value.                                     |
| **String**           | A single-line string of plain text.                                          |
| **Tag**              | A tag that represents a value.                                               |
| **Tags**             | One or more tags that represent an array of values.                          |
| **Time**             | A time.                                                                      |

## Regenerate columns

You can regenerate columns after your data source changes by selecting **Regenerate Columns** in the **Inspector**. This functionality parses a random sample of entries in your data and adds any missing columns to your table, removes the primary key, and resets the width of your columns. 

Regenerating columns does not remove any custom columns nor does it override other modifications to existing columns. If your entries have varied properties, the random sample of entries might not detect every column in your data. To avoid this issue, you can manually add your columns or transform your data into a consistent shape before passing it to the table.

export function ArcadeRegenerate() {
  return (
    
      
    
  )
}

## Configure editable columns

You can edit column cell values and save the changes back to the data source. To make a column editable, click  next to the column, then select **Make editable**. You can also select a column and click the **Editable** checkbox.

export function ArcadeEditable() {
  return (
    
      
    
  )
}

### Edit existing cell values

Click a cell to edit its value. All edited cell value changes, along with their respective primary keys, are temporarily stored in the table's `changesetArray` property.

```json
...
  "changesetArray": [
    {
      "verified": true,
      "id": 58
    }
  ],
...
```

:::tip 

The changeset only includes values for edited columns by default. If you need to include all column values, click  in the **Interaction** settings and enable **Include full rows in changeset array**.

:::

### Configure input validation

Editable columns with **Format** set to **Number** support inline cell input validation. Use the **Min** and **Max** settings to configure a minimum and maximum input value. The user input is automatically updated to the relevant minimum or maximum value upon edit.

### Write a query to save changes

To save changes, you must first write a query that references `changesetArray` and updates the existing data source. For example, to save changes back to a PostgreSQL table, write a query that uses the [bulk update via a primary key](../../../../queries/guides/sql/writes.mdx#2-select-the-action-type) action.

import Spread from "./_spread.mdx"

## Add custom columns

:::tip 
You can use `{{ currentRow }}` and `{{ currentSourceRow }}` to reference other column values from the same table row or data source row respectively.
:::

You can add custom columns to tables using the  button above the list of columns. Custom columns are often used to calculate data based on other values in your app.

Custom columns have the same properties as regular table columns, such as the format and value. You can optionally specify an existing column to use for data, then reference it with `item`.  For example, to display a future relative date (e.g., "In two days") for a column with date values:

1. Select a column with date values (e.g., `expiration`) to use as the source column. 
2. Set the column format to **String**.
3. Use `item` to reference the mapped value and the preloaded moment library to convert it to a relative date. You can also use the preloaded lodash library to format the string using start case. For example:

```js
{{ _.startCase(moment(item).fromNow()) }}
```

export function ArcadeCustom() {
  return (
    
      
    
  )
}

## Configure dynamic columns

You can dynamically configure columns based upon mapped values from the data source, such as column labels and formats. Using dynamic column settings is particularly useful if the data source's schema changes which would change the columns available or if you need greater flexibility over columns.

For example, a Table component might reference data from a query that returns employee information. The data source includes a balance of their vacation days, which you only want to display to managers. Use dynamic column settings to display that property as a column only if the `current_user` is part of the `managers` user group.

Click  in the **Content** settings and enable **Use dynamic column settings**. 

export function ArcadeDynamic() {
  return (
    
      
    
  )
}

You then configure the following settings. In these settings, you can either set a value explicitly, or dynamically reference mapped values using `item`, which corresponds to the unused keys (columns) in the data source. 

- **Label**: The label of the dynamic column.
- **Format**: The format of the dynamic column. If you will have more than one dynamic column, click **FX** to set this value dynamically, or **Auto** predicts the appropriate format based on data for that column. For example: `{{ item === 'discontinued' ? 'boolean' : 'string' }}` 
- **Hidden**: Whether the column should be hidden. For example: ` {{ !current_user.groups.map(group => group.name).includes('managers') }}` hides the column if the current user is not in the `managers` group.
- **Editable**: Whether the column should be editable.
- **Column Properties**: The properties of the dynamic column. You can configure appearance options for each **Format** that your dynamic column might have. For example, if the column could have a format of either `boolean` or `string`, add **Column Properties** for **Boolean** and **String**.

Expand the following section for a full list of the valid values for the **Format**:

- Auto (`auto`)
- String (`string`)
- Multiline String (`multilineString`)
- Number (`decimal`)
- Percent (`percent`)
- Progress (`progress`)
- Currency(`currency`)
- Phone Number(`phoneNumber`)
- Date (`date`)
- Datetime (`datetime`)
- Time (`time`)
- Boolean (`boolean`)
- Tag (`tag`)
- Tags (`tags`)
- Icon (`icon`)
- Avatar (`avatar`)
- Image (`image`)
- Link (`link`)
- BigInt(`bigint`)
- Button (`button`)
- Markdown (`markdown`)
- HTML (`html`)
- Rating (`rating`)
- JSON (`json`)
- Email (`email`)

## Sort columns

You can sort table rows by one or more columns directly from the table's header or using JavaScript.

Click on a column header to sort rows in ascending or descending order. When sorting is active, the header by which the table sorts displays an **↓** or **↑** arrow to denote sort order.

Shift-click additional columns to include them for sorting.

export function ArcadeSortHeader() {
  return (
    
      
    
  )
}

The [sortArray](../../../reference/components/data/table.mdx#property-filterStack) property contains all sorting options currently applied to the table. You can use the following JavaScript methods to configure sorting:

| Method                                                                     | Description                                       |
| -------------------------------------------------------------------------- | ------------------------------------------------- |
| [clearSort](../../../reference/components/data/table.mdx#method-clearSort) | Clear all sort options applied to the table.      |
| [setSort](../../../reference/components/data/table.mdx#method-setSort)     | Sort rows by the specified columns and direction. |

```js title="Example usage"
// Clear Sort
table1.clearSort();

// Set Sort
table1.setSort([{ columnId: "date", sortDirection "desc" }, { columnId: "lastName", sortDirection "asc"}]);
```

### Default sorting

Tables do not automatically sort rows by default. You can configure default sort options that apply whenever a user opens the app. Click  in the **Interaction** settings and specify the columns and orders with which to sort.

export function ArcadeDefaultSort() {
  return (
    
      
    
  )
}

### Sort modes

You can fine-tune how each column sorts values. This can be useful if columns contain multiple values, or when sorting by ascending or descending values doesn't meet your requirements. There are four sort modes.

| Mode     | Description                                                                           |
| -------- | ------------------------------------------------------------------------------------- |
| Default  | Sorts based on the mapped value. If the mapped value is unset, the raw value is used. |
| Raw      | Sorts based on the raw value, regardless of the mapped value.                         |
| Options  | Sorts based on a list of options you create manually.                                 |
| Disabled | Disables column sort modes.                                                           |

---

## Customize the appearance of the Table component

The Table component includes a range of options to customize the appearance and user experience.

## Demo

The following demo app uses a toolbar with custom buttons. It also includes a summary row to summarize column values.

## Configure the toolbar

The Table component's *toolbar* is enabled by default and contains buttons to perform different actions, such as filtering. It also includes navigation controls for paginated data, if enabled. Select the **Toolbar** add-on to configure toolbar options.

### Toolbar position

You can toggle the **Position** setting to change where the toolbar appears. 

export function ArcadePosition() {
  return (
    
      
    
  )
}

### Customize buttons

The toolbar includes a selection of buttons by default. You can customize the toolbar buttons and configure custom ones that perform actions when clicked.

| Button Type | Description                                                |
| ----------- | ---------------------------------------------------------- |
| Filter      | Filter table data by column values.                        |
| Sort        | Sort table data by the specified columns.                  |
| Group by    | Group rows by the specified column value.                  |
| Add Row     | Add new rows of data that can be saved to the data source. |
| Custom      | Trigger event handlers when clicked.                       |

export function ArcadeCustomButtons() {
  return (
    
      
    
  )
}

## Configure row height

You can use the **Row height** setting to adjust the height of table rows. The Table component can also dynamically adjust the height of each row based on cell values.

| Value   | Height                                                           |
| ------- | ---------------------------------------------------------------- |
| X-Small | `20px`                                                           |
| Small   | `32px`                                                           |
| Medium  | `48px`                                                           |
| Large   | `60px`                                                           |
| Dynamic | Dynamically adjusts the height of each row based on its content. |

export function ArcadeRowHeight() {
  return (
    
      
    
  )
}

## Summarize columns

The **Summary row** add-on summarizes values from the specified columns using different aggregation methods. 

1. Click **+** in the **Add-ons** settings and select **Summary row**.
2. Specify the columns to summarize.
3. Select the default aggregation method to use.

You can use the following methods to summarize column values. The methods available depend on the column format.

| **Method**         | **Description**                                |
| ------------------ | ---------------------------------------------- |
| **Min**            | The smallest value in the column.              |
| **Max**            | The largest value in the column.               |
| **Average**        | The average (mean) of the column's values.     |
| **Sum**            | The total sum of all the values in the column. |
| **Count Distinct** | The number of unique values in the column.     |

export function ArcadeSummary() {
  return (
    
      
    
  )
}

import Appearance from "../../../../_shared/_appearance.mdx";

## Dynamic row and column colors

You can also use conditional logic to customize row and column background colors. 

- Use the **Row color** setting to control the background color of rows.
- Select a column in the Inspector and use the **Background color** setting.

You can use a ternary operator set the color based on certain conditions. The following example sets the background color of active users to `theme.success` (green) and inactive users to `theme.warning` (amber).

```js
{{ currentRow.enabled ? theme.success : theme.warning }}
```

To set background colors on individual columns, select the column and edit the background color. You can reference column values with `item` for conditional logic. 

```js
{{ item ? theme.success : theme.warning }}
```

export function ArcadeBackgroundColors() {
  return (
    
      
    
  )
}

---

## Filter data in the Table component

The Table component has built-in filter and search options. You can configure default filters, combine multiple filters together, and connect an input component to search through table rows.

## Apply filters

import ServerSideFiltering from './_server-side-filter.mdx'

Table filters are comprised of *rules* and *groups*. You can create and edit table filters directly from the table's toolbar, with the Filter component, or using JavaScript.

- Filter rules compare column values for each row to the operator and value you specify.
- Filter groups combine filter rules for each row and returns results based on the **And** or **Or** logical operator.

The following operators are available for use in table filters. The operator you use depends on the type of data used in the specified column. 

| Operator           | Description              | Data Type           |
| ------------------ | ------------------------ | ------------------- |
| ``                | Greater than             | Number, Date        |
| `>=`               | Greater than or equal to | Number, Date        |
| `=`                | Equal to                 | Number, Text        |
| `!=`               | Not equal to             | Number, Text        |
| `Is`               | Is                       | Boolean, Text       |
| `Is Not`           | Is not                   | Boolean, Text       |
| `Includes`         | Includes                 | Text, Array         |
| `Does Not Include` | Does not include         | Text, Array         |
| `Is True`          | Is true                  | Boolean             |
| `Is False`         | Is false                 | Boolean             |
| `Is Empty`         | Is empty                 | Text, Array         |
| `Is Not Empty`     | Is not empty             | Text, Array         |
| `Intersects`       | Intersects               | Array               |
| `Is One Of`        | Is one of                | Text, Number, Array |
| `Is None Of`       | Is none of               | Text, Number, Array |
| `Is Before`        | Is before                | Date                |
| `Is After`         | Is after                 | Date                |

Click  in the table toolbar to set filters. This displays a popup filter editor where you can specify filter rules and groups.

Filter table data from the toolbar.

The [Filter](../../../reference/components/data/filter.mdx) component provides a persistent interface for the filter editor accessible from the toolbar. Once you add a Filter component to the canvas, click **Link to a table** and select the table to filter.

export function ArcadeFilterComponent() {
  return (
    
      
    
  )
}

The [filterStack](../../../reference/components/data/table.mdx#property-filterStack) property contains all filters currently applied to the table. You can use the following [JavaScript](../../../../queries/concepts/javascript.mdx) to configure filters:

| Method                                                                                   | Description                                                                 |
| ---------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- |
| [setFilterStack](../../../reference/components/data/table.mdx#method-setFilterStack)     | Set filters and operators for the table.                                    |
| [clearFilterStack](../../../reference/components/data/table.mdx#method-clearFilterStack) | Clear all filters applied to the table.                                     |
| [resetFilterStack](../../../reference/components/data/table.mdx#method-resetFilterStack) | Reset the filter stack to the default filters.                              |
| [setFilter](../../../reference/components/data/table.mdx#method-setFilter)               | Add a filter to the table or update an existing filter with a specified ID. |
| [clearFilter](../../../reference/components/data/table.mdx#method-clearFilter)           | Remove the filter with the specified ID.                                    |

```js title="Set filter stack"
table1.setFilterStack({
  filters: [
    {
      filters: [
        { columnId: "id", operator: ">", value: 1 },
        { columnId: "id", operator: "", value: 6 },
        { columnId: "id", operator: "

The following examples filter different types of data based on the column value and format. 

| **Column** | **Operator**  | **Value**               | **Description**                                                    |
| ---------- | ------------- | ----------------------- | ------------------------------------------------------------------ |
| **Age**    | **>**         | `30`                    | Filters rows where the **Age** is greater than 30.                 |
| **Name**   | **Includes**  | `John`                  | Filters rows where the **Name** includes the string "John".        |
| **Status** | **Is One Of** | `["Active", "Pending"]` | Filters rows where the **Status** is either "Active" or "Pending". |
| **Date**   | **Is Before** | `2024-01-01`            | Filters rows where the **Date** is before January 1, 2024.         |

## Configure default filters

Use the **Default filters** settings to configure filter rules that are active by default. Each To configure a filter:

1. Click **+** to add a filter.
2. Select the **Column** by which to filter.
3. Select the **Operator** for comparison, such as **Includes** or **Is not empty**.
4. Specify the value with which to compare.

 

Default filter rules are applied to the root level of filters. You cannot configure default filter groups with nested rules.

## Search table data

The **Search** and **Search term** settings enable you to add search functionality to tables without the use of filters. You can reference any value to use as the search term, such as an input component. To add a search field:

1. Add a Text Input component to the canvas.
2. Update the table's **Search term** setting to reference the Text Input component value, such as `{{ textInput1.value }}`. 

export function ArcadeSearch() {
  return (
    
      
    
  )
}

The search term applies to all column values and the table instantly updates as the input value changes. You can configure the **Search** setting to change the type of search to perform, such as **Fuzzy match** or **Exact**.

---

## Getting started with the Table component

Tables are a common way to view and interact with your data. The [Table](../../../reference/components/data/table.mdx) component enables users to sort, filter, paginate, and download rows of information. It also supports row selection and insertion, server-side pagination, and custom columns.

import Demo from "./_demo.mdx";

## Features

Key features of the Table component include:

- [Column formats](./columns.mdx), such as tags and checkboxes.
- [Editable columns](./columns.mdx#edit-existing-cell-values) and [adding new rows](./rows.mdx#add-new-rows).
- [Row grouping](./rows.mdx#group-rows-by-column) by column.
- [Expandable rows](./rows.mdx#expand-rows-with-additional-content) that can contain other components.
- [Filter](./filters.mdx), [sorting](./columns.mdx#sort-columns), and [search](./filters.mdx#search-table-data) options.
- [Server-side pagination](./pagination.mdx).
- Customizable [toolbar](./customization.mdx#configure-the-toolbar) and [row action](./rows.mdx#configure-row-actions) buttons.

## Configure content options

When you add a Table component to an app, it attempts to display data from the most recently created query. If your app doesn't have a query, the component includes demo data instead.

The data source needs to be an array of objects. Table creates columns for each key and its rows map to array items.

### Specify the data source

You can change the data displayed in a table using the **Data source** setting. From the dropdown, you can select a resource or JavaScript query, transformer or variable, or an array.

export function ArcadeDataSource() {
  return (
    
      
    
  )
}

The Table component supports both client-side and server-side [pagination](./pagination.mdx). You can specify the number of rows to display and the table automatically paginates the current data. With server-side pagination, you can write performant queries that retrieve only the required data to display in the table.

### Set the primary key

Retool recommends you select a primary key column to uniquely identify each row of data. If your data already has an ID column, Retool automatically uses it for the primary key. You can also manually set it to any column that is guaranteed to be unique.

:::tip title
If you don't want to show the primary key in your table, click  to open the contextual menu for the column and select **Hide column**.
:::

Set the primary key.

### Configure columns

Retool automatically generates [columns](./columns.mdx) from the specified data source. You can configure columns with a number of options, available by clicking on each column in the Inspector.

When you add tables to the canvas, Retool automatically attempts to configure the options for each column, such as their type. Each column has a _source key_ that maps to the data source. You can optionally specify a source key for [custom columns](./columns.mdx#add-custom-columns), should you want to use it for column values.

export function ArcadeFormat() {
  return (
    
      
    
  )
}

The Table component can also [summarize column values](./customization.mdx#summarize-columns) using different aggregation methods.

### Configure rows

You can add [row actions](./rows.mdx#configure-row-actions) buttons that appear for each row on mouse hover and trigger event handlers when clicked. The Table component also includes a number of additional interface features which you configure in the **Add-ons** section, such as [expandable rows](./rows.mdx#expand-rows-with-additional-content) that can contain components.

export function ArcadeExpandable() {
  return (
    
      
    
  )
}

## Configure user interaction

The **Interaction** section of the Inspector contains settings that control user interaction, such as:

- [Row and cell selection](./rows.mdx#select-rows) that control how users select values.
- [Event handlers](../../interaction-navigation/event-handlers.mdx) that listen for and handle interactions with your components.
- [Default filters](./filters.mdx#) that initially filter data based on specified criteria.
- [Search options](./filters.mdx#search-table-data) that enable users to search within the table.

export function ArcadeRowSelect() {
  return (
    
      
    
  )
}

## Customize appearance

You can [customize](./customization.mdx) the appearance of tables. The Table component supports automatic and fixed height so it can automatically expand based on the row count or remain a fixed height and scroll. It also supports fixed or dynamic [row height](./customization.mdx#configure-row-height) that expands to fit cell content.

export function ArcadeRowHeight() {
  return (
    
      
    
  )
}

All components have a **Hidden** setting, which you can dynamically control with a truthy value that evaluates to `true` or `false`. You can also control the `hidden` property for a component using the `.setHidden()` method from event handlers and JavaScript queries.

You can also create custom style rules using the **Styles** setting. Each component has different styles that you can configure, but common ones include colors, fonts, and text styles. Use [app-level or organization-level themes](../../presentation-styling/themes.mdx) to set these styles across all components.

## Next steps

Learn more about using the Table component.

---

## Configure server-side pagination for the Table component

import ServerSidePagination from "../../../../_shared/_server-side-pagination.mdx";

The Table component supports [Limit-Offset](#configure-limit-offset-pagination), [Cursor](#configure-cursor-based-pagination), and [GraphQL Relay](#configure-graphql-relay-cursor-pagination) server-side pagination.

import Demo from "./_demo.mdx";

## Enable server-side pagination

import ServerSideFiltering from './_server-side-filter.mdx'

In the **Add-on** settings, click **+** and select **Pagination**. The Table component uses client-side pagination by default. Click **Enable server-side pagination** to display the relevant settings.

Whichever pagination method you use, you must specify the **Page size**—the number of rows you want each page to use—for the table. Once enabled, the table's `pagination` property contains information for you to use in paginated queries.

:::tip

The Table component uses zero-based numbering for pagination values (e.g., `pageNumber` and `offset`).

:::

## Configure Limit Offset pagination

**Limit offset based** pagination uses a *limit* to define the number of records to return and an *offset* to specify the starting point. 

| Parameter | Value                 | Description                                                                                                                 |
| --------- | --------------------- | --------------------------------------------------------------------------------------------------------------------------- |
| Limit     | `pagination.pageSize` | The page size of the table.                                                                                                 |
| Offset    | `pagination.offset`   | The offset from which to start. The table automatically calculates this based on the page size and currently selected page. |

You can optionally configure the **Total row count**, if available, so that the table displays numbered pagination controls.

export function ArcadeLimitOffset() {
  return (
    
      
    
  )
}

### Write a query to paginate results

After you enable server-side pagination, write a query that paginates results. Limit Offset-based pagination is commonly used in SQL queries or with APIs that allow basic control over paginated data.

To retrieve table data with server-side pagination, you first configure a query to reference the table's `pagination` properties using the `limit` and `offset` SQL clauses.

```sql title="Server-side pagination example"
select
  id,
  first_name,
  last_name,
  email
from
  customer
where
  first_name ilike {{ '%' + textInput1.value + '%' }}
order by
  id
limit
  {{ table1.pagination.pageSize }} 
offset 
  {{ table1.pagination.offset }}
```

### Update the table's pagination settings

The Table component automatically triggers the query whenever the page is changed. If you wrote a separate query to get a total count of records, you can reference this in the **Total row count** setting. 

For example, you can get the total number of all records for a database table with a separate query that uses `count()`. You can then reference this query and set **Total row count**.

```sql title="Get total row count"
select
  count(*)
from
  customer
```

If set, users can jump to specific pages of data when using the table.

Many APIs include support for limit and offset pagination. For example, the NASA Image and Video Library API supports `perPage` to limit the number of results and `page_size` to use as the offset.

```bash title="Server-side pagination example"
https://images-api.nasa.gov/search?q=artemis&page_size={{ table.pagination.pageSize }}&page={{ table.pagination.currentPage + 1 }}
```

| Parameter   | Value                                    |
| ----------- | ---------------------------------------- |
| `page_size` | `{{ table.pagination.pageSize }}`        |
| `page`      | `{{ table.pagination.currentPage + 1 }}` |

### Configure the table's pagination settings

The Table component automatically triggers the query whenever the page is changed. The NASA Image and Video Library API also returns the total number of results, `collection.metadata.total_hits` to use for the total row count.

## Configure Cursor-based pagination

**Cursor based** pagination uses a unique identifier, such as an ID, that represents a specific position in the data set. This cursor changes with each page change and is referenced by the query to move through the data set. How you configure pagination settings is dependent on the data source.

| Setting       | Description                                                                            |
| ------------- | -------------------------------------------------------------------------------------- |
| Next cursor   | The identifier to use for the next set of results.                                     |
| Has next page | Whether there is another page of results. If `false`, the **Next** button is disabled. |

export function ArcadeCursor() {
  return (
    
      
    
  )
}

### Write a query to paginate results

After you enable server-side pagination, write a query that paginates results. For example, the Stripe API supports cursor-based pagination with the `limit` and `starting_after` parameters. To retrieve a list of customers with server-side pagination, you first configure a query to reference the table's `pagination` properties.

1. Add a resource query for the **Stripe** resource.
2. Select the **GET /v1/customers** operation.
3. Set the `limit` parameter to `{{ table1.pagination.pageSize }}`.
4. Set the `starting_after` parameter to `{{ table1.pagination.afterCursor }}`.

### Update the table's pagination settings

Next, update the table configuration to reference the query's pagination data. Stripe uses an object ID as the cursor so you would reference the last ID in the query's results to use as the cursor. 

1. Select the table and open the **Pagination** settings.
2. Set **Pagination type** to **Cursor-based**.
3. Set **Next cursor** to the ID for the last item of the query results. You can use the [preloaded lodash library](../../../../queries/reference/libraries/preloaded.mdx) and `_.last` to reference the ID for the last item in the array, such as `{{ _.last(query.data.data).id }}`.
4. Stripe also returns the `has_more` property which indicates whether there are more pages of results. Set **Has next page** to `{{ query.data.has_more }}`.

## Configure GraphQL Relay Cursor pagination

Retool supports cursor-based pagination using [Relay's GraphQL server specification](https://relay.dev/docs/guides/graphql-server-specification/). Paginated GraphQL queries accept the `first`, `last`, `after`, and `before` parameters to identify the data to retrieve.

export function ArcadeGraphql() {
  return (
    
      
    
  )
}

### Write a query to paginate results

After you enable server-side pagination, write a query that paginates results. For example, the GitHub GraphQL API supports Relay cursor-based pagination. To retrieve a list of public repositories for the `github` prganization with server-side pagination, you first configure a query to reference the table's `pagination` properties.

```graphql title="Get public repositories"
query GetOrganizationRepositories($first: Int, $after: String, $before: String) {
  organization(login: "github") {
    repositories(first: $first, after: $after, before: $before, privacy: PUBLIC) {
      edges {
        node {
          name
          description
          url
        }
        cursor
      }
      pageInfo {
        startCursor
        endCursor
        hasNextPage
        hasPreviousPage
      }
    }
  }
}
```

GraphQL queries use variables for referenced values.

| Variable | Value                                 | Description                                                                   |
| -------- | ------------------------------------- | ----------------------------------------------------------------------------- |
| first    | `{{ table.pagination.pageSize }}`     | The page size of the table.                                                   |
| after    | `{{ table.pagination.afterCursor }}`  | The cursor from which to start when navigating to the next page.              |
| before   | `{{ table.pagination.beforeCursor }}` | The previous cursor from which to start when navigating to the previous page. |

### Update the table's pagination settings

Next, update the table configuration to reference the query's pagination data. GitHub returns both next and previous cursors with which to paginate.

1. Select the table and open the **Pagination** settings.
2. Set **Pagination type** to **GraphQL Relay cursor based**.
3. Set **Previous cursor** the cursor for the first item in the query results. You can use the [preloaded lodash library](../../../../queries/reference/libraries/preloaded.mdx) and `_.first` to reference the cursor for the last item in the array, such as `{{ _.first(query.data.organization.repositories.edges).cursor }}`.
4. Set **Next cursor** to the cursor for the last item in the query results. You can use `_.last` to reference the cursor for the last item in the array, such as `{{ _.last(query.data.organization.repositories.edges).cursor }}`.
5. GitHub also returns the `hasNextPage` property which indicates whether there are more pages of results. Set **Has next page** to `{{ query.data.organization.repositories.pageInfo.hasNextPage }}`.

Since the query uses a fixed page size and the GitHub GraphQL API returns both next and previous cursors, `last` is not required.

---

## Configure rows in the Table component

The Table component has a number of row-related features and supports adding new rows of data. 

## Configure row actions

A _row action_ is a button that appears when the cursor hovers over a row. You configure row actions with event handlers that perform an action or trigger a query when clicked. 

Row actions are useful for performing an action with a specific row, such as deleting it. To configure an row action:

1. Click  in the **Row actions** section to add a row action. 
2. Specify a label and icon for the row action.
3. Configure the event handler with the action to perform when clicked. 

export function ArcadeRowAction() {
  return (
    
      
    
  )
}

## Select rows

The Table component supports single and multiple row selection, or you can disable selection entirely. Use the **Row selection** settings in the **Interaction** section of the Inspector to specify the row selection option to use. 

export function ArcadeRowSelect() {
  return (
    
      
    
  )
}

The **Default row** option controls whether the table selects a row by default. If enabled, you can specify the default row index (or indexes) to select.

You can enable **Persist row selection** if you want the table to maintain the selected row even if they are no longer present in the data source This is useful if the data is filtered outside of the table. This option requires that the **Primary key** be set for the table. 

## Expand rows with additional content

Expandable rows enables you to assemble a repeatable set of components that appear when a user expands a row. Click  in the **Content** settings and toggle **Enable expandable rows** on.

When enabled, you can add components directly to the first row instance. These appear in all row instances.

Expandable rows can use the `currentRow` and `currentSourceRow` properties to reference column values.

export function ArcadeExpandable() {
  return (
    
      
    
  )
}

## Group rows by column

:::tip

You can provide users with controls to group rows using the **Group by** [toolbar button](./customization.mdx#customize-buttons)

:::

You can group rows by column value to provide an additional method of organizing data. Click  in the **Content** settings and use **Group rows by** to specify columns with which to group. 

When grouping is enabled, the Table component groups unique column values into expandable sections. You can click a group to reveal all of its grouped rows. 

:::info 

You cannot group rows using editable columns. Instead, add a [custom column](./columns.mdx#add-custom-columns) that references the editable column value and use that for grouping.

:::

export function ArcadeGrouped() {
  return (
    
      
    
  )
}

## Add new rows

In addition to [editing column cell values](./columns.mdx#configure-editable-columns), you can enable the **Add Row** add-on to insert new table rows and save them to your data source. Any columns for which you want to provide values must either be editable or editable for new rows only.

export function ArcadeNewRows() {
  return (
    
      
    
  )
}

### Enable the add-on

1. Click **Toolbar** in the **Add-ons** section of the Inspector.
2. Click  to add a new toolbar button. 
3. Select **Add Row**.

### Add a row

Click the  button in the toolbar to display a pop-up row editor for you to add a new row of data. New row data is temporarily stored in the table's `newRows` property.

### Write a query to save changes

To save changes, you must first write a query that references `newRows` and saves them to the existing data source. For example, to save changes back to a PostgreSQL table, write a query that uses the [bulk insert](../../../../queries/guides/sql/writes.mdx#2-select-the-action-type) action.

import Spread from "./_spread.mdx"

---

## Getting started with boolean inputs

import Interaction from "/docs/apps/guides/forms-inputs/_partials/_interaction.mdx";
import Appearance from "/docs/_shared/_appearance.mdx";
import Content from "/docs/apps/guides/forms-inputs/_partials/_content.mdx";

You can use the following components to accept boolean user inputs:

* [Checkbox](../../reference/components/select-inputs/checkbox.mdx)
* [Checkbox Group](../../reference/components/select-inputs/checkbox-group.mdx)
* [Checkbox Tree](../../reference/components/select-inputs/checkbox-tree.mdx) (a preset of Checkbox Group)
* [Switch](../../reference/components/select-inputs/switch.mdx)
* [Switch Group](../../reference/components/select-inputs/switch-group.mdx)

import WebPlayground from '/docs/_partials/_components/_web-playground.mdx';

## Features

Key features of boolean inputs include:

- Manual or mapped option lists in Checkbox Group, Checkbox Tree, and Switch Group.
- Nested option lists in Checkbox Group, Checkbox Tree, and Switch Group.
- Input validation, including marking components as required or creating custom rules.
- Event triggering based on status of the boolean input.
- Style configuration.

## Specify content options

The **Content** section of the Inspector contains settings that control the content of an input field, such as:

- **Default value**: Pass in a single value or an array of values that correspond to the items you want checked or switched "on" by default.
- **Check strictly**: When checked, removes the association between parent and child. For example, checking the parent option does not automatically check the nested child options.
- **Delimiter**: The string delimeter to use on nested items in the `checkedPathStrings` and `leafPathStrings` properties. For example, setting this value to `-` would result in a string formatted as follows: `"Shoes-Sandals-Flip-Flop"`.

All boolean components include an **Add ons** section, which enables you to add a label or tooltip to the component.

### Define options

You can define the options for Checkbox Group, Checkbox Tree, and Switch Group either manually or by mapping data. Refer to the [Define option lists](./option-lists.mdx) guide for more information.

If you're mapping data for Checkbox Group, Checkbox Tree, or Switch Group, your data must be in a flat array format.

### Identify parent

Checkbox Tree uses the **Parent label** setting to identify where each option should sit in the nested option list. This setting must correspond to the value of another key in the dataset, which identifies the parent of the item. Items without a parent label appear at the top level.

For example, to create a Checkbox Tree, you could have a JavaScript query, `query1`, that includes the following code: 

```JavaScript
const jobs = [
  {label: 'Engineering', value: 'eng'},
  {label: 'Software Developer', value: 'dev', department: 'Engineering'},
  {label: 'QA Tester', value: 'qa', department: 'Engineering'},
  {label: 'Product', value: 'prod', },
  {label: 'Product Manager', value: 'pm', department: 'Product'},
];

return jobs;
```

Set the **Data source** to `query1`, and set **Parent label** to `{{ item.department }}`. The Checkbox Tree appears as follows:

- Engineering
  - Software Developer
  - QA Tester
- Product
  - Product Manager

### Configure group layout

Checkbox Group and Switch Group each support different **Group layout** settings.

:::note

When using Checkbox Tree or the component has a nested option list, Retool enforces the **Tree** group layout.

:::

The following screenshots show the different layout options available.

## Create a dark mode switch

A common use for Switch is it create a dark mode toggle. This example also demonstrates how to set the default value of the component and add event handlers. Use the following instructions to add a dark mode toggle to your app.

:::note

Organization-level themes are available on a Business or an Enterprise plan only.

:::

1. Create an [organization-level theme](../presentation-styling/themes.mdx) with a light mode and a dark mode.
2. Navigate to your app's settings, and select the theme you just created.
3. Add a Switch component to the canvas. Update the label to `Dark mode`.
4. Set default value to `{{ theme.mode === "Dark" }}`. This ensures that the switch position corresponds to the current state of the mode.
5. Add a new event handler with the following settings:
   1. **Event:** True
   2. **Action:** Set theme mode
   3. **Mode name:** Dark
   4. **Persist:** Checked
6. Add a new event handler for when the Switch is set to False.
7. Test out your dark mode toggle.

---

## Getting started with Agent Chat

The [Agent Chat](../../../reference/components/special-inputs/agent-chat.mdx) component provides an interface for users to interact with [Retool Agents](../../../../agents/index.mdx). Use the chat interface to ask questions or make requests, and the agent uses tools to respond to the request. The Agent Chat component functions similarly to the **Chats** tab available inside a Retool Agent.

:::note

The Agent Chat functionality cannot be used from public apps, as calling an agent requires a logged-in user.

:::

## Features

Key features of Agent Chat include:

- An automatically-created query that triggers a run of the agent you choose.
- Inputs pulled directly from your agent, no configuration needed.
- Customizeable styles.

## Prerequisites

Before you get started with an Agent Chat component, you must first create an agent. Refer to the [Retool Agents](../../../../agents/index.mdx) documentation for more information.

## Specify content options

The **Content** section of the Inspector contains settings that control the content of Agent Chat, including:

- **Title**: The title of your agent. Note that you must check the **Show header** setting in the **Appearance** section for the title to appear.
- **Query to trigger**: The query triggered when a user sends a message.

Agent Chat configuration.

When you add the Agent Chat component to the canvas, Retool automatically generates a query that accesses a Retool Agent. You can customize this query as necessary.

export function ArcadeEmbed() {
  return (
    
      
    
  )
}

You can also manually create a resource query that calls an agent using the **Retool Agent** resource.

:::note

Consider using [Text](../../../reference/components/presentation/text.mdx) or another component to explain the purpose of your agent and the kinds of requests that it can handle.

:::

import Appearance from "/docs/_shared/_appearance.mdx";

## Query an agent with Agent Chat

When querying an agent with the Agent Chat component, the queries must use JSON mode, and provide inputs with the following structure:

```json
{
  "action": "invoke",
  "messages": [
    {
      "role": "user",
      "content": "hello world"
    }
  ]
}
```
### Agent Inputs
All Agent Chat components are automatically connected to a preconfigured query, which sends the [agentInputs](../../reference/components/agent-chat#property-agentInputs) property to the agent in the structure outlined above. This allows you to use an Agent Chat with an agent query with no configuration. 

Agent Inputs

The agent query must be able to do two things: 
1. Trigger the agent.
2. Poll for the result of the agent execution. 

The Agent Chat component achieves this by modifying the shape of `agentInputs` depending on whether it is triggering a query or waiting on the response. 

Retool dynamically changes the structure of `{{ agentChat1.agentInputs }}` between the following two payloads:

```json
{
  "action": "invoke",
  "messages": [
    {
      "role": "user",
      "content": "hello world"
    }
  ]
}
```

```json
{
  "action": "getLogs",
  "agentRunId": "uuid..."
}
```

### Pass additional parameters from app to agent
To pass additional parameters from your app into the agent, modify the `agentChat.agentInputs` object. 

Append one (or more) `user` messages to the `messages` array with the parameters from the app that you want to pass in. 
```json title="User message example"
{{ {
  ...agentChat1.agentInputs,
  messages: [
    ...(agentChat1.agentInputs.messages || []),
    { role: 'user', content: `Name of select: ${select1.value}` }
  ]
} }}
```

For example, in an app that allows users to select a customer via a [Select](../../reference/components/select) component, add the user message example above to the **Agent Inputs** field in your agent query to pass the customer ID into an agent. 

export function ArcadeEmbed_agentInput() {
  return (
    
      
    
  )
}

To create an object with additional parameters (like those from other in-app components), add them to the `messages` array while preserving the overall shape. For example, you can use the following structure to send more than one input to an agent:

```json
{{ {
  ...agentChat1.agentInputs,
  messages: [
    ...(agentChat1.agentInputs.messages || []),
    { role: 'user', content: hereYouCanReferenceAnyComponentProperty.value }
    { role: 'user', content: youCanAddAsManyOfTheseAsYouLike.value }
    { role: 'user', content: `You can prepend values with text: ${likeThis.value}` }
  ]
} }}
```

---

## Getting started with chat components

import Intro from '../../../../_shared/_chat-components.mdx';

The following tabs outline the differences between the chat types.

:::note

To enable comments and discussion between users of your app, choose the [Comment Thread](../comment-thread.mdx) component instead.

:::

Use the Agent Chat component to enable users to chat with one of your [agents](../../../../agents/index.mdx). When you add Agent Chat to your canvas, Retool generates an `Invoke Agent` query that allows you to select the agent you want to call, with no additional configuration needed. Messages to Agent Chat with questions or requests trigger a run of your selected agent.

Agent Chat configuration.

Use the LLM Chat component to enable users to send messages to an LLM. When you add LLM Chat to your canvas, Retool generates a [Retool AI query](../../../../queries/concepts/actions.mdx#chat-actions). In this query, you can configure the model you want users to chat with and the [Retool Vectors](../../../../data-sources/quickstarts/retool-vectors.mdx) that can add more context to your queries.

---

## Getting started with LLM Chat

The [LLM Chat](../../../reference/components/special-inputs/llm-chat.mdx) component provides an interface for users to interact with AI. You can choose how the AI model behaves and the actions it takes in response to user messages.

import WebPlayground from '/docs/_partials/_components/_web-playground.mdx';

## Features

Key features of LLM Chat include:

- An automatically-generated [Retool AI query](../../../../queries/concepts/actions.mdx#chat-actions).
- Configurable [AI models](../../../../data-sources/concepts/models.mdx).
- [Retool Vectors](../../../../data-sources/quickstarts/retool-vectors.mdx) to add more context to your queries.
- Event handlers.
- Customizeable styles.

The following video also provides an exploration of the LLM Chat features.

## Specify content options

The **Content** section of the Inspector contains settings that control the content of LLM Chat, such as:

- **AI name** and **Sender name**, which define the name of the user and the AI in the chat.
- **Actions**, which enable the user to copy AI responses.
- **Add-ons**, which customize the layout of the content in the AI chat, including **Avatar** and **Header**.

### Configure Retool AI

When you add the LLM Chat component to the canvas, Retool automatically generates a query that accesses [Retool AI](../../../../queries/concepts/actions.mdx#chat-actions). You can customize this query as necessary.

The query uses your default [Retool AI model](../../../../data-sources/concepts/models.mdx). If you haven't set one up, this is a Retool-managed OpenAI configuration. Click **Configure more models** to [set up a different AI model](../../../../data-sources/guides/integrations/ai/index.mdx) to use in your LLM Chat.

### Add context to answers

In the Retool AI query, you can update the **Input**, **Message history**, and **System Prompt** to reference components and queries in the app. Otherwise, the AI does not have access to the contents of the app. For example, set **System Prompt** to `You are a helpful assistant. Use {{ customersTable.data }} to answer questions about customers, and {{ billingTable.data }} to answer questions about billing.`

Optionally toggle the **Use Retool Vectors to provide more context to your query** setting. [Retool Vectors](../../../../data-sources/quickstarts/retool-vectors.mdx) enables you to use a vector database to store unstructured text from documents and web pages for use with Retool AI. If you enable this setting, the LLM Chat component incorporates information from your the selected vector in responses to users.

### Configure data streaming

[Data streaming](../../../../data-sources/concepts/streaming.mdx) is enabled by default on queries that use the **Generate chat response** action type. Streaming enables LLM Chat to incrementally update the user interface as soon as a row or chunk of data arrives, and the `data` property continuously updates.

To turn off data streaming for LLM Chat, open the **Advanced** settings of your Retool AI query and uncheck the **Stream response data and update app model incrementally** setting. 

## Configure user interaction

The **Interaction** section of the Inspector contains settings that control user interaction, such as:

- **Query to trigger**, which identifies which query to trigger when a user sends a message. You can update this setting to trigger any other [Retool AI query](../../../../queries/guides/ai/chat.mdx).
- **Disable send**, which disables the input field and send button.
- **[Event handlers](../../interaction-navigation/event-handlers.mdx)**, which listen for and handle interactions with your components.

As with other components, you can access LLM Chat component properties using [JavaScript](../../../../queries/concepts/javascript.mdx). For example, LLM Chat components have a `messageHistory` property that contains all messages in the chat log. You can reference specific messages using `{{ chat1.messageHistory[0].content }}`.

import Appearance from "/docs/_shared/_appearance.mdx";

---

## Getting started with Comment Thread

The [Comment Thread](../../reference/components/special-inputs/comment-thread.mdx) component enables users to post comments and start discussions directly within your apps. All users who have access to an app that contains Comment Thread can use the component. Each user's comment appears with their name and avatar image.

import WebPlayground from '/docs/_partials/_components/_web-playground.mdx';

## Features

Key features of the Comment Thread component include:

- Dynamic comment threading to associate comments with Table rows or List View items.
- Configurable title, placeholder, and other information.
- Event handlers triggered on submit or click.
- Action buttons to copy or delete messages.
- Granular control over the refresh period.
- Customizeable styles.

## Specify content options

The **Content** section of the Inspector contains settings that control the content of the Comment Thread, such as:

- **Thread ID**: A unique thread ID with which to associate comments. This can be a dynamic value so that comment threads dynamically change based on user interactions. For example, setting the thread ID to `{{ table1.selectedRow.id }}` associates comments to whichever row is currently selected.
- **Title**: A title that appears in the Comment Thread header when **Show title** is toggled on.
- **Placeholder**: A message that appears in the input field before a user types a message.

Use the **Add-ons** section to [configure user avatars](../presentation-styling/avatar.mdx) and the message that appears in the empty state.

export function AddThread() {
  return (
    
      
    
  )
}

:::note

Thread IDs are used across all of your Retool apps. For example, Comment Threads with a Thread ID `thread_123` all display the same thread, no matter which app the Comment Thread component is in.

:::

Retool does not implement comment retention time limits; comments are preserved in their threads indefinitely.

## Configure user interaction

The **Interaction** section of the Inspector contains [event handlers](../interaction-navigation/event-handlers.mdx), which listen for and handle interactions with your components. For the Timeline component, events trigger when users send a message or click a message action.

Use the **Actions** setting to configure the actions available for each message in the thread. All Comment Threads automatically include the   message action, which you cannot delete. The  message action is also automatically included and can be edited in the Inspector.

The **Interaction** section also enables you to configure the refresh interval of the comment thread and disable message sending. For example, if you only want admins to be able to leave comments, you can set **Disable send** to `{{ Object.keys(current_user.groups.find((g) => g.name === "admin")).lenth === 0 }}`.

### Save comments in Retool Database

You can choose to log comments to [Retool Database](../../../data-sources/quickstarts/retool-database.mdx) to preserve them for use in other apps. For example, you can store user feedback or persist team discussions.

:::note

Without completing these steps, comments are not otherwise retrievable or downloadable from Retool.

:::

First, [create a new table in Retool Database](../../../data-sources/guides/retool-database/manage-data.mdx), titled `comments`, with the following columns:

- `id` as a primary key.
- `body` for the content of the message.
- `name` for the name of the sender.
- `user_id` to uniquely identify the user.

In your app, create a new Retool Database query that inserts a record into the `comments` table. Pass in the current user's ID and name, and send the comment's body with `{{ body }}`.

Finally, add an event handler for the Comment Thread that triggers on the `Submit` event. Set the event handler to run the following script, which sends the comment body using `additionalScope`:

```JavaScript
query1.trigger({
  additionalScope: {
    body: commentThread1.value
  }
})
```

:::note

While you can delete or edit comments at the database level, these changes are not reflected in the actual Comment Thread component. The component does not support editing comments or triggering events when a comment is deleted.

:::

import Appearance from "/docs/_shared/_appearance.mdx";

---

## Getting started with date and time components

import Interaction from "/docs/apps/guides/forms-inputs/_partials/_interaction.mdx";
import Appearance from "/docs/_shared/_appearance.mdx";
import Content from "/docs/apps/guides/forms-inputs/_partials/_content.mdx";

You can use the following components to accept date or time inputs:

- [Calendar Input](../../reference/components/date-time-inputs/calendar-input.mdx)
- [Date Range](../../reference/components/date-time-inputs/date-range.mdx)
- [Date Time](../../reference/components/date-time-inputs/date-time.mdx)
- [Date](../../reference/components/date-time-inputs/date.mdx)
- [Day](../../reference/components/date-time-inputs/day.mdx)
- [Month](../../reference/components/date-time-inputs/month.mdx)
- [Time](../../reference/components/date-time-inputs/time.mdx)
- [Year](../../reference/components/date-time-inputs/year.mdx)

import WebPlayground from '/docs/_partials/_components/_web-playground.mdx';

## Features

Key features of date and time inputs include:

- First day configuration.
- Manual or mapped data sources.
- Time zone management.
- Input validation, including minimum and maximum values.
- Style configuration.

### Define date and time format

For the Date, Date Time, Time, and Date Range components, you can define the **Format** setting of the user input component. This setting accepts a string in Unicode Locale Data Markup Language. Refer to the [Unicode symbol reference](https://www.unicode.org/reports/tr35/tr35-dates.html#Date_Field_Symbol_Table) for more information.

:::note

Regardless of the **Format** setting, the **Default value** of Date and Date Time must adhere to the [ISO 8601 format](https://www.iso.org/standard/70907.html). For example, `"2024-01-01T12:00:00.555Z"` would be an acceptable value.

:::

### Manage time zones

import Shared from '/docs/_shared/_time-zones.mdx';

### Define mapped date values

The Day, Month, and Year components each include a **Mode** setting, which enables you to choose between **Manual** and **Mapped** data to define your input choices. When using the **Mapped** option, you can set the **Data source** to a [query](../../../queries/index.mdx), [transformer](../../../queries/guides/transformers.mdx), or [JavaScript](../../../queries/concepts/javascript.mdx).

For example, create a transformer with the following code, which defines the months of the year in Spanish. Save and run your transformer before continuing.

```javascript
const spanishMonths = ["Enero", "Febrero", "Marzo", "Abril", "Mayo", "Junio", "Julio", "Agosto", "Septiembre", "Octubre", "Noviembre", "Diciembre"];

return spanishMonths;
```

Then, drag the Month component onto the canvas. Change the **Mode** to **Mapped**, and set the **Data source** to the transformer you just created. The Month component now enables you to choose between the months in Spanish.

---

## Getting started with file inputs

import Interaction from "/docs/apps/guides/forms-inputs/_partials/_interaction.mdx";
import Appearance from "/docs/_shared/_appearance.mdx";

Retool provides a number of components that enable users to upload files in web apps:

- [File Button](../../reference/components/special-inputs/file-button.mdx)
- [File Dropzone](../../reference/components/special-inputs/file-dropzone.mdx)
- [File Input](../../reference/components/special-inputs/file-input.mdx)

## Features

Key features of file inputs include:

- Configuration of file types permitted.
- Configuration of the number of files permitted.
- Automatic file parsing.
- Input validation, including file size and number of files.
- Style options.

To see it in action, add one of these components to the canvas. This enables users to upload files from the web app.

## Specify content options

The **Content** section of the Inspector contains settings that control the content of an input field, such as:

- File types.
- Selection type.
- Parse files.

### Accepted file types

Use the **File types** setting to specify an array of RFC2046 media types to allow for upload. The following examples are for common use cases.

import Tabs from "@theme/Tabs";
import TabItem from "@theme/TabItem";

The following list contains RFC2046 media types for commonly used files, such as PDFs and Microsoft Word documents.

```json Document media types types
[
  "text/*",
  "application/*",
  "application/pdf",
  "application/msword",
  "application/vnd.ms-excel",
  "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
  "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
  "application/json",
  "application/rtf",
  "application/xml"
]
```

Use `['image/*']` to allow the upload of any image.

Leave the **File types** setting empty to allow the upload of any file.

Refer to the [RFC2046 reference](https://www.iana.org/assignments/media-types/media-types.xhtml) for the full list of media types.

### Single or multiple selection

Use the **Selection type** setting to specify whether users can upload a single file, multiple files, or a directory of files. If you allow multiple files, you can also specify a minimum or maximum number of files to allow.

### Parse text files

Retool can parse uploaded text files (any file with a `text/plain` [media type](#accepted-file-types)) and make the content available for use in the app. Enable the **Parse files** setting in the Inspector.

When enabled, the component's `parsedValue` property contains an array of strings for parsed text. The index of each item corresponds to the index of the uploaded file in `value`.

:::caution

While Retool does not limit the size of file uploads, we recommend defining a maximum file size using the **Validation rules** setting to prevent the browser from crashing.

:::

### Save uploaded files and images

You can reference `value` in queries to [store files in a data store](../../../queries/guides/files.mdx), such as Retool Storage or [Amazon S3](../../../data-sources/guides/integrations/object-file-store/amazon-s3.mdx).

:::tip Save files directly to Retool Storage

You can enable the **Upload file to Retool Storage** setting for the component to automatically store the file in Retool Storage. Retool Storage has a file size limit of 40 MB. For more information, refer to the [Retool Storage quickstart](../../../data-sources/quickstarts/retool-storage.mdx).

:::

## Reference uploaded file data

Uploaded files are Base64-encoded. The component's [value](../../reference/components/special-inputs/file-input.mdx#property-value) property contains an array of `file` objects that represent each uploaded file.

`value` is always an array of file objects, regardless of whether a user uploads a single file or multiple files. Single-file uploads are accessible using `value[0]`.

You can reference Base64-encoded values anywhere in Retool that accepts a string value, including URLs. For example, you can set the URL of an Image component to `{{ fileInput1.value[0] }}` to display an uploaded image.

---

## Getting started with the Form component

import FormIntro from '/docs/apps/guides/forms-inputs/_partials/_form.mdx';

This guide walks you through building an app and form using the Form component. You can fill in the form to create a record or update existing data by selecting a record from a table and populating the form. The form also increments IDs automatically.

Try out the app to see the form in action (submissions are disabled).

## Features

Key features of Form include:

- Automatically generate a form schema from a database or table.
- Manually add any component to the form.
- Input validation of the form.
- Customizeable styles.

## Specify content options

The **Content** section of the Inspector contains settings that control the content of your form, including **Data source** and **Add-ons**.

### Generate the inputs

You can manually add form inputs or generate them from a resource or table. To generate inputs, add a form to the canvas and click **Generate from database**. This opens a modal where you can select a resource or table, along with the inputs.

:::note

If your app already includes a table, your new Form includes a **Generate from table** option, instead of **Generate from database**.

:::

Retool evaluates each column to set an appropriate label and input type, but you can edit these properties as well. You can uncheck individual columns to exclude them from the form. This is useful if you don't want users to provide certain information, such as IDs or timestamps. You can also change the generated input type or label, and whether the field should be required.

Each input is generated with a value for **Form data key**—shown in the **Interaction** section of the Inspector—that corresponds to the column name from the table. The **Form data key** is the key used to identify data in the `form.data` object.

import Interaction from "/docs/apps/guides/forms-inputs/_partials/_interaction.mdx";

### Set the form title

Update the form's title so it's clear to the user if they are creating a record or updating an existing one. Select the form's title and change the **Value**:

```javascript
#### {{ table1.selectedRow ? 'Edit' : 'Create new' }} product
```

This displays either **Edit product** or **Create new product**, depending on whether a table row is selected or not.

### Validate submissions

Forms can check the validation status of inputs. For more information, refer to the [Input validation guide](../input-validation.mdx#validate-form-submissions).

### Clear the form

To clear values from the form after submissions, enable **Reset after successful submit** in the **Interaction** section of the form's Inspector. This setting resets the values to those in the form's **Data source** field.

import Appearance from "/docs/_shared/_appearance.mdx";

### Create a query to update the dataset

When you generate a Form using the **Generate from database** option, Retool automatically creates a query (`form1SubmitToProducts` in the screenshot) to insert new records into the database when the form is submitted. 

If your form is generated from a table or manually, you must create this query manually and attach it to the form as an [event handler](../../interaction-navigation/event-handlers.mdx).

### Use the form to update existing records

You can also use this form to make changes to an existing record. A common approach is to populate a form with data from a selected table row and then submit changes to update the record.

Use the **Data source** property to automatically populate the form's inputs using the selected table row data. The Form component matches each input's **Form data key** to a corresponding column name from the table to determine where values should go.

For example, this ternary operator sets the form's initial data only if a table row is selected:

```javascript
{{ table1.selectedRow ? table1.selectedRow : null; }}
```

If set, a form's **Data source** overrides any default values for nested components.

#### Change the query action type

The form's [existing query](#create-a-query-to-update-the-dataset) only creates new records. However, you can update the existing query to update records when the form is submitted. Select the existing query, `form1SubmitToProducts`, and change the **Action type** to **Bulk upsert via primary key**.

:::warning

Remember to save changes to your queries by clicking **Save**.

:::

This action requires a **Primary key** column in the Table so the query knows which record to update. This should be a unique identifier to avoid incorrect changes. In this case, set the value to `id`.

When the form is submitted, the query looks for an existing record using the provided ID. If there's a match, that record is updated. If not, the query creates a new record instead.

#### Include the form data

The form data isn't included in the new action type. Set the **Array of records to update** to `{{ [form1.data] }}`. Note that the form data is wrapped in brackets to provide it as an Object.

### Refresh the table after submission

You might want to automatically update other components in your app (like a Table) based on the database changes. You can create an [event handler](../../interaction-navigation/event-handlers.mdx) to refresh the table to show the changes automatically:

1. Add a new event handler for a **Success** event.
2. Select **Trigger query** as the action to perform.
3. Select **query1**. Triggering this query retrieves the dataset, including your additions, and updates the table.

The **Styles** setting updates the style of the Form component as a whole. The **Nested styles** setting applies changes across all components inside the Form.

## Customize the form

The following section includes additional ways that you can consider customizing your form.

### Increment IDs automatically

The form in this example currently allows users to specify an ID. Since this form also creates and updates records, users could accidentally overwrite a record if they provide an existing ID. Instead, you should automatically increment the ID and prevent users from providing one in the form.

:::warning

The following guidance is provided for demonstration purposes. Retool recommends using your database's auto-increment feature to create unique identifiers automatically.

:::

For this guide, use `Math` to find the highest ID number and use it to increment to the next ID automatically:

```javascript
Math.max(...query1.data.id) + 1;
```

First, disable the ID input by setting the value for **Disabled** to `true` in the **Interaction** section of the Inspector. Next, update the form's Initial data to provide a new ID if a row isn't selected:

```javascript
{
  {
    table1.selectedRow
      ? table1.selectedRow
      : { id: (Math.max(...query1.data.id) + 1).toString() };
  }
}
```

### Display image preview

The Form component can contain any other components, not just inputs. You can include an optional image preview using the provided image URL.

#### Add toggle

1. Drag a [Toggle Link](../../../reference/components/buttons/toggle-link.mdx) component and position it below the input fields. This toggle is used to show or hide the image preview.
2. Update the value for **Text** to `{{ self.value ? 'Hide' : 'Show' }} image preview`.
3. Set the value for **Disabled** using the value of the image URL input, such as `{{ !textInput2.value }}`. This disables the toggle if the input doesn't have a value.

Toggle Link has a value of `false` when in a closed state and displays the text **Show image preview**.

#### Add image

1. Drag an [Image](../../../reference/components/presentation/image.mdx) component and position it below the toggle.
2. Set the **Image source** using the value of the input, such as `{{ textInput2.value }}`.
3. Set the value for **Hidden** to `{{ !toggleLink1.value || toggleLink1.disabled }}`. This hides the image preview if Toggle Link is either disabled or returns a value of `false`.

The image preview can now be toggled but is hidden automatically if no image URL is present.

---

## Getting started with the Form and JSON Schema Form components

Retool provides two components that enable you to gather user input from within an app: [Form](./form-component.mdx) and [JSON Schema Form](./json-schema-form.mdx).

:::warning

This section covers only in-app forms. You can also create standalone forms using [Retool Forms](../../../../forms/index.mdx).

:::

The following tabs outline the differences between the form types.

import WebPlayground from '/docs/_partials/_components/_web-playground.mdx';
import FormIntro from '/docs/apps/guides/forms-inputs/_partials/_form.mdx';
import JsonSchemaFormIntro from '/docs/apps/guides/forms-inputs/_partials/_json-schema-form.mdx';

:::note

Retool recommends using the Form component instead of the JSON Schema Form component in most cases.

:::

The Form component includes the following features:

- Drag-and-drop of input components.
- Full control over layout. Supports all components, with control properties in the editor.
- Manually make changes and updates. Use the editor to update inputs and properties.
- Generate forms from an SQL database schema. Automatically adds inputs to the form and creates a query to insert records. 

The JSON Schema Form component includes the following features:

- Inputs defined using JSON.
- Single-column layout, and you cannot manually add additional components. Control properties using JSON.
- Changes and updates are mostly programmatic. Edit JSON to update inputs and properties.
- Generate forms from an SQL database schema. Automatically generates the JSON and UI schema. 

You should consider using JSON Schema Form if:

- **You already use JSON schema-based forms**. You can reuse your existing schema rather than build forms from scratch.
- **You need more flexibility**. You can create programmatic workflows for your forms that would otherwise not be possible or need to be done manually with the Form component (e.g., complex validation needs or granular control of inputs with custom schema).

---

## Getting started with the JSON Schema Form component

import JsonSchemaFormIntro from '/docs/apps/guides/forms-inputs/_partials/_json-schema-form.mdx';

:::info

Retool recommends using the [Form](./form-component.mdx) component as it's more versatile for most use cases. Use the JSON Schema Form component only if your use case requires it. Refer to the [comparison of form components](./index.mdx) for more details.

:::

Try out the demo below to see the form in action (submissions are disabled).

The following guide outlines how to create a JSON Schema Form that enables a user to either edit or create a new entry in a database of sales deals.

## Features

Key features of JSON Schema Form include:

- Automatic form generation from a database.
- The ability to make programmatic updates to the form schema.
- Configurable default form data.
- Input validation and event handlers.
- Customizeable title, description, and submission button text.

:::note
JSON Schema Form does not support Markdown formatting for labels and captions.
:::

## Specify content options

The **Content** section of the Inspector contains settings that control the content of your form:

- **JSON Schema**: The JSON schema for the form, which uses [react-jsonschema-form](https://github.com/rjsf-team/react-jsonschema-form).
- **UI Schema**: The [UI schema](https://rjsf-team.github.io/react-jsonschema-form/docs/api-reference/uiSchema/) for the form, which provides information on how the form should be rendered.
- **Default Form Data**: The default form data, which is displayed inside the input fields.

### Generate the inputs

You can create a JSON schema from scratch or by using the **Generate schema from a database** option. Use this option to select a connected PostgreSQL, MySQL, or MS SQL database and corresponding table, and Retool generates a **JSON Schema** and **UI Schema** for your JSON Schema Form. You can then manually edit the schemas.

This example shows how to connect the JSON Schema Form to a `deals` table in the `onboarding_db`, a PostgreSQL database.

### Define default data

Use the **Default Form Data** setting to display a certain set of default form data. You can set this manually or define it programmatically. For example, you could set the default values to be the values of the currently selected row. The following code snippet shows an example of how you use data from a row if one is selected or provide default values if one is not:

```json
{
  "email": {{ table1.selectedRow ? table1.selectedRow.email : "email@example.com" }},
  "amount": {{ table1.selectedRow ? table1.selectedRow.amount : 1000 }},
  "stage": {{ table1.selectedRow ? table1.selectedRow.stage : "Lead, Opportunity, or Customer" }},
  "deal_name": {{ table1.selectedRow ? table1.selectedRow.deal_name : "Company Name" }}
}
```

### Set the form title

Update the form's title so it's clear to the user if they are creating a record or updating an existing one. In the **JSON Schema** setting, update the form's title to `"title": "{{ table1.selectedRow ? 'Edit' : 'Create new' }} deal"`.

This displays either **Edit deal** or **Create new deal**, depending on whether a table row is selected or not.

## Configure user interaction

You can configure the user's interaction with the JSON Schema Form through input validation, event handlers, and other mechanisms in the **Interaction** section of the Inspector.

### Input validation

You can apply [validation rules](https://rjsf-team.github.io/react-jsonschema-form/docs/usage/validation/) in the JSON schema to limit what values your form can accept and which fields are required. For more information, refer to the [Input validation](../input-validation.mdx#validate-form-submissions) guide.

### Add an entry to the database

When the user clicks **Submit**, you need to add their responses to the database. Create a new query that adds the deal information to the database.

In this example, you can create a SQL query that accesses the `deals` table in the `onboarding_db` resource. Set **Action type** to **Update a record, or create a new record if it doesn't exist**. Filter by the `deal_name`. 

Add the key-value pairs that you want to include in the new or updated entry. For example, set `amount` to `{{ jsonSchemaForm1.data.amount }}` to use the submission details.

Next, configure the JSON Schema Form's event handler to run this query when the **Submit** button is pressed.

### Refresh the table after submission

You might want to automatically update other components in your app (like a Table) based on the database changes. Create an [event handler](../../interaction-navigation/event-handlers.mdx) to refresh the table and show the changes automatically:

1. Return to the query you created. Add a new event handler for a **Success** event.
2. Select **Trigger query** as the action to perform.
3. Select **getDeals**. Triggering this query retrieves the dataset, including your changes, and updates the table.

## Customize appearance

Customize the appearance of your form using the **UI schema** setting. Explore the [uiSchema object documentation](https://rjsf-team.github.io/react-jsonschema-form/docs/api-reference/uiSchema/) for more information about the customizations available.

You can also customize the presentation of your component in the **Spacing** and **Appearance** sections of the Inspector, although these settings are limited.

---

## Forms and inputs



---

## Validate inputs and forms

Many input components support validation rules. These rules enforce certain requirements on user inputs, such as minimum or maximum length. Configure validation settings in the **Interaction** section of the Inspector.

The validation rules available depend on the type of component you're using. The following lists include common rules:

Validation rules common across [number input](./number-inputs.mdx) components:

| Rule       | Description                                                                                 |
| ---------- | ------------------------------------------------------------------------------------------- |
| Allow null | Whether to allow a null value. If true, the input's cleared state is `null` instead of `0`. |
| Minimum    | The minimum value to accept.                                                                |
| Maximum    | The maximum value to accept.                                                                |
| Required   | Indicates that a value must be provided for this input.                                     |

Validation rules common across text input components:

| Rule       | Description                                                                                 |
| ---------- | ------------------------------------------------------------------------------------------- |
| Pattern    | A limitation on the type of string to accept. Options are **Email**, **Regex**, or **URL**. |
| Min length | The minimum number of characters to accept.                                                 |
| Max length | The maximum number of characters to accept.                                                 |
| Required   | Indicates that a value must be provided for this input.                                     |

Validation rules common across [file input](./file-inputs.mdx) components:

| Rule      | Description                                                                                         |
| --------- | --------------------------------------------------------------------------------------------------- |
| Min files | The minimum number of files that can be uploaded.                                                   |
| Max files | The maximum number of files that can be uploaded.                                                   |
| Min size  | The minimum size of uploaded files with optional file size units. Without units, measured in bytes. |
| Max size  | The maximum size of uploaded files with optional file size units. Without units, measured in bytes. |
| Required  | Indicates that a value must be provided for this component.                                         |

Validation rules common across [date and time input](./dates.mdx) components:

| Rule             | Description                                             |
| ---------------- | ------------------------------------------------------- |
| Min date or time | The minimum allowed date or time.                       |
| Max date or time | The maximum allowed date or time.                       |
| Required         | Indicates that a value must be provided for this input. |

Validation rules common across special input components:

| Rule     | Description                                             |
| -------- | ------------------------------------------------------- |
| Required | Indicates that a value must be provided for this input. |

:::note

These lists are not comprehensive. Check the **Validation rules** of your component for the full list.

:::

## Apply custom validation rules

You can also create a **Custom rule**. Custom rules support any JavaScript expression that evaluates to a truthy value.

Optionally, set this field to evaluate to a string, which the component displays as an error message.

For example, you could create a custom rule with the following value: `{{ new Date().getDay() === 1 ? 'This input is invalid on Mondays' : '' }}`. This rule triggers an error message to display on Mondays. No error message is displayed on all other days.

## Validate form submissions

[Forms](./forms/form-component.mdx) can check the validation status of inputs—enable **Validate inputs on submit** in the **Interaction** section of the Inspector. Each input component supports several validation options. Validation for a selected input is configured in the **Validation** section of the Inspector.

:::note

If any single input fails validation, the form fails validation.

:::

For [JSON Schema Form](./forms/json-schema-form.mdx) components, you define [validation rules](https://rjsf-team.github.io/react-jsonschema-form/docs/usage/validation/) in the JSON schema. You can also enable automatic validation by enabling **Live validation** in the Inspector.

You can also modify input properties of JSON Schema Form components by updating the [type](https://rjsf-team.github.io/react-jsonschema-form/docs/usage/single#field-types) field. This can be useful if fields need to accept null values or you need to modify your form to more closely match your data.

---

## Getting started with number inputs

import Interaction from "/docs/apps/guides/forms-inputs/_partials/_interaction.mdx";
import Appearance from "/docs/_shared/_appearance.mdx";
import Content from "/docs/apps/guides/forms-inputs/_partials/_content.mdx";

You can use the following components to accept number-based user inputs:

- [Currency](../../reference/components/number-inputs/currency.mdx)
- [Editable Number](../../reference/components/number-inputs/editable-number.mdx)
- [Number Input](../../reference/components/number-inputs/number-input.mdx)
- [Percent](../../reference/components/number-inputs/percent.mdx)
- [Phone Number Input](../../reference/components/number-inputs/phone-number-input.mdx)

## Features

Key features of number inputs include:

- Stepper arrows.
- An optional "clear" button.
- Informational icons for currency code and country.
- Decimal rounding.
- Input validation, including minimum and maximum values.
- Style configuration.

### Format presets

Configure the format of your input using the **Format** setting.

:::tip
Currency and Percent components are presets of the Number Input component with preconfigured **Format** settings.
:::

### Decimal places

The Number Input component and its [presets](#format-presets) accept integers (numbers without a decimal place) and floating-point numbers (numbers with a decimal place).

Use the **Decimal places** setting to configure the maximum number of digits after the decimal. Retool rounds numbers to adhere to the configured decimal places when the input exceeds this limit.

The following defaults apply to numeric user inputs:

- **Standard format** defaults to up to three decimal places.
- **Percent format** defaults to up to three decimal places. The user's input is converted to a floating-point number `value` between 0 and 1.
- **Currency format** defaults to the standard number of decimal places for that currency.

:::note

JavaScript uses the IEEE 754 double-precision floating-point format, which allows for up to 16 significant decimal digits.

:::

Checking the **Pad decimal places** setting includes trailing zeros to match the specified number of decimal places.

### Currency code

The Currency component requires the **Currency code** setting. This setting accepts three-letter country codes defined by [ISO 4217](https://www.iso.org/iso-4217-currency-codes.html). Retool updates the icon to the left of the input field with the corresponding code or icon.

The **Currency code** setting defaults to USD.

---

## Define option lists

Many components present a list of options, such as the dropdown in a [Select](https://retool.com/components/select) or the menu of a [Split Button](https://retool.com/components/split-button). Each option contains a number of settings depending on the component—`value`, `label`, `caption`, `tooltip`, and `disabled`, and more.

import Shared from '/docs/_shared/_define-option-lists.mdx';

import Nested from '/docs/_shared/_nested-option-lists.mdx';

---

## Getting started with select inputs

import Interaction from "/docs/apps/guides/forms-inputs/_partials/_interaction.mdx";
import Appearance from "/docs/_shared/_appearance.mdx";
import Content from "/docs/apps/guides/forms-inputs/_partials/_content.mdx";

You can use the following components to present a set of options from which users can select:

- [Cascader](../../reference/components/select-inputs/cascader.mdx)
- [Listbox](../../reference/components/select-inputs/listbox.mdx)
- [Multiselect](../../reference/components/select-inputs/multiselect.mdx)
- [Multiselect Listbox](../../reference/components/select-inputs/multiselect-listbox.mdx)
- [Radio Group](../../reference/components/select-inputs/radio-group.mdx)
- [Segmented Control](../../reference/components/select-inputs/segmented-control.mdx)
- [Select](../../reference/components/select-inputs/select.mdx)

import WebPlayground from '/docs/_partials/_components/_web-playground.mdx';

Most select input components include an **Add ons** section, which enables you to add items such as a label or tooltip to the component.

### Define options

You define the options for most select input components manually or dynamically by mapping to an array of data. Refer to the [Define option lists](./option-lists.mdx) guide for more information.

### Identify parent

Cascader uses the **Parent value** setting to identify where each option should sit in the nested option list. Items without a parent value appear at the top level. The value of the **Parent value** setting must correspond a key in the dataset, which identifies the parent of the item. The value of this key must correspond to the `value` of the parent item.

For example, to create a Cascader, you could have a JavaScript query, `query1`, that includes the following code: 

```JavaScript
const jobs = [
  {label: 'Engineering', value: 'eng'},
  {label: 'Software Developer', value: 'dev', department: 'eng'},
  {label: 'QA Tester', value: 'qa', department: 'eng'},
  {label: 'Product', value: 'prod', },
  {label: 'Product Manager', value: 'pm', department: 'prod'},
];

return jobs;
```

Set the **Data source** of the Cascader to `query1`, and set **Parent value** to `{{ item.department }}`. Note that the `eng` department corresponds to the `value` of another item in the array.

The Cascader is structured as follows:

- Engineering
  - Software Developer
  - QA Tester
- Product
  - Product Manager

export function Cascader() {
  return (
    
      
    
  )
}

You can set the value of a selection component programmatically using the `.setValue()` method.

### Select all options

Multiselect and Multiselect Listbox both enable a user to select all options in the list using the UI, or programmatically with the `.values` property or `.setValues()` method.

For example, you could add a Checkbox component that, when checked, selects all values for the Multiselect component. To accomplish this, add a Multiselect component and a Checkbox component to the canvas.

In the Checkbox inspector, add an event handler with the following settings:

- **Event:** True
- **Action:** Control component
- **Component:** `multiselect1`
- **Method:** Set value
- **Value:** `{{ multiselect1.values }}`

Use the **Show clear button** setting to show an icon that lets the user clear the input field and return it to the **Placeholder** text. Multiselect Listbox includes **Show footer** setting instead, which displays **Select all** and **Clear** options in the footer.

---

## Getting started with sliders and ratings

Sliders and ratings are components that enable a user to define a number value or a range of number values between a minimum and a maximum. Use these components in [forms](../forms-inputs/forms/index.mdx) to collect user feedback or as controllers for other components.

Retool provides the following range and rating components:

- [Range Slider](../../reference/components/number-inputs/range-slider.mdx), which enables a user to select a range of number values using a slider.
- [Rating](../../reference/components/number-inputs/rating.mdx), which enables a user to select a number value on a scale using icons.
- [Slider](../../reference/components/number-inputs/slider.mdx), which enables a user to select a number value using a slider.

import WebPlayground from '/docs/_partials/_components/_web-playground.mdx';

## Features

Key features of sliders and ratings include:

- Configurable start values and minimum and maximum.
- Optional icons, labels, and tooltips.
- Event handlers triggered on change.
- Input validation.
- Customizeable styles.

## Specify content options

The **Content** section of the Inspector contains settings that control the content of a component, such as:

- **Default value**, which defines the initial value of the component. This is **Start value** and **End value** for Range Slider.
- **Minimum** (if applicable) and **Maximum**, which define the bounds of the input component.
- **Add-ons**, which define any labels, icons, and tooltips.

import Interaction from "/docs/apps/guides/forms-inputs/_partials/_interaction.mdx";

Event handlers are triggered on changes to the value of the component. Use event handlers to call queries, control components, or perform another action.

For example, you can use a Range Slider to dynamically filter a Table. Set the event hander **Action** to **Control component**. Select the table, and update the **Method** to **Set filter stack**. Enter a [list of filters](../../reference/components/data/table.mdx#property-filterstack) that you want to apply to the table. In this case, use the `value.start` and `value.end` of the Range Slider to define a minimum and maximum value for a column:

```json
[ { columnId: "amount", operator: ">", value: {{ self.value.start }} }, { columnId: "amount", operator: "

import Appearance from "/docs/_shared/_appearance.mdx";

The Rating component also includes several **Icon** options in this section.

---

## Getting started with buttons and links

Buttons and links enable users to navigate throughout or interact with your app. You configure these components with event handlers. When clicked, they can trigger a query, control a component, or perform another action.

The [Button](../../reference/components/buttons/button.mdx) component has a variety of presets and variations. Explore the following playgrounds to discover each of them.

import WebPlayground from '/docs/_partials/_components/_web-playground.mdx';

[Button Group](../../reference/components/buttons/button-group.mdx) is a group of individually configurable Button components.

[Close Button](../../reference/components/buttons/close-button.mdx) is a Button preset with a smaller size and a configured **Prefix icon** add-on.

[Dropdown Button](../../reference/components/buttons/dropdown-button.mdx) is a combination of Button and [Select](../../reference/components/select-inputs/select.mdx). When you click the button, a dropdown menu appears. Clicking an option in the menu performs the associated action.

[Outline Button](../../reference/components/buttons/outline-button.mdx) is a Button preset with **Variant** set to **Outline**.

[Split Button](../../reference/components/buttons/split-button.mdx) is a combination of Button and [Select](../../reference/components/select-inputs/select.mdx). When you click the icon next to the button, a dropdown menu appears. Selecting an option from the menu changes the button, and pressing the button performs the associated action. 

[Toggle Button](../../reference/components/buttons/toggle-button.mdx) is a Button preset with the **Text** setting configured to a truthy value. The default for this component has **Show** and **Hide** states.

The [Link](../../reference/components/buttons/link.mdx) component has a variety of presets and variations. Explore the following playgrounds to discover each of them.

[Link List](../../reference/components/buttons/link-list.mdx) is a group of Link components.

[Toggle Link](../../reference/components/buttons/toggle-link.mdx) is a Link preset with the **Text** setting configured to a truthy value. The default for this component has **Show** and **Hide** states.

:::note

The [Table](../data/table/columns.mdx) component supports the **Button** column type, enabling you to use buttons within a table.

:::

## Features

Key features of buttons and links include:

- Configurable icons, text, and accessible names.
- Input validation.
- Event handlers triggered on click.
- Customizeable styles.

## Specify content options

The **Content** section of the Inspector contains settings that control the content of the button or link, such as:

- **Text**, which is displayed on the Button or as the Link text.
- **Accessible name**, which is the name for a Button so that it can be identified via screen reader.
- **Add-ons**, which include icons and tooltips.
- **Actions**, which define a list of options for buttons or links that feature a list, including Dropdown Button, Split Button, and Link List.

You can choose to set the **Text** setting to a string or to a JavaScript snippet, which is useful if you want the text to be dynamic. For example, you could set the **Text** of a logout button to ` Log out {{ current_user.firstName }} `.

### Define options

Dropdown Button, Split Button, and Link List all feature a list of buttons or links. You [define the list of options](../forms-inputs/option-lists.mdx) manually or dynamically map them from an array of data.

## Configure user interaction

The **Interaction** section of the Inspector contains settings that control user interaction, such as:

- **Disabled**, a truthy value that disables the button or link when evaluated to `true`.
- **[Event handlers](../interaction-navigation/event-handlers.mdx)**, which listen for and handle interactions with your components.

Event handlers determine what happens when you click a button or link. Common actions are **Control query**, **Control component**, and **Go to URL**.

:::note

Button and link clicks are non-programmatic, and clicks can only be triggered by direct user action.

:::

### Configure option interactivity

Define interaction for Dropdown Button, Split Button, and Link List on a per-option basis. Select the option you want to edit, and add an event handler in the **Interaction** section of the pop-up.

### Use buttons with forms

Buttons are automatically included with Forms, but you can configure standalone buttons to configure forms, too. Change the **Type** to **Submit**, and select the form that you want to submit. 

import Appearance from "/docs/_shared/_appearance.mdx";

---

## Configure event handlers

import Shared from '/docs/_shared/_event-handlers.mdx';

---

## Interaction and navigation



---

## Getting started with navigation components

Use navigation components to help users quickly find the information they need and move between pages and apps. The following components are helpful:

- [Navigation](../../reference/components/navigation/navigation.mdx): A primary menu to help users quickly move between pages and apps. The Navigation component is automatically included in headers and sidebars when adding using multiple pages. 
- [Breadcrumbs](../../reference/components/navigation/breadcrumbs.mdx): A hierarchical list of links to help users trace their path through an app.

import WebPlayground from '/docs/_partials/_components/_web-playground.mdx';

## Features

Key features of navigation components include:

- Automatic configuration of Navigation when there are multiple pages.
- Manual or mapped menu items.
- Event handlers that navigate users to another app, page, or URL.
- Optional icons for each menu item.
- Customizeable style, including horizontal or vertical orientation for Navigation.

### Alternative navigation options

Retool provides a few other types of components that you can also use to help users navigate through steps or flows on a single page. These components are covered by other guides:

- [Steps](../../reference/components/navigation/steps.mdx): Create a [linear step-by-step flow](../layout-structure/container.mdx#stepped-containers).
- [Tabs](../../reference/components/navigation/tabs.mdx): Create a [set of tabbed options](../layout-structure/container.mdx#tabbed-containers).
- [Wizard](../../reference/components/containers-forms/wizard.mdx): Create step-by-step flows that can [branch into different paths](../interaction-navigation/wizard.mdx).
- [Page Input](../../reference/components/navigation/page-input.mdx), and [Pagination](../../reference/components/navigation/pagination.mdx): Create [options for paginating large amounts of information](./pagination.mdx).

## Specify content options

The **Content** section of the Inspector contains settings that control the content of the navigation components, such as:

- **Mode**, which controls whether the Navigation uses **Manual** or **Mapped** data
- **Menu items** or **Breadcrumbs**, which define the items in the navigation components.
- **Add-ons**, which enable you to add and configure a logo and tooltips. 

You define the options for these components manually or dynamically by mapping to an array of data. Refer to the [Define option lists](../forms-inputs/option-lists.mdx) guide for more information.

### Use Navigation with multiple pages

When you use a Navigation component in a an app with multiple pages, the menu items automatically populate. The **Data source** defaults to `{{ retoolContext.pages }}`, and item **Label** defaults to the page's title, if available.

New _headers_ and _sidebars_ are also automatically populated with a pre-configured Navigation component.

### Manually configure menu items

You can manually configure navigation options in the **Menu items** or **Breadcrumbs** section to configure menu items. Click on a menu item to configure it using the **Edit menu item** settings. Set the **Type** to identify the destination of each menu item. 

For Navigation, you can configure menu items to appear highlighted using the **Highlight** boolean setting. This is useful to visually highlight a menu item for the current page or app.

#### Add dropdown menu items

In Navigation components, you can group menu items together as _nested_ dropdown menus. Each _parent_ menu item contains _child_ menu items (submenu items) that appear within the dropdown menu. Dropdown menu items support one level of nesting—child menu items cannot also be parent menu items.

:::note

Parent menu items cannot be configured to trigger actions on click. This event is used to expand or collapse the menu.

:::

To nest a menu item, drag it below and to the right of the menu item you want to nest it under. A blue line appears as you drag menu items around and indents to the right when nesting.

Parent menu items include a count of all nested items.

## Configure user interaction

The **Interaction** section of the Inspector enables you to set [event handlers](../interaction-navigation/event-handlers.mdx), which listen for and handle interactions with your components.

### Perform actions on click

:::note

Event handlers for Breadcrumbs with a **Type** of **App** or **Custom** are automatically defined. Breadcrumbs must be a **Custom** type if you want to create your own event handlers.

:::

Configure menu items to perform specific actions when clicked. You can create menus that export data, link to other apps or URLs, copy content to the clipboard, or run queries.

When configuring menu items to **Open another Retool Page**, you can optionally include [URL query and hash parameters](../app-management/customize-app-urls.mdx) to pass data to, or configure certain characteristics of, other apps.

You can also configure event handlers to trigger when you click the logo. Select the logo in the Navigation component, and click **+** to add an event handler

### Add a logout button 

To allow users to log out of Retool directly from your app, you can add a logout button.

Add a Button or Icon component and attach a **Click** [event handler](../interaction-navigation/event-handlers.mdx) to it. Set the **Action** to **Go to URL**, and in the **URL** field, append `/logout` to your organization's base URL. After they click the button, users are sent to your organization's login page.

import Appearance from "/docs/_shared/_appearance.mdx";

### Orientation

The Navigation component defaults to horizontal orientation, but it also supports vertical orientation to display a navigation menu alongside content, such as within the [Sidebar frame](../../concepts/ide.mdx#sidebar-frame).

### Mobile layout navigation

If you configure your app with a mobile layout, Retool can automatically configure a slide-in Navigation menu using the Sidebar frame. When viewed on mobile, the Header frame contains a menu button to show or hide the menu.

---

## Getting started with pagination components

Pagination enables you to present large amounts of data in a more manageable way for your users. Retool provides several components that can help users navigate through pages of data or information:

- [Page Input](../../reference/components/navigation/page-input.mdx): An input field to jump to a specific page of data.
- [Pagination](../../reference/components/navigation/pagination.mdx): A sequential set of numbered links to navigate through pages.

import WebPlayground from '/docs/_partials/_components/_web-playground.mdx';

:::note

These components are independent from the [Navigation](../../reference/components/navigation/navigation.mdx) and [Breadcrumbs](../../reference/components/navigation/breadcrumbs.mdx) components, which are primarily used to [navigate between apps or between pages](./navigation.mdx).

:::

## Features

Key features of pagination components include

- Default page and range.
- Automatic adjustment of page numbers to fit width.
- Event handlers to perform actions when a page is clicked.
- Customizeable style.

:::tip

The [Table](../../reference/components/data/table.mdx) component automatically paginates tabulated data and has built-in support for [server side pagination](../data/table/pagination.mdx).

:::

## Specify content options

The **Content** section of the Inspector contains settings that control the content of the pagination components, such as:

- **Default page**.
- **Page count**.
- **Add-ons**.

You can set the **Default page** and **Page count** to an integer or a JavaScript value. The `Math.ceil()` function is useful for determining the number of pages—for example, you could set the **Page count** to `{{Math.ceil(query1.data.count / 5)}}` if you want each page to have five entries.

Page Input enables you to customize the component text using **Prefix text** and **Suffix text** add-ons.

## Configure user interaction

The **Interaction** section of the Inspector enables you to set [event handlers](../interaction-navigation/event-handlers.mdx), which listen for and handle interactions with your components.

import Appearance from "/docs/_shared/_appearance.mdx";

## Pagination example

This guide walks you through an example use case of Pagination, in which a [List View](../../reference/components/repeatables/list-view.mdx) displays a custom-built list view of users with the [Avatar](../../reference/components/presentation/avatar.mdx) component. The list displays information about three users at a time and uses the Pagination component to navigate.

### Create queries

A `getUsers` query retrieves a list of user information from a PostgreSQL database:

```sql title="getUsers"
select * from users
order by id asc
limit 3 offset {{(pagination1.value - 1) * 3}}
```

This query returns information from 3 users at a time. It utilizes the value of a Pagination component (which we will create in a moment) to identify the offset for the query. If `pagination1.value` is `2`, then the query returns users with an `id` of `4`, `5`, and `6`.

An additional query, `getCount`, retrieves the number of entries in the table:

```sql title="getCount"
select count(*) from users
```

### Add a Pagination component

Add a Pagination component to the canvas. In this example, each page should contain a list of three users. Set the **Page count** to `{{ (Math.ceil((getCount.data.count)/3)) }}`.

- `(getCount.data.count)/3` calculates the number of pages by dividing the total count by the number of users to include per page.
- `Math.ceil()` rounds this number up to the nearest whole number. This allows the last page to contain fewer than three users. The `users` table has 13 users, so there are five pages.

### Add a List View component

Next, add a List View component to the canvas, and update the **Data source** to `getUsers`. Add an Avatar component to the first instance of the List View. Update the following settings:

- **Fallback text**: `{{ getUsers.data.name[i] }}`
- **Label**: `{{ getUsers.data.name[i] }}`
- **Caption**: `{{ getUsers.data.email[i] }}`

Now, you have a List View component with an Avatar for each user in the database. Entries are organized into three pages,

---

## Getting started with the Wizard component

import Tabs from "@theme/Tabs";
import TabItem from "@theme/TabItem";

The [Wizard](https://retool.com/components/wizard) component allows you to group components into a series of steps that follow a linear path or branch into multiple ones. It supports running queries on step change or reset, [virtual steps](#add-virtual-step) that display a loading screen, and setting the initial step dynamically. The Wizard component also includes a workflow editor to add or remove steps and define transitions between them.

This guide walks you through building a wizard to deactivate a user. Before doing so, the wizard checks that the user's account is in good standing. If not, you have the option of sending the user an invoice to collect an outstanding balance. The wizard also requires you to provide a reason before you deactivate the user.

## Generate user data

For the purpose of this guide, your app uses sample user information that's generated from our [REST API generator](https://retool.com/api-generator/). This online tool creates a REST API endpoint with a custom dataset for you to interact with.

:::warning Generated APIs are for test purposes only

Generated API endpoints require no authorization and are limited to 150 rows of data. Retool may delete APIs that have not been used in the last 30 days.

:::

Launch the [REST API generator](https://retool.com/api-generator/) and create a new dataset with the following information:

| Column name | Data type                                                 |
| ----------- | --------------------------------------------------------- |
| user        | Email address                                             |
| active      | Boolean                                                   |
| balance     | Random number (set the minimum and maximum range of 0—10) |

Set the **API Name** to `wizard` and **# Rows** to `25`, then click **Generate API**.

Retool automatically generates a custom API and outputs details of the endpoint.

## Query user data

First, create a new app. Retool automatically creates a new query (**query1**) to get started. Select the query and change its **Resource** to **RESTQuery**, then configure it to perform a **GET** request from your generated API endpoint URL (e.g., `https://retoolapi.dev/cZQzL3/wizard`). Click **Save & Run** to save the query and run it.

This API returns sample data with the following structure:

```json
[
  {
    "id": 1,
    "user": "sbrito2q@posterous.com",
    "active": true,
    "balance": 9
  },
  {
    "id": 2,
    "user": "vkubach6t@smh.com.au",
    "active": true,
    "balance": 9
  },
...
]
```

Click the query's name and rename it `getUsers`.

## Build the wizard

Drag a Wizard component onto the Retool canvas. Wizard functions like the [Container](https://retool.com/components/container) component and groups other components together. Next, drag a [Table](https://retool.com/components/table) component onto the Wizard. The table automatically uses the most recent query as a data source to display the API data.

Right click the table and select **Rename**. Rename it to `selectUser`.

### Create additional steps

Select the wizard and click **Edit workflow** in the **Edit** section of the right panel to open the **Workflow editor**. You use this to add additional steps and define paths through the wizard.

:::note Navigate the Workflow editor

You can pan and zoom around the Workflow editor as you build out additional steps.

:::

Rename this first step to **Select user** and add two new steps for **Payment reminder** and **Deactivate user**. Connect **Select users** to both new steps by holding the **Shift** key as you click-and-drag.

### Define paths

The wizard can follow different paths depending on _truthy_ conditions. If the path's **Enable if** value evaluates to **true**, the wizard presents this as a next step. If it evaluates to **false**, the step is not available. Set the value of **Enable if** between **Select user** and each path of the following:

1. **Send payment reminder**: `{{ selectUser.selectedRow.balance > 0 }}`. This enables the path to the **Send payment reminder** step only if there's a balance remaining.
2. **Deactivate user**: `{{ selectUser.selectedRow.balance === 0 }}`. This enables the path to the **Deactivate user** step only if the balance is zero.

### Customize payment reminder step

Each step of a wizard is independent and can contain different components. Click on the **Edit step** dropdown in the **Edit** section of the right panel, then select **Payment reminder**.

To create a form that can be used to send a payment reminder:

1. Drag a [Form](https://retool.com/components/form) component to the wizard and update its title to `Reminder form`.
2. Drag an Email component to the form. Set its value to `{{ selectUser.selectedRow.user }}`.
3. Drag a Currency component to the form. Set its value to `{{ selectUser.selectedRow.balance }}`.

The form automatically uses the email address and the balance as values for the inputs. You could use these fields within a query to send emails using the [Retool Email](../../../data-sources/quickstarts/retool-email.mdx) resource. For the purpose of this guide, you won't configure the form to perform any further action.

### Customize the deactivation step

The last step in the wizard is to deactivate the selected user. Select **Deactivate user** in the **Edit step** dropdown of the Inspector, then drag a [Select](https://retool.com/components/select) component on to the step and rename it to `reasonSelect`. Update its list of options to include:

| Value   | Label                 |
| ------- | --------------------- |
| stopped | Stopped using service |
| cost    | Too expensive         |
| other   | Other reason          |

### Configure a virtual step to deactivate user

You can use a _virtual step_ to automatically trigger a query or conditionally route to a different step. In this case, to deactivate a user and submit the reason.

#### Create deactivation query

First, create a new **RESTQuery (restapi)** query with the following information:

- Action type: **PATCH**
- URL: `[endpoint-path]/{{ selectUser.selectedRow.id }}`
- Body:
  - `reason`: `{{ reasonSelect.value }}`
  - `active`: `false`

Save the query, then rename it to `deactivateUser`.

This query performs a PATCH request with the selected user's ID. It includes the `reason` from the dropdown menu and sets `active` to `false`, disabling the user.

#### Add virtual step

Add a virtual step to the workflow editor named **Reason selected** and toggle **Virtual step** on. Set **On enter query** to the PATCH query you just created. Next, create a path from the **Deactivate user** step to this new virtual step. To ensure that a reason is selected, set this path's **Enable if** to `{{ reasonSelect.value !== null }}`.

Selecting a reason enables the virtual step. As you progress to it, the virtual step triggers the query and deactivates the user.

### Add a final step to verify the changes

Virtual steps appear as loading screens and automatically progress to another step once a query is run. The final step of this wizard verifies the changes have been made.

Create another query to perform a **GET** request that retrieves the deactivated user from the API:

- Action type: **GET**
- URL: `[endpoint-url]/{{ selectUser.selectedRow.id }}`

Update the query's name to `verifyUser`.

Add another step to the wizard and name it **Verification**. Connect a path from the **Reason selected** step and set **Trigger if** to `{{ deactivateUser.data }}`, so it immediately moves to the next step after triggering the deactivation query.

Select the **Verification** step and drag a [Text](https://retool.com/components/text) component to it. Update its value to display the query's results.

```
User: {{ verifyUser.data.user }}
Active: {{ verifyUser.data.active }}
```

Your wizard is now ready and can deactivate users. If a user has an outstanding balance, it presents the option to send a reminder instead.

---

## Group components with containers

You can group related components together in a card using the container components. Containers also support multiple views that allow you to separate content into distinct views that users can switch between.

Containers function like any other component. Once added, you can group components within them by dragging them onto the Container.

You can use the following components as containers:

- [Collapsible Container](../../reference/components/containers-forms/collapsible-container.mdx)
- [Container](../../reference/components/containers-forms/container.mdx)
- [Link Card](../../reference/components/containers-forms/link-card.mdx)
- [Stack](../../reference/components/containers-forms/stack.mdx)
- [Steps](../../reference/components/navigation/steps.mdx) and [Stepped Container](../../reference/components/containers-forms/stepped-container.mdx)
- [Tabs](../../reference/components/navigation/tabs.mdx) and [Tabbed Container](../../reference/components/containers-forms/tabbed-container.mdx)
  

import WebPlayground from '/docs/_partials/_components/_web-playground.mdx';

## Features

Key features of containers include:

- The ability to group related components together.
- Multiple views and the ability to switch between them.
- Event handlers and other opportunities for interaction.
- Customizeable style options.

## Specify content options

The **Content** section of the Inspector contains settings that control the content in the container, such as:

- Views (if more than one view exists).
- Default view.
- Add-ons.

### Customize the container layout

Containers contain three sections in which you can place components:

- **Header**: The top area that initially contains a Text component for use as a title.
- **Body**: The main area for you to place components. If you enable multiple views, switching views changes the content shown in the body.
- **Footer**: The bottom area that is useful for optional footnotes and supplementary content.

You can show or hide headers and footers using **Add-ons** in the container's **Inspector**.

### Configure multiple views

Containers support multiple views for grouping content. Only one view is visible at a time and users switch between them by changing the current view of the Container component. Switching views applies only to the body of a Container—the header and footer areas are shared across all views.

Select or drag a Container on the canvas and click **+ Enable multiple views** in the **Content** section of the **Inspector**. Once enabled, you manage views and select which view is visible using the option list editor.

To add components to the selected view, select **Add component**. Each component is associated with the view in which it's contained. As you switch between views, only components associated with the current view appear.

### Link to Steps or Tabs

You can link a [Steps](../../reference/components/navigation/steps.mdx) or [Tabs](../../reference/components/navigation/tabs.mdx) component to a Container with multiple views, creating a Stepped or Tabbed Container. Toggle the **Link to a Container** setting and select a Container to link with. To unlink, toggle the option off.

In general, Retool recommends using the preconfigured Stepped or Tabbed Containers.

### Stepped Containers

The [Stepped Container](../../reference/components/containers-forms/stepped-container.mdx) component combines the [Steps](../../reference/components/navigation/steps.mdx) component with a Container, allowing you to create a step-by-step flow through the Container's views. This is useful if you have a multi-step process that users must complete.

You can drag Stepped Containers directly to the canvas. The Container automatically includes a Steps component that maps to views. As you make changes to views, the steps update automatically.

You can use conditional logic to prevent users from progressing to the next step, such as an incomplete selection or missing input, or mark steps as complete.

### Tabbed Container

The [Tabbed Container](../../reference/components/containers-forms/tabbed-container.mdx) combines the [Tabs](../../reference/components/navigation/tabs.mdx) component with a Container, allowing you to create tabbed navigation for the Container's views.

You can drag Tabbed Containers directly to the canvas. The Container automatically includes a Tabs component that maps to views. As you make changes to views, navigation options update automatically.

## Configure user interaction

The **Interaction** section of the Inspector contains settings that control user interaction, such as:

- **Transition**, which customizes the animations upon view change.
- **Hoist loading state**, which shows a loading state when inner components are loading.
- **[Event handlers](../interaction-navigation/event-handlers.mdx)**, which listen for and handle interactions with your components.

### Disable interaction

You can disable interaction with a Container by setting **Disabled** to a truthy value. Disabling a Container also prevents user interaction with any nested components it contains.

### Switch between views

In addition to using Stepped or Tabbed Containers, you can select a Container's current view using event handlers, JavaScript, or a linked Steps or Tabs component.

#### Event handler

Use the **Control component** action and select either the **Set current view key** or **Set current view index** method.

#### Set views with JavaScript

You can write JavaScript (e.g., **JavaScript queries** or **Run script** [event handlers](../../guides/interaction-navigation/event-handlers.mdx)) to select a view. Use `setCurrentView()` with the key for the view or `setCurrentViewIndex()` with the index of the view:

```javascript
container1.setCurrentView("View 1");
container1.setCurrentViewIndex(0);
```

import Appearance from "/docs/_shared/_appearance.mdx";

### Width and height

You have manual control over the width of a Container. You can configure its **Height** to either **Auto** or **Fixed**:

- **Auto**: The height grows automatically based on the content.
- **Fixed**: The height is a fixed size that you manually control. Scroll bars appear if the content exceeds the height.

### Group components with flexible box layouts {#stacks}

The [Stack](../../reference/components/containers-forms/stack.mdx) component is a container with [Flexbox](https://developer.mozilla.org/en-US/docs/Learn/CSS/CSS_layout/Flexbox)-like controls. Rather than use with the 12-column grid layout, Stack arranges nested components horizontally or vertically, and provides additional controls over the layout.

The **Direction** setting specifies the direction that components follow within the container. Select **→** to arrange components horizontally or **↓** to arrange components vertically.

Stack components have additional settings for **Layout** and **Spacing**, including:

- Distribute
- Align
- Gap
- Allow wrap

---

## Getting started with frames

Frames are special containers that enable you to divide and manipulate the app viewport in different ways.

All apps contain a Main frame, which takes up the entire canvas area by default. 

import Frames from '/docs/_shared/_frames.mdx';

## Frame scopes

The [Header](#header-frame) and [Sidebar](#sidebar-frame) frames are globally scoped and persist across all pages. Any components within these frames inherit this behavior and are globally scoped. All other frames (e.g., Modal and Drawer) are page-scoped and only available within the app in which they reside. Find more information about scopes in the [Quickstart](../../quickstart.mdx#global-and-page-scopes).

## Features

- Optional headers and footers for certain frames.
- The ability to group related components together within a frame.
- Event handlers and other opportunities for interaction.
- Customizeable styles.

## Specify content options

The **Component tree** tab of the [IDE](../../concepts/ide.mdx) contains two sections: **Frames** and **Components**. Select a frame to view the components that are nested inside it. 

Some frames (Sidebar, Modal and Drawer) support a header and footer within the frame itself. Enable these using the **Add-ons** setting in **Content** section of the Inspector.

In the following image, you can see all the components that are nested within the **Header** and **Body** of the Drawer component. Note that the **Header** setting is enabled in the Inspector on the right side of the screen.

Main, Header, and Split Frame have no settings in the **Content** section of the Inspector because all settings are applied at the component level.

## Configure user interaction

[Event handlers](../interaction-navigation/event-handlers.mdx) which listen for and handle interactions with your components. You configure these in the **Interaction** section of the Inspector.

The **Show** and **Hide** events enable you to trigger actions when a user opens or closes a frame. For example, in the following screenshot, a **Hide** event handler turns off the **Modal** toggle in the Switch Group by setting the value to `{{ switchGroup1.value.filter(item => item !== 'modal') }}`.

You can also close a Modal or Drawer using the **Close when clicking outside** or **Close when pressing Escape** settings.

import Appearance from "/docs/_shared/_appearance.mdx";

The following settings are useful for customizing frames:

- **Show overlay**: Show an overlay behind a Drawer or Modal component that darkens the rest of the app. Turn off this setting to be able to drag content from Main to a Drawer or Modal frame.
- **Expand content to fit**: Removes all padding and margin, and causes the frame to take up the entire screen. Can only be used when there is a single component in the frame.
- **Hidden**: A truthy value that shows the frame when set to `true`. The `.setHidden` method is also useful for dynamically hiding a frame.
- **Styles**: Updates the style of the frame as a whole.
- **Nested styles**: Applies style changes across all components inside the frame, but not to the frame itself.

---

## Layout and structure



---

## Assemble mobile layouts for web apps

Retool apps are responsive and support different viewport widths. You can configure separate desktop and mobile layouts to customize your app for mobile web users. To do this, you optionally show or hide components on the mobile layout and arrange them as needed. The mobile layout also uses a narrower [12-column grid layout](../../concepts/ide.mdx#grid-layout) to accommodate the smaller viewport size.

Both desktop and mobile layouts use the same component but allow you to change their position independently. Any changes you make to a component's settings, such as labels or event handlers, apply to both views.

## Switch between desktop and mobile layouts

No components are included in the mobile layout by default. Once you add a component, the app IDE [navbar](../../concepts/ide.mdx#navbar) displays a toggle to switch between desktop and mobile layouts.

Use the layout controls to switch between **Desktop** and **Mobile** layouts. Components you add to either layout appear only in the layout you're currently viewing. For example, if you add a component while viewing the mobile layout, it does not appear in the desktop layout until you enable it.

## Add components to the mobile layout

Use the Inspector in the right panel to add selected components to the mobile layout. Click the **Advanced settings** icon in the **Appearance** section, then enable **Show on mobile**.

Retool initially positions components in the mobile layout similar to the desktop layout. If another component is already in the same position on the mobile layout, the app IDE notifies you. Should this occur, reposition any existing components on the mobile layout first to allow for the addition of the other component.

## Arrange components in the mobile layout

You can customize the arrangement of components for each layout separately—repositioning a component in the desktop layout does not alter its position for the mobile layout. Switch to the mobile layout and then position or resize components as needed.

---

## Build modules to reuse queries and components

import Modules from '/docs/_shared/_module-best-practices.mdx';

A module is a collection of components and code that you can build and embed within other apps. Modules enable you to reuse components and code in multiple apps, and modules can pass data to and from the parent app. This can help reduce app maintenance as you only need to update a module; any parent apps immediately reflect the changes.

Common uses for modules include:

- Sharing specific functionality across different apps. For example, a module to send SMS notifications using Twilio could be used in apps used by different teams rather than rebuilding the same functionality multiple times.
- Splitting large or complex apps into smaller, more maintainable parts. Multiple app builders and teams can then maintain their own functionality without making changes to the parent app.

As modules are a type of app, they support many of the same features. You can [import or export modules using JSON](../app-management/import-export.mdx), [set permissions](../../../permissions/guides.mdx) to restrict access, and [create new releases](../app-management/releases-history.mdx).

## Best practices

### Limitations

Modules have the following limitations:

- Modules inherit custom CSS and preloaded custom JavaScript from the parent app, and these settings are unsupported for modules themselves.
- User permissions for modules must be the same as those for the parent app.
- Using modules inside List Views is not supported.
- While it is possible to [create new releases](../app-management/releases-history.mdx) of a module, all apps that contain the module use the live version. Different apps cannot use different versions of the same module.

## Build a module

You can create a module from scratch, by cloning a module from an app, or by copying components to a module.

### Create a new module

To create a new, empty module, navigate to the **Apps** tab in your organization and select **Create new > Module**.

After creating a module, assemble components using the same methods you would use to build an app.

### Add the module to an app

Add a module to your app in the same way you would add a component. Just like with components, you can  reposition and resize modules on the canvas.

Click **+** in the left sidebar to open the **Add UI tab**. Switch to the **Modules** tab and then drag the module onto the canvas.

Once embedded within your app, you can return to editing the module by right-clicking on the module and clicking **Edit module**.

### Export components to modules

You can also select one or more components and export them to a module from within the app editing experience. All the related logic, such as queries, variables, and transformers, are automatically included in the exported module.

To copy components to a module, select the components—click and drag or ctrlcmd + click—and select **Export to module**.

export function Export() {
  return (
    
      
    
  )
}

Once the export is complete, Retool automatically replaces the selection with the newly created module.

## Send data to and from the module

Once you've created your module, you can define it further with inputs and outputs. These enable your module to exchange data with the parent app and other modules.

### Add inputs

You can pass in **data inputs** and **query inputs** to your module from parent apps or other modules.

For example, you could build a module that simplifies how hiring managers review job applicants. The parent app contains a Select component with the list of applicants. Then, add modules that enable the manager to view the total number of applicants, or view each person's portfolio. The modules can be reused in multiple apps without rebuilding the UI and logic. The examples in the following sections explain how to build this app and configure the data inputs and query inputs.

A data input is a property that apps can pass into modules. Use a data input when you want your module to reference a component on the screen, a variable, or a certain value.

From the module editor, add an input using the **Module settings**, found within the Inspector. Set the **Type** to one of the following values:

- **Any**
- **String**
- **Number**
- **Boolean**
- **Enum**

Choose from **String**, **Number**, **Boolean**, or **Enum** for type validation on your module input. Selecting a **Type** enforces stricter validation rules and displays a warning if the input type does not match what you selected. You can also define additional characteristics about the input, such as options for **Enum**, and default values for all types.

:::note

Changing the type of the module input does not alter the actual value passed into the module—invalid values are still passed through as-is. The validation only affects how the input fields behave and display warnings.

:::

If you set the **Type** to **Any**, Retool performs no validation on the module input.

Once your module is configured, navigate to the app editor. Click on the module and add the corresponding input in the **Content** section of the module Inspector.

Example

For example, you could create a module for your applicant review app that displays a preview of a selected candidate's portfolio by using an [IFrame](../../reference/components/custom/iframe.mdx) component.

To create this module, complete the following steps in the module editor:

1. Add an IFrame component to the canvas.
2. Open the **Module settings** and add an input.
3. Update the **Type** to **String**. Optionally set the input **Description** and **Default value**. If the **Default value** is not of the correct type, you will see an error.
4. Select the IFrame, and set the URL to `{{ input1.value.website }}`.

export function DataInput() {
  return (
    
      
    
  )
}

To add this module to the parent app, complete the following steps in the app editor:

1. Drag the module onto the canvas.
2. Set the module's input to `{{ select1.selectedItem }}`.

Query inputs allow you to pass queries into modules from parent apps. The module can then trigger these queries directly, calling them in event handlers and as success and failure trigger queries.

Though query inputs are not defined in modules, you can view them in the query editor and add [event handlers](../interaction-navigation/event-handlers.mdx) to them.

From the module, add a query input using the **Module settings**, found within the Inspector. From the app, click on the module and add the corresponding input in the **Content** section.

Example

For example, you could create a module for your applicant review app that queries a dataset of job applicants to find the total number and display that number to the user using a Statistic component.

To create this module:

1. Create a query that finds the total number of applicants.
2. In **Module settings**, add a query input, which automatically creates a query that is triggered upon the input of the parent app. Set the success event of `input1` to `query1`.
3. Add a Statistic component to the canvas.
4. Update the Statistic component settings to use `{{ query1.data.id }}` as the value.

To add this module to the parent app:

1. Drag the module onto the parent app's canvas.
2. Set the module's input to `getApplicants`, a query that retrieves the full list of applicants.

#### Trigger module queries from parent apps

Use query inputs to trigger queries within a module, not from the parent app defining the query. If you need to trigger a module's query from its parent app, you can use data inputs.

1. In your module, create a data input. You'll use this input to watch for changes from the parent app.
2. In your module, use the **Query JSON with SQL** resource to write a query which runs automatically when your data input changes. The result of this query won't be used. For example, given `input1` as a data input, the query could be:

```sql
select id from {{ input1.value }}
```

3. In your module, define the query you want the parent app to trigger. In the **Query JSON with SQL** query, trigger this query as a **Success** event handler.
4. In the Inspector of your parent app, pass data to the module's data input. To trigger the module query based on the result of a query in the parent app, set the data input to `{{ parentQuery.data }}`. The module then watches for changes to `parentQuery.data`.

To test out this behavior, [download and import the JSON](/img/docs/8ee48756-c6db-48f7-966f-a9049f3c8d04.json) of an example module using data inputs.

#### Test inputs with sample values

Because module inputs are controlled by the parent app, you can pass through test values to preview your module. When you select the module container, the right panel of the app IDE shows a **Test inputs** section. There, you can test your module's behavior by giving expected values for each of your inputs.

### Add outputs

Outputs are properties that parent apps can reference. Outputs are the only way to pass information from your module to a parent app—no other data from within your module is accessible to it. Module outputs must be formatted as strings.

From the module, add an output using the **Module settings**, found within the Inspector.

For example, imagine creating a search field that can be reused between all your apps. To do so, create a module and add a text input field. Create an output, and set the `output1` value to `{{ textInput1.value }}`.

To use the module to search a table within a parent app, drag the module onto the canvas. Select the Table, and set the **Search term** to `{{ moduleSample1.output1 }}`.

## Customize layout

You can also configure the layout of your module using the **Spacing** and **Appearance** sections of the Inspector:

- **Height**: Whether the height of the module is automatic or fixed.
- **Margin**: Whether the module should have a normal margin or none.
- **Overflow**: Whether overflow content in the module is hidden or accessible via scroll.

When you configure these settings inside the module editor, the settings apply as the default values only to new module instances. Existing module instances are unchanged. You can also override these settings for a single module instance by changing them in the Inspector of the app that contains the module.

---

## Build custom views using repeatable components

A repeatable component maps an array of data to repeating instances. Visually, a repeatable component is made up of a series of instances—each instance contains one or multiple components. Assemble components to display within the first instance, and configure them to reference your data source. A series of instances appears, each one corresponding to one item in your array.

[List View](../../reference/components/repeatables/list-view.mdx) is the main repeatable component for apps. The initial state of this component is a vertically-scrolling list of instances, each of which include a Container and are connected to demo data.

The following components are presets of List View:

- [Container List View](../../reference/components/repeatables/container-list-view.mdx), which is a [Container](./container.mdx) that includes a List View component.
- [Grid View](../../reference/components/repeatables/grid-view.mdx), which is a preset of List View with **Layout Type** set to **Grid**.

In the following demo, the underlying configuration for displaying user names, emails, avatars, and other charateristics is only done once. The configuration is then repeated for every user in the data source, resulting in the grid-like view.

## Features

Key features of repeatable components include:

- One-time configuration of repeatable components.
- Dynamic loading of source data.
- The ability to add several nested components in each instance.
- Configurable user interaction.
- Custom styles.

## Specify content options

The **Content** section of the Inspector contains settings that control the content in the repeatable components, such as:

- The data source.
- The primary key.

### Select the data

Add a List View component to the canvas and select a **Data source**. You can write [JavaScript](../../../queries/guides/javascript/index.mdx), pass an array, or use [query data](../../../queries/quickstart.mdx) as your data source. The only requirement is that the data source is an array (primitive and object arrays both work).

:::note Set a primary key

Retool tries to set a primary key for List View components when you select a data source. If your app has multiple user input components, make sure the primary key is set correctly. This ensures your app state is correct after users make changes like adding or sorting data.

:::

After selecting a data source, Retool seeds the component with containers for each item in your data source. Add components to the first container, and they're automatically propagated to the other containers. Retool sets some default values, but you use the `item` and `i` variables to display data in each component.

#### The `item` variable

The `item` variable corresponds to the value of an entry in your data source. It's equivalent to referencing `listView1.data[i]`. You can use `item` to access values in your data source, such as `{{ item.id }}` or `{{ item.value }}`.

#### The `i` variable

The `i` variable is a number that represents the index of an entry in your data source. Since a list view is a repeatable component, each repeated instance has a different index which you can use to render and access unique values.

`i` is often used to iterate over a set of values and perform some kind of action. For example, you could iterate over an entire list view to update values, make API requests, send emails, etc. For example, this JavaScript [transformer](../../../queries/guides/transformers.mdx) iterates over all the entries in `listView1`, and returns each sales person with over 200 sales.

```javascript Transformer
var salesPerson = {{ listView1.data }};
var highSales = salesPerson.filter(i => i.sales > 200);
return highSales;
```

## Configure user interaction

The **Interaction** section of the Inspector contains settings that control user interaction. These settings are limited for repeatable components – to configure [event handlers](../interaction-navigation/event-handlers.mdx) and other interactions, select the first Container and make your changes in the Inspector.

### Aggregate form components

If you need to access the value of an input component within a repeatable component, toggle **Enable instance values** on. When enabled, the repeatable component attempts to evaluate all input values and make them available in the `instanceValues` property. 

`instanceValues` is an array of objects for every instance of the component, where each object is a mapping of an input's `formDataKey` to its `value`. This also includes the `primaryKey` of the list item.

For example, you can use a [transformer](../../../queries/guides/transformers.mdx) to iterate over a list of Number Input components and return the sum total:

```javascript title="Reference instance values"
const formData = {{ listViewName.instanceValues }}
const sum = formData.reduce((sum, data) => sum += data.numberInput1, 0)
return sum
```

:::tip Reset or clear instance values

Use `resetInstanceValues()` or `clearInstanceValues()` to reset or clear input values for a List View or Grid View component anywhere in the app. 

:::

import Appearance from "/docs/_shared/_appearance.mdx";

You can customize the overall appearance of repeatable components. Select the component and modify appearance settings like height, layout type, etc.

### Height configuration

Retool recommends using a fixed height for List View cmoponentss. However, you might use auto height when your dataset is less than 25 items or when fixed heights don't otherwise fit your use case. For the latter, Retool recommends using the auto height setting with server side pagination. This requires some additional code to function.

For example, you can use a [ transformer](../../../queries/guides/transformers.mdx) and a [Page Input](../../reference/components/navigation/page-input.mdx) component to return only a subset of values from your data source.

```javascript
const currentPage = {{ pageInput1.value }} - 1
const pageSize = 5
const pageStart = currentPage * pageSize
const pageEnd = pageStart + pageSize

return {{filteredTableData.value}}.slice(pageStart, pageEnd)
```

After you set the List View's data source to the transformer value, you add a **Page Input** component. Page Input components include a **Page count** value that you can set manually, or you can write JavaScript to display the page count dynamically. For example, `{{Math.ceil(filteredTableData.value.length / 5)}}` divides the number of items returned to calculate the page count where each page contains five items. See the demo below for a walkthrough.

## Nest repeatable components

You can nest a repeatable component within another repeatable component. This nesting dynamically generates UI elements based on multidimensional arrays and tree-like data structures—for example, org charts or threaded comments. Nested repeatable components support up to three levels of depth and can also reference nested components using `componentName.item`.

### Access items via namespace

Each List View can access its items and those of its parent and child using namespaces. For example, imagine the following set of nested List Views:

```
listView1
  listView2
    listView3
```

In this example:

- `listView1` can access its item via `item` or `listView1.item`.
- `listView2` can access its item via `item` or `listView2.item`. It can access its parent's item via `listView1.item`.
- `listView3` can access its item via `item` or `listView3.item`. It can access its ancestor's item via `listView1.item` and `listView2.item`.

### Reference nested components

You can reference nested components from other nested components within the same repeatable instance (e.g., `{{ componentName.value }}`). Nested components cannot be referenced outside of a given repeatable. If you need to reference the source data of a list item, you can reference the `data` or `instanceValues` of List View. 

### Demo nested List View

The following demo app uses two List View components. The first contains the teams: Engineering, Marketing, and Sales. The second is nested inside the first and contains the team members.

The `transformedDataJS` and `transformedDataSql` queries convert `sampleData` into objects that you can use in a nested List View.

import Tabs from "@theme/Tabs";
import TabItem from "@theme/TabItem";

  

  Create a JavaScript query named `sampleData` with the following:

```js title="sampleData"
return {
  name: ["Bob", "Kate", "Sarah", "Ali", "Joe", "Lisa", "Lenny"],
  team: [
    "Engineering",
    "Engineering",
    "Marketing",
    "Sales",
    "Engineering",
    "Sales",
    "Marketing",
  ],
};
```

  
  

  Create a transformer named `transformedDataJS` with the following:

```js title="transformedDataJS"
function aggregateSampleData() {
  const teams = _.uniq({{ sampleData.data.team }});
  const aggregated = teams.map(team => ({
    team,
    members: {{ sampleData.data.name }}.filter(name => 
      {{ sampleData.data.team }}[{{ sampleData.data.name }}.indexOf(name)] === team
    )
  }));
  return aggregated;
}

return aggregateSampleData();
```

  
  

  Create a Query JSON with SQL resource query named `transformedDataSQL` with the following:

```sql title="transformedDataSQL
select team, array(name) as members from {{sampleData.data}} group by team
```

  

To configure the demo app, set the parent List View component to use the `transformedDataJs` transformer as its data source. Set the **Value** of the container title to `{{ item.team }}`, which displays the team names. Set the nested List View's data source to `{{ item.members }}`, and set the value of the container title to `{{ item }`, which displays the members of each team. 

You could also transform the data with SQL instead. The demo shows an additional container view that uses `transformedDataSQL` as the data source.

Click through the following Arcade to view the configuration process.

---

## App error reporting and observability

import Observability from "../../../_shared/_observability.mdx";
import Errors from "../../../_partials/_app-errors.mdx";
import Filtering from "../../../_shared/_filtering-routing.mdx";

---

## Observability



---

## App performance monitoring

Use Retool's performance monitoring integration to send page load and query traces to Datadog or Sentry. Once enabled, you can view detailed performance information that helps provide context on potential performance issues on which you can improve.

Queries and page loads that occur while editing an app are not reported.

## 1. Configure observability provider

Perform the following steps to configure your chosen observability provider.

To configure Datadog:

1. [Create a Datadog pipeline](https://docs.datadoghq.com/logs/log_configuration/pipelines/?tab=source#create-a-pipeline) to consume traces. Retool spans will stream from `service:retool_performance_monitoring`.
2. [Create a Datadog API key](https://docs.datadoghq.com/account_management/api-app-keys/) to use in the next step. Note your organization's [Datadog Site](https://docs.datadoghq.com/getting_started/site).
3. Contact your Datadog account manager for access to the [Datadog OTLP Traces Intake Endpoint](https://docs.datadoghq.com/opentelemetry/setup/intake_endpoint/otlp_traces/?tab=javascript), which is in preview.

To configure Sentry:

1. [Create a Sentry project](https://docs.sentry.io/product/sentry-basics/integrate-frontend/create-new-project/) for Retool performance monitoring.
2. Navigate to the Client Keys (DSN) section of your Sentry project's settings. Copy the DSN key to use in the next step.

## 2. Connect providers to Retool

Next, configure your Retool organization to connect to your observability provider. You can configure the connection by either:

- Navigating to **Settings > Observability**.
- Using the [Retool API](../../../api/index.mdx) to programmatically configure observability provider connections.

Configure the **API key** setting with the Datadog API key you created in the previous step. Make sure you specify the correct [Datadog Site](https://docs.datadoghq.com/getting_started/site)

Configure the **DSN key** setting with the Sentry DSN key you created in the previous step.

## Traces

Retool sends traces to Sentry and Datadog, where they appear in the form of a flame graph. Traces are primarily made up of query spans. A query span includes raw query duration and the time that Retool takes to process the result and update the app model. Child spans are included only if they take longer than 10 ms.

- For resource queries, the raw query duration measures the time spent retrieving the result from an external service.
- For JavaScript queries, the raw query duration measures the time spent executing the code (including waiting on async functions called from JavaScript).

Refer to the [Query best practices documentation](../../../queries/concepts/best-practices.mdx) for more information on how to optimize your queries. Retool provides traces in the following categories:

- [Page initialization](#page-initialization)
- [Page navigation](#page-navigation)
- [User interaction](#user-interaction)

### Page initialization

Page initialization traces show how your app performs from initial page load until all initialization queries are completed. Page initialization is broken down into the following metrics:

- **Init:** Time from when the app is opened to when all queries that were triggered on load have completed or the end of Time to Productivity, whichever is longest.
- **Time to First Query (TTFQ):** Time from when the app is opened to when the first query is run. Can be affected by app size, number of plugins, and complexity of custom Javascript.
- **Time to Productivity (TTP):** Time from when the first query is run to when the first [long task](https://developer.mozilla.org/en-US/docs/Glossary/Long_task) (longer than 50 ms) has been completed. This is a proxy for when the app is ready to interact.

The rest of the span shows traces of initialization queries and any dependent queries.

### Page navigation

Page navigation traces show how your app performs when switching pages. Page navigation is broken down into the following metrics:

- **Time to First Query (TTFQ):** Time from when the user initiates a page transition to when the first query on the new page runs.
- **updatePreloadedJS:** Time to run the preloaded JS of the items on the new page. Subset of TTFQ.
- **batchedUpdateTemplate:** Time to load the components on the page. Subset of TTFQ.
- **Time to Productivity (TTP):** Time from when the first query is run to when the first [long task](https://developer.mozilla.org/en-US/docs/Glossary/Long_task) (longer than 50 ms) has been completed. This is a proxy for when the page is ready to interact.

This trace also includes the `previousPage` and `currentPage` attributes, which help identify the pages that the user navigated between.

### User interaction

User interaction traces are triggered when a user performs an [action](../../reference/event-handlers.mdx), such as clicking a Button or typing into a Text Input, that triggers query runs.

## Query subspans

_Query subspans_ are portions of the query run that show a breakdown of what the different actions Retool takes when executing a query.

| Metric            | Definition                                                                                                                                                                         |
| ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Prepare           | Measures the time to calculate all query inputs, format them, and send requests to the Retool backend.                                                                             |
| Backend           | For resource queries only. Measures the time to execute the query and transfer data to and from the Retool backend. This usually makes up the majority of the query duration.      |
| Response handling | Measures the time to handle the raw query response by downloading a file containing query data, or uploading a file to a cloud provider (as in S3 and GCS queries).                |
| Frontend          | For JavaScript queries, parentWindow queries, and Query JSON with SQL. Measures the duration of the JavaScript function. This usually makes up the majority of the query duration. |
| Transformer       | If the query transforms data, measures the time to transform the data.                                                                                                             |
| Post-processing   | Measures the time for Retool to process the data and update the app state.                                                                                                         |

## Tags

Retool provides tags on traces and individual spans to provide additional context around the conditions that generated a specific measurement.

### Trace tags

| Tag              | Definition                               | Example                                                                      |
| ---------------- | ---------------------------------------- | ---------------------------------------------------------------------------- |
| `appName`        | The name of the app.                     | `Pets`                                                                       |
| `appUuid`        | The app identifier.                      | `16423e12-eef4-11ef-b170-eb7362451ab0`                                       |
| `browser`        | The browser type.                        | `chrome`                                                                     |
| `browserVersion` | The browser version.                     | `133.0.0`                                                                    |
| `environment`    | The app environment.                     | `production`                                                                 |
| `os`             | The host operating system.               | `Mac OS`                                                                     |
| `release`        | The Retool version.                      | `3.165.0`                                                                    |
| `traceType`      | The type of action the trace represents. | `init`                                                                       |
| `url`            | The page URL.                            | `http://localhost:3000/apps/16423e12-eef4-11ef-b170-eb7362451ab0/Pets/page1` |
| `user`           | The user info.                           | `username:Alex Wang`                                                         |
| `viewMode`       | The app view mode.                       | `presentation`                                                               |

### Span tags

| Tag                  | Context                                                 | Example                                |
| -------------------- | ------------------------------------------------------- | -------------------------------------- |
| `appName`            | The name of the app.                                    | `Store Manager`                        |
| `appTemplatePlugins` | The number of components.                               | `22` (number)                          |
| `currentPage`        | The current page.                                       | `franchises`                           |
| `dbconnectortime`    | The time to execute resource on backend.                | `600.00ms`                             |
| `isEmbedded`         | Whether the app is embedded.                            | `false`                                |
| `isFirstPageLoad`    | Whether the user navigated to the page directly.        | `true`                                 |
| `javaScriptLinks`    | The number of pre-loaded JS queries.                    | `3` (number)                           |
| `operation`          | The type of action the span represents.                 | `pageload`                             |
| `previousPage`       | The previous page.                                      | `home`                                 |
| `queryId`            | The query identifier.                                   | `dogAPI`                               |
| `queryParams`        | The additional query parameters.                        | `{}`                                   |
| `resourceId`         | The resource identifier.                                | `92c3cbec-bf16-4872-a859-2a8975dd4cf3` |
| `resourceType`       | The type of resource.                                   | `restapi`                              |
| `responsesize`       | The query response size.                                | `3.1 KiB`                              |
| `servedFromCache`    | Whether the result was served from a cache.             | `false`                                |
| `status`             | The query completion status.                            | `Success`                              |
| `traceType`          | The type of action the trace represents.                | `init`                                 |
| `truncated`          | Whether the trace ended before the span could complete. | `false`                                |

## Troubleshooting performance monitoring metrics

Use the recommendations in this section to address issues that you identify with your app performance. Refer to the [web app best practices](../../concepts/best-practices.mdx) and [query best practices](../../../queries/concepts/best-practices.mdx) guides for more general recommendations.

### Time to First Query (TTFQ)

Most of the TTFQ value can be attributed to Retool's initialization process, but there are some areas where the app itself can affect load times. To improve your TTFQ metric, Retool recommends investigating the following spans:

| Span                      | Recommendations                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| ------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `generateDependencyGraph` | This reflects how long it took for Retool to initialize the app’s [dependency graph](../../quickstart.mdx#connect-the-interface-and-code-together). If this span is long, consider reducing the number of app plugins across all pages. You can also simplify complex and deep dependency chains by pulling logic from components into JavaScript queries, calculating iterative values only once, and removing nested modules and containers.Complex [modules](../layout-structure/modules.mdx) should be used sparingly as they can quickly bloat the dependency graph. It is better to use a module once and populate the data dynamically rather than reusing a module multiple times with different data. |
| `fetchScripts`            | This reflects the time spent running your org’s [pre-loaded JavaScript](../../../queries/guides/javascript/custom.mdx). If this span is long, consider reducing complexity or removing unnecessary functions in pre-loaded JavaScript.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |

### Time to Productivity (TTP)

To improve your TTP metric, Retool recommends investigating the following:

- **Identify the bottleneck query:** Look for the query or query chain in the flamegraph that extends TTP the furthest. Reducing the run time of this query or its downstream events will shorten TTP.
- **Find opportunities to parallelize queries:** If a chain of queries is extending TTP, consider re-architecting the app to run more queries in parallel.

### Query run times

To improve your query run times, Retool recommends investigating the following:

- **Consider whether the query needed to run:** By default, many queries run on page load or when referenced values change. If the query was not needed at the time it ran, you could modify your query’s [run behavior](../../../queries/guides/run-behavior.mdx) to be more controlled.
- **Use the run time breakdown:** If a lot of time is spent on the backend, this suggests the query itself needs to be more performant. If a lot of time is spent on the frontend, this suggests that the dependencies downstream of the query need to be simplified.
- **Look at query response size:** A large payload slows down both backend and frontend processing. Modify the query or use pagination to reduce response size.
- **Check the servedFromCache tag:** If the query hasn’t been cached yet and the app doesn’t need immediate data updates, consider [caching the query](../../../queries/concepts/caching.mdx).

---

## Native app testing

Use native testing to create, edit, and run tests from within the **Code** tab of the app IDE. Use this guide to learn more about native testing and how to use it to ensure that your app runs as expected in any scenario.

## Create tests

In the **Tests** section of the **Code** tab, click  to create a new test. Name your test, and choose the method that you want to use:

- **Manual**: Add an empty test and configure each step manually.
- **Record**: Record interactions with your app and automatically generate a test based on your actions.

### Record tests

Retool's test recording mechanism enables you to write tests more quickly than you could from scratch. The recording works by capturing your activity in the editor and translating that into a test.

Use the **Record** button to begin a recording. Start a recording at any point where you want to validate behavior. Each recording automatically captures the app state at the time the recording starts, so you don't need to include setup steps unless you wish to have tests for those events. 

:::note

When a recording is running, the Retool editor behaves the same way it would if you were manually testing it. Be sure that any actions you take during the recording are safe for your app and data.

:::

As you record, you’ll see the actions that Retool captures on the left sidebar. This gives you an indication of how long the generated test will be.

Click **Stop** to conclude the recording. Retool automatically creates a test based on the events that occurred during the recording. You may edit any generated test post-recording so that it is most useful to you.

Each test runs in isolation, and doesn’t make network calls. This means you do not need to worry about any live resources being run or app state being changed when you run tests. This also means you do not need to write any “tear down” logic for your tests.

In order to ensure a good recording output, use the following best practices:

- Do not start a recording while the app has queries in progress.
- Wait for queries to complete before ending a recording.

## Test composition

Each _test_ is composed of a series of _steps_ that are run together to represent a series of user actions and resulting changes to the app state.

You can edit any part of a test, including the following:

- Rename a test by double clicking on its name.
- Edit the steps executed in a test by clicking the  icon next to a step.
- Delete a step from a test by clicking its  icon. 
- Delete an entire test by hovering over the name and clicking the  icon.

Changes are autosaved.

### Global app state

The first step in each test is the **Global app state**. This step is made up of a set of _preconditions_, which define the app state before you started the test.

You can edit preconditions in the following ways:

- Add preconditions by clicking the  icon at the top of the preconditions list.
- Remove preconditions by hovering over the name and clicking the  icon.
- Edit precondition values by clicking the precondition name and opening up the value field. You can type directly in the value field to change it.

### Steps

Each step corresponds with an action either directly taken by a user or indirectly triggered by their behavior. Click the  icon to add a new step. You can test the following actions:

- **Component event trigger**: Configure the **Component** and **Event** that you want to trigger.
- **Resource query run**: Configure the **Component** and **Event** that you want to trigger.
- **JS query run**: Configure the **Component** and **Event** that you want to trigger.
- **Value change**: Configure the **Component**, **Field**, and **Value** that you want to change.

Each step is tested using the validation of _assertions_ and _mocks_.

#### Assertions

_Assertions_ are statements that evaluate as `true` once the original action in the step is complete. For example, a **Resource query run** action might result in updated component or variable values, so an assertion would represent the updated value.

If one or more assertions evaluate as `false`, the step fails.

You can create the following types of assertions:

- **Custom**: A custom assertion using the [Jest testing framework](https://jestjs.io/docs/expect).
- **Model update**: An update to the app state.
- **Run query**: A query was triggered.
- **Utils API**: A method from the [Utils API](../../../queries/reference/libraries/utils.mdx) was called.

You can edit assertions in the following ways:

- Add an assertion by clicking the  button at the top of the assertions list.
- Edit an assertion by changing dropdown values or text input values.
- Delete an assertion by hovering over the assertion and clicking the  icon on the right side.
- Write an assertion using the [JEST framework](https://jestjs.io/docs/expect) by creating a **Custom** assertion from the  dropdown options.

#### Mocks

_Query mocks_ represent any network calls that are run as a result of user action. When you record a test, these calls are executed. During any subsequent test runs, these calls are mocked in order to prevent changes to your data.

You can edit query mocks in the following ways:

- Add query mocks by clicking the  icon at the top of the **Mocks** list.
- Remove query mocks by hovering over the name and clicking the  icon.
- Edit mocked responses by clicking the query name and opening up the value field. You can type directly in the value field to change the mocked response.

## Run tests

Tests must be run manually. At the top of your list of steps, click **Run all** to run each step.

## Known limitations

This feature is still in development and there are several known limitations, which are listed below. If your app contains one of the known limitations or bugs, the recording will likely generate assertions that fail. Retool recommends deleting these assertions from your tests.

- Tests do not currently support mocking any calls to workflows from apps. Workflows will be called rather than being mocked if they are in a test flow.
- Tests do not currently support mocking for AI Action queries. Unexpected behavior may occur if AI queries are in a test flow.
- Tests may not evaluate correctly on apps that use certain beta features, such as app functions.
- Tests using non-deterministic Javascript (e.g. `Math.random`) are not currently supported.
- Date mocking is not available for `Date` calls in user-imported JS libraries. Retool recommends either using custom assertions to rewrite assertions so that they do not use dates, or using the `Date` library directly so that dates can be mocked.
- JS code that uses `fetch()` calls are not supported. Retool recommends re-writing `fetch()` calls to use a Retool query.
- Queries that are set to run periodically, and JS code that includes `setInterval` usage are not currently supported.
- Tests with deprecated or legacy components may cause unexpected behavior.
- Function calls to `window` from preloaded Javascript are not currently supported.
- For tests involving repeatable components (such as List Views), certain parts of generated assertions may not be editable post-recording.
- Tests do not support certain objects, such as localStorage.
- AlaSQL `NOW()` usage is not currently date-mocked.

## Source Control

Tests can also be protected with [Source Control](/source-control) and are stored alongside your app's code in the repository. Source Control support for tests is currently available on cloud instances and will be released for self-hosted instances in a future update.

## Best practices

Use the following best practices to ensure that Retool can successfully record tests on your app. In addition, refer to the [app best practices](../../concepts/best-practices.mdx) for general app building best practices that also apply to native app testing.

import LargeQueries from '/docs/_shared/_large-query-payload.mdx';

---

## Record app user sessions with Fullstory

Retool can record user behavior and interactions with apps using [Fullstory](https://fullstory.com/). When enabled, data about user interactions with apps are reported directly to Fullstory for you to review in detail. You can then analyze app analytics, evaluate impact, and review interactions with [session replay](https://www.fullstory.com/platform/session-replay/).

## Considerations

A [user session](https://help.fullstory.com/hc/en-us/articles/360020624614-What-defines-a-session-in-Fullstory) contains behavioral data, such as interactions, that occur with an app during a period of activity. Retool captures user session data for production apps that are accessed in user mode. This includes any external apps your organization may have.

A new session starts whenever a user launches an app. The session ends when a user leaves the app (such as closing the browser or navigating to a different page). Sessions may also end after a period of inactivity. Refer to [Fullstory's session documentation](https://help.fullstory.com/hc/en-us/articles/360020624614-What-defines-a-session-in-Fullstory) to learn more about session activities.

Retool automatically includes the user's email address for each user session. User-sensitive data is automatically [masked by Fullstory by default](https://help.fullstory.com/hc/en-us/articles/360044349073-Fullstory-Private-by-Default). If you need to display sensitive information in plain text within apps, Retool recommends using [configuration variables](../../../org-users/guides/configuration/config-vars.mdx) or [secrets](../../../self-hosted/guides/secrets/index.mdx) to prevent this information from being captured.

If required, you can also [configure data capture rules](https://help.fullstory.com/hc/en-us/articles/360020623574-How-do-I-protect-my-users-privacy-in-Fullstory#01F5DPW1AJSBTRBN12KFB54AQP) in Fullstory for specific elements using CSS selectors.

## 1. Enable user session recording

Gather the following information from your Fullstory account:

- A [Fullstory](https://app.fullstory.com/) organization. You can choose to create a new organization or use an existing one.
- Your Fullstory [Org ID](https://help.fullstory.com/hc/en-us/articles/360047075853-How-do-I-find-my-Fullstory-Org-Id). Retool uses this ID when sending user sessions to Fullstory.

Then, navigate to the [observability settings](https://login.retool.com/auth/login?source=docs&redirectOnLogin=settings/observability) in your organization and set the **Fullstory orgID**. Choose how you want to handle embedded apps. Once configured, Retool automatically captures and reports user sessions to Fullstory.

export function Fullstory() {
  return (
    
      
    
  )
}

### Embedded apps

import EmbeddedApps from "../../../_partials/_session-replay-embed.mdx";

## 2. Define session elements

Fullstory automatically tags certain Retool components to provide insights, such as elements clicked the most. You can define additional [elements](https://help.fullstory.com/hc/en-us/articles/360063014873-Naming-Elements-in-Fullstory) with Fullstory using [CSS selectors](../../concepts/custom-css.mdx) from Retool components.

---

## Write WebDriver tests

WebDriver tests are a great way to ensure that your Retool app is working as expected.
They can be run on your local machine, against a staging instance, or in a CI/CD pipeline against
your development branch to ensure that your app is working as expected.

The instance on which to run your tests depends on your use case. Since these tests use your
Retool resources, you should not run them against your production instance.

To write your tests in the same repository as your Retool app, put the tests and all WebDriver setup
code in a directory called `user/` in the root of your repository.

## Prerequisites

Before you get started, follow the setup instructions in the [Cypress](https://docs.cypress.io/app/get-started/install-cypress) or [Playwright](https://playwright.dev/docs/intro) documentation.

## Set up a testing account

Retool recommends that you configure a test account with limited permissions. Navigate to **Settings** > **User** in Retool to configure these permissions.

To use 2FA for this account, you need to use a library such as [otplib](https://www.npmjs.com/package/otplib) at the login step.

You might want to disable SSO for this account so that it's easier for your WebDriver to log in.
To use SSO, you need to use a library that works with your SSO provider to log in such as
[the Cypress Okta library](https://docs.cypress.io/guides/end-to-end-testing/okta-authentication).

:::info Billing

If you are concerned about increased costs associated with the additional test account, you can choose to run tests on an existing account.

:::

## Log in

1. Visit the login page at `.retool.com/auth/login` to programmatically log in. For self-hosted, use `/auth/login`.
2. Using your WebDriver, find the username and password inputs by placeholder text.
3. You may want to persist the session between tests so that you don't need to login for every test. See below for examples.

```js title="cypress/support/commands.js"
// Add Cypress Testing Library commands (https://testing-library.com/docs/cypress-testing-library/intro/)
import "@testing-library/cypress/add-commands";

Cypress.Commands.add("login", (username, password) => {
  cy.visit("http://localhost:3000/auth/login"); // Adjust this to your local app's login URL
  cy.get('input[placeholder="name@company.com"]').type(username); // Adjust the placeholder to match your username field
  cy.get('input[placeholder="*******************"]').type(password); // Adjust the placeholder to match your password field
  cy.contains(/^Sign in$/).click(); // Click the Sign in button
});

describe('Login Test', () => {
  it('should log in successfully and navigate to the home page', () => {
    cy.login("name@company.com", "password"); // Replace with actual username and password
    cy.url().should('eq', "http://localhost:3000/auth/login");
  });
});
```

In Playwright, you can configure a [global setup function](https://playwright.dev/docs/test-global-setup-teardown) to programmatically log in. The function should set an environment variable `process.env.COOKIES` containing the cookies from the authenticated session.

```ts title="playwright/global-setup.ts"
import { chromium } from "@playwright/test";

export default async function globalSetup() {
  const browser = await chromium.launch();
  const page = await browser.newPage();
  await page.goto("https:///auth/login");
  await page
    .getByPlaceholder("name@company.com")
    .fill(`${process.env.PLAYWRIGHT_USERNAME}`);
  await page
    .getByPlaceholder("*******************")
    .fill(`${process.env.PLAYWRIGHT_PASSWORD}`);
  await page.getByText(/^Sign in$/).click();
  await page.getByText("Welcome to Retool").click();
  const cookies = await page.context().cookies();
  process.env.COOKIES = JSON.stringify(cookies); // set this env var to store the authed page session
  await browser.close();
}
```

Then, create a [fixture](https://playwright.dev/docs/test-fixtures) that extends the base test by providing `authedPage`.

```ts title="playwright/fixtures/authedPage.ts"
import { test as base } from "@playwright/test";

export const test = base.extend({
  authedPage: async ({ page }, use) => {
    const deserializedCookies = process.env.COOKIES
      ? JSON.parse(process.env.COOKIES)
      : undefined;
    if (deserializedCookies) {
      await page.context().addCookies(deserializedCookies);
    }
    await use(page);
  },
});
```

This new `authedPage` can be used in multiple test files, and will be already logged into Retool.

```ts title="playwright/tests/example.spec.ts"
import { test } from "../fixtures/authedPage";

test("example test", async ({ authedPage }) => {
  authedPage.goto(url);
  // do things
});
```

:::warning Handling parallelization

In Playwright, tests run in parallel so having a single shared account for all tests may result in flakiness or unexpected behavior. You can use a single account for testing if you're positive your tests won't impact each other.

If your tests affect server-side state (for example, one test runs a query that updates a table, while another test checks the values of that table), or if you aren't sure that tests won't impact each other, then you may need multiple testing accounts. See Playwright's [documentation](https://playwright.dev/docs/auth#moderate-one-account-per-parallel-worker) for more information on setting up multiple testing accounts.

:::

## Writing E2E Tests

A solid Retool E2E test generally has 3 steps:

1. Visiting an app
2. Querying for elements on the page and interacting with them
3. Making assertions about the state of the app

Each of these steps map to Retool-specific code.

### 1. Visit an app

After setting up your WebDriver and [logging into your testing account](#log-in), navigate to the Retool app that you want to test.
We will use our [User Management app](https://docsdemos.retool.com/embedded/public/b4889e10-bead-4f30-876e-905e51b1616a) as an example.

To visit a page, we pass the URL we want to visit to the WebDriver-appropriate function.

```jsx title="cypress/e2e/userManagement.spec.cy.js"
describe("User Management", () => {
  it("opens the app", () => {
    cy.login("", "");
    cy.visit(
      "https://docsdemos.retool.com/embedded/public/b4889e10-bead-4f30-876e-905e51b1616a"
    );
  });
});
```

```ts title="playwright/tests/userManagement.spec.ts"
import { expect } from "@playwright/test";
import { test } from "../fixtures/authedPage";

test.describe("User Management", () => {
  test("opens the app", async ({ authedPage }) => {
    await authedPage.goto(
      "https://docsdemos.retool.com/embedded/public/b4889e10-bead-4f30-876e-905e51b1616a"
    );
  });
});
```

### 2: Query and interact with elements

Now that our app has loaded, we want to interact with it. Let's try to search for all the users with the name `'eva'`.

To type into the search bar, we need to query for it.

:::info Guidelines for querying elements

When deciding what selectors to use to find elements, we recommend
using [roles in the accessibility tree](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Techniques#roles)
(like button, textbox) combined with label text as much as possible. Code-specific inputs like `data-testid` should only be used when
absolutely necessary.

Tests should ideally resemble how users interact with your app as much as possible. For more guidelines
on what types of queries to use, see [Testing Library's guidelines](https://testing-library.com/docs/queries/about#priority).

To make your testing experience smoother, we also highly recommend installing the appropriate
[Testing Library framework](https://testing-library.com/docs/cypress-testing-library/intro) for your
WebDriver.

:::

Using the element inspector, we can see that the accessibility role of the search bar is `"textbox"` and the name is `"Label"`.
Since the name `"Label"` is not very descriptive (Retool defaults to `"Label"` because there is no label text associated with
this text input), we can also use the placeholder text `"Search by name"` to make our query more specific.

We can use these attributes to query for our search bar.

```jsx title="cypress/e2e/userManagement.spec.cy.js"
describe("User Management", () => {
  it("opens the app", () => {
    cy.login("", "");
    cy.visit(
      "https://docsdemos.retool.com/embedded/public/b4889e10-bead-4f30-876e-905e51b1616a"
    );

    // findByRole is only available with Cypress Testing Library (https://testing-library.com/docs/cypress-testing-library/intro/)
    cy.findByRole("textbox", {
      name: /Label/,
      placeholder: /Search by name/,
    });
  });
});
```

```ts title="playwright/tests/userManagement.spec.ts"
import { expect } from "@playwright/test";
import { test } from "../fixtures/authedPage";

test.describe("User Management", () => {
  test("opens the app", async ({ authedPage }) => {
    await authedPage.goto(
      "https://docsdemos.retool.com/embedded/public/b4889e10-bead-4f30-876e-905e51b1616a"
    );
  });
  await authedPage.getByPlaceholder(/Search by name/);
});
```

:::tip Extend timeouts

If your WebDriver is timing out on element selector queries, it may be because your Retool app hasn't finished loading.
It can be useful to increase the default timeout for element selector queries to ensure they are running when your Retool
app fully loads.

```jsx
cy.findByRole("textbox", {
  name: /Label/,
  placeholder: /Search by name/,
  timeout: 10000,
});
```

```jsx
await authedPage
  .getByPlaceholder(/Search by name/)
  .fill("eva", { timeout: 5000 });
```

:::

Now that we have the search bar, we can find our users. Let's type `'eva'` in our search bar.

```jsx title="cypress/e2e/userManagement.spec.cy.js"
describe("User Management", () => {
  it("opens the app", () => {
    cy.login("", "");
    cy.visit(
      "https://docsdemos.retool.com/embedded/public/b4889e10-bead-4f30-876e-905e51b1616a"
    );

    cy.findByRole("textbox", {
      name: /Label/,
      placeholder: /Search by name/,
    }).type("eva");
  });
});
```

:::tip Force certain interactions

Because of how Retool handles overflow behavior, some interactions may throw errors warning that
an element is being covered by another element even when it is visible to users.

If your element is clearly visible, you can add `{force: true}` to these interactions to force Cypress to run.

```
element.click({ force: true });
```

:::

```ts title="playwright/tests/userManagement.spec.ts"
import { expect } from "@playwright/test";
import { test } from "../fixtures/authedPage";

test.describe("User Management", () => {
  test("opens the app", async ({ authedPage }) => {
    await authedPage.goto(
      "https://docsdemos.retool.com/embedded/public/b4889e10-bead-4f30-876e-905e51b1616a"
    );
  });
  await authedPage.getByPlaceholder(/Search by name/).fill("eva");
});
```

### 3. Add assertions

Finally, we can make assertions to ensure our app is working properly. In our example, we want to check that
when we search for `'eva'`, there are two people named `Eva` in the list and that they are the names we expect.

But how can we select all the elements within the users list? If we go into the element inspector, we see that the each card
has a title element that could be a good candidate for selection. The accessibility role is "heading" and the name is the name
of each individual person.

We could write a test that queries for all the `heading` roles that also contain the name `"Eva"`. We can then assert that
there should be two returned elements containing the names we expect: `"Eva Noyce"` and `"Eva Lu Ator"`.

:::danger

#### This is an example of what not to do

```jsx title="cypress/e2e/userManagement.spec.cy.js"
describe("User Management", () => {
  it("opens the app", () => {
    cy.login("", "");
    cy.visit(
      "https://docsdemos.retool.com/embedded/public/b4889e10-bead-4f30-876e-905e51b1616a"
    );

    cy.findByRole("textbox", {
      name: /Label/,
      placeholder: /Search by name/,
    }).type("eva");

    cy.findAllByRole("heading", { name: /Eva/i })
      .should("have.length", 2)
      .and("contain", "Eva Noyce")
      .and("contain", "Eva Lu Ator");
  });
});
```

```ts title="playwright/tests/userManagement.spec.ts"
import { expect } from "@playwright/test";
import { test } from "../fixtures/authedPage";

test.describe("User Management", () => {
  test("opens the app", async ({ authedPage }) => {
    await authedPage.goto(
      "https://docsdemos.retool.com/embedded/public/b4889e10-bead-4f30-876e-905e51b1616a"
    );
  });
  await authedPage.getByPlaceholder(/Search by name/).fill("eva");

  await expect(authedPage.getByRole("heading", { name: /Eva/i })).toHaveCount(
    2
  );
});
```

:::

However, there is a big problem with this test: **it passes even when the search bar doesn't work**!

This is because the test is specifically designed to look for user tiles that match the query `"Eva"`.
As long as those two users are correctly displayed, the test is considered successful, regardless of
any additional users shown in the list.

So how should we correctly write the assertions for this test?

We could simply find all elements with the role `heading`, but this requires us to filter out other `heading`
elements, like the `User Management` app title. More importantly, this makes our test more brittle, because if we
added another element with the `heading` role to our app, it would break.

The key is that we need to get the container that contains all the list elements. That way, we can query only within the
container for `heading` roles to get the users displayed by the search.

The list container has no accessibility roles or text associated with it, so we will need to use `data-testids` in our query.

To find the `data-testid` for the container, we need to know the name of the component in the Retool editor.
In our case, the component is named `listView1`.

Now, we can go into the element inspector and `ctrl-f` for our component name. We are looking for the `data-testid` field that corresponds
to the container and contains the name of our component, which in this case is `RetoolGrid:listView1`.

:::warning

#### Data test IDs

Always use the component name defined in the Retool editor, such as `RetoolGrid:myComponentName`, for `data-testid`. This is guaranteed by Retool to be unique. Other IDs may unexpectedly break when you change your app, For example, another `RetoolWidget:ListViewWidget2` `data-testid` is added when you add another ListView component.
:::

Now we can use our `data-testid` to get the list container element, and query by role within that to assert that there
are two `Eva`s displayed with the correct names.

```jsx title="cypress/e2e/userManagement.spec.cy.js"
describe("User Management", () => {
  it("opens the app", () => {
    cy.login("", "");
    cy.visit(
      "https://docsdemos.retool.com/embedded/public/b4889e10-bead-4f30-876e-905e51b1616a"
    );

    cy.findByRole("textbox", {
      name: /Label/,
      placeholder: /Search by name/,
    }).type("eva");

    cy.findByTestId("RetoolGrid:listView1")
      .findAllByRole("heading")
      .should("have.length", 2)
      .and("contain", "Eva Noyce")
      .and("contain", "Eva Lu Ator");
  });
});
```

```ts title="playwright/tests/userManagement.spec.ts"
import { expect } from "@playwright/test";
import { test } from "../fixtures/authedPage";

test.describe("User Management", () => {
  test("opens the app", async ({ authedPage }) => {
    await authedPage.goto(
      "https://docsdemos.retool.com/embedded/public/b4889e10-bead-4f30-876e-905e51b1616a"
    );
    await authedPage.getByPlaceholder(/Search by name/).fill("eva");

    const gridContainer = authedPage.getByTestId("RetoolGrid:listView1");

    await expect(gridContainer.getByRole("heading")).toHaveCount(2);
    await expect(gridContainer).toContainText("Eva Noyce");
    await expect(gridContainer).toContainText("Eva Lu Ator");
  });
});
```

Now our test works as expected!

:::warning

#### Assertions after Retool queries

If you make assertions on your Retool app after a Retool query is expected to run (for example,
the test clicks a button which runs a query to add a user to a table), you may need to add
wait calls after the query to ensure it completes.

There is currently no way to programmatically await Retool query completion.
:::

## Integrating with CI

Integrating with CI allows you to run your tests automatically on pull requests or pushes to your main branch. This ensures that
your app is always working as expected. You must use the same Retool instance for CI tests as you do for development.

### Run tests on Retool branches

You can run tests on branches created in Retool using the following URL format: `https://.retool.com/tree//apps/`.

```js
const branch = encodeURIComponent("..."); // get this from your CI environment
const baseUrl = "https://.retool.com";
let url = `${baseUrl}/tree/${branch}/apps/testable-app`; // your app URL in the branch preview
if (branch === "main") {
  url = `${baseUrl}/apps/7d2307d4-b4a2-11ee-bd77-73254f108a3d/testable-app`; // your app URL on preview mode in main
}
```

### Setting up your pipeline

Your CI pipeline will need to be able to access the Retool instance you are testing against. You will need to set up
your pipeline to install the appropriate WebDriver and run your tests.

---

## Getting started with avatars

Use the [Avatar](../../reference/components/presentation/avatar.mdx) and [Avatar Group](../../reference/components/presentation/avatar-group.mdx) components to display profile information, and configure [preloaded JavaScript libraries](../../../queries/quickstart.mdx#configure-custom-libraries) that enable you to retrieve profile images from [Gravatar](https://gravatar.com).

import WebPlayground from '/docs/_partials/_components/_web-playground.mdx';

## Features

Key features of avatar components include:

- Display profile image and details about a user or group of users.
- Access to the [Gravatar](https://gravatar.com) library.
- Event handlers triggered on click.
- Customizeable styles.

## Specify content options

The **Content** section of the Inspector contains settings that control the content of the component:

- **Image URL**: The avatar image that you want to display. This value defaults to `{{ current_user.profilePhotoUrl }}`.
- **Fallback icon**: The avatar icon content to use if the **Image URL** evaluates to `null`. Set a value dynamically, or select from Retool's library of icons.
- **Fallback text**: The text to include inside the avatar icon if **Image URL** and **Fallback icon** both evaluate to `null`. If this looks like a full name, Retool sets this to the user's initials. For example, if the value of **Fallback Text** is `Maya Chen`, Retool displays `MC` on the avatar. This value defaults to `{{ current_user.fullName }}` and has a maximum length of two characters.

For Avatar Group, provide the image URLs and fallback texts in an array format.

Use the **Add-ons** section to configure a label or tooltip to accompany the avatar. Avatar Group does not support labels.

### Reference current user information

You can reference the [current_user](../../reference/objects/current-user.mdx) object in any of the **Content** settings to retrieve details about the user currently interacting with an app. For instance, `{{ current_user.email }}` returns their email address. This can be useful for logging user actions, such as updating an order or applying a discount.

### Obtain profile images from Gravatar

[Gravatar](https://gravatar.com/) is a free service for hosting profile images that are associated with your email addresses. Websites can retrieve the avatar image using the [Gravatar API](https://en.gravatar.com/site/implement/images/) automatically without requiring users to upload profile images to multiple locations.

You can retrieve profile images from Gravatar using a URL and an MD5-hashed email address. You accomplish this by preloading the [blueimp-md5](https://github.com/blueimp/JavaScript-MD5) JavaScript library and using it to generate the hash.

#### Preload the MD5 JavaScript library

You can [preload JavaScript libraries](../../../queries/quickstart.mdx#preloaded-code-and-libraries) on a per-app basis or automatically for all apps within your organization. To preload a library within an app, open **App settings** and select **Libraries**.

Click **Add new** and enter the following URL: 

```
https://cdnjs.cloudflare.com/ajax/libs/blueimp-md5/2.19.0/js/md5.min.js
```

This URL is the minified, UMD build of the libarary, retrieved from the [blueimp-md5](https://cdnjs.com/libraries/blueimp-md5) listing. For more information, refer to [Preload custom JavaScript code and libraries](../../../queries/guides/javascript/custom.mdx).

#### Display the Gravatar profile image

Retrieve profile images using the URL `https://www.gravatar.com/avatar/[hashedEmail]`. You can generate the MD5 hash within the URL by calling `md5()` and providing an email address. For example, to obtain the current user's Gravatar profile image, use:

```
https://www.gravatar.com/avatar/{{ md5(current_user.email) }}
```

## Configure user interaction

:::note

Avatar Group does not support user interaction.

:::

The **Interaction** section of the Inspector contains [event handlers](../interaction-navigation/event-handlers.mdx), which listen for and handle interactions with your components. For the Avatar component, events trigger when users click the Avatar.

import Appearance from "/docs/_shared/_appearance.mdx";

## Use avatars in Tables

The Table component supports an **Avatar** column format, which is commonly used to identify users.

Set the **Mapped value** setting to the user's name. Optionally create a **Caption** add-on to include more user information.

Set the **Image** and **Fallback** text in the same way that you would do so for the Avatar component. Refer to [Specify content options](#specify-content-options) for more information.

---

## Getting started with the Calendar component

Use the [Calendar](../../reference/components/presentation/calendar.mdx) component to display dates and times on an interactive interface. Calendar uses the [FullCalendar](https://fullcalendar.io/docs/event-object) library.

import WebPlayground from '/docs/_partials/_components/_web-playground.mdx';

## Features

Key features of the Calendar component include:

- Calendar events manually listed or dynamically generated.
- Event handlers triggered on user interaction.
- Customizable format and style.

## Specify content options

The **Content** section of the Inspector contains settings that control the contents of the Calendar. Define the calendar events manually or dynamically by mapping to an array of data. Refer to the [Define option lists](../forms-inputs/option-lists.mdx) guide for more information.

### Use case: Integrate Google Calendar resource

You can show events from a Google Calendar on this component using [Retool's Google Calendar integration](../../../data-sources/guides/integrations/google/google-calendar.mdx). Use the instructions in this section to set up the Calendar component to display events from an external calendar.

:::note

The following method of integrating the Google Calendar API uses OAuth authentication and requires other Retool users to sign in to Google. Only users with whom you've shared a calendar will have access. 

:::

#### Create a Google Calendar resource

[Create a Google Calendar resource](../../../data-sources/guides/integrations/google/google-calendar.mdx). Authenticate the resource with an account that has access to the calendar that you want to use.

#### Create a resource query

Now you can update your app to use the Google Calendar data in the Calendar component.

Create a new resource query that accesses [your Google Calendar resource](#create-a-google-calendar-resource). Set the **Operation** to `GET /calendars/{calendarId}/events` to return a list of events for a given calendar. In the `calendarId` path parameter, enter the ID of your calendar, which you can find in the settings for your Google Calendar.

export function ResourceQuery() {
  return (
    
      
    
  )
}

#### Configure the Calendar component

Configure the following settings in the Inspector:

- **Mode**: Mapped
- **Data source**: `{{ getEvents.data.items }}`
- **Event id**: `{{ item.id }}`
- **Title**: `{{ item.summary }}`
- **Start**: `{{ item.start.date ? item.start.date : item.start.dateTime }}`
- **End**: `{{ item.end.date ? item.end.date : item.end.dateTime }}`
- **All day**: `{{ item.start.date }}`
- **Group id**: `{{ item.recurringEventId }}`

:::note

All-day events use `date` and time-bound events use `dateTime` to signify when the event begins and ends. This is why the ternary operation is necessary for the **Start** and **End** settings.

:::

### Display recurring events

To display recurring events, set **Group id** in the Inspector. Set the value of this setting to a shared identifier that is present for all recurring events. 

For Google Calendar, set **Group id** value to `{{ item.recurringEventId }}`. You also need to edit the `getEvents` query: Add a query parameter with a key of `showEvents` and a value of `true`. This ensures that the API returns each ocurrence as a separate event.

## Configure user interaction

The **Interaction** section of the Inspector contains settings that handle user interaction with the Calendar component.

The following settings are available in the **Interaction** section to configure the Calendar's behavior:

- **Default date**: The default date that the Calendar focuses on. Defaults to the current date.
- **Default view**: The default view of the Calendar. Possible options include **Month**, **Week**, **Day**, and **List**. Defaults to **Week**.
- **List type**: If the **Default view** is set to **List**, this setting controls how the events are categorized. Defaults to **Week**.
- **Advanced Setting** > **Timezone**: The timezone in which to display events. Defaults to local time.
- **Advanced Setting** > **Start week on**: The day of the week on which to begin the week. A number between 0 and 6, where 0 is Sunday and 6 is Saturday. Defaults to 0.

[Event handlers](../interaction-navigation/event-handlers.mdx) listen for and handle interactions with your components. The Calendar component supports **Create event**, **Click event**, **Change event**, and **Remove event** event handlers. Refer to the following sections for more information about internal user interaction with the calendar.

### Enable event creation

To enable users to create events, add a **Create event** event handler. This event triggers when a user selects a time interval on the Calendar interface. 

:::note

Retool immediately triggers **Create event** when a user completes a selection. There is no message appears to confirm or finalize the change.

:::

Selecting a time interval on the Calendar populates the values of the `selectedInterval` Calendar property. The following object shows an example:

```JSON
{ 
  "allDay": false,
  "end": "2025-03-27T10:00:00.000Z",
  "start": "2025-03-27T09:00:00.000Z"
}
```

Create a new query that accesses these properties and creates a new event on a connected calendar. Set the event handler's **Action** to trigger this query.

### Enable event changes

To enable users to change events, add a **Change event** event handler. This event triggers when a user selects and moves an existing event on the calendar. Moving an event populates the values of the `changeset` Calendar property. If the event is recurring, the `changeset` contains all of the recurrences. The following object shows an example `changeset`, which is identified by the event ID:

```JSON
{
   "6nt7smomc81s2jfsdsfvkkph88": {
      "allDay": false,
      "end": "2025-03-27T11:00:00.000Z",
      "id": "6nt7smomc81s2jfsdsfvkkph88",
      "start": "2025-03-27T10:00:00.000Z"
   }
}
```

Create a new query that accesses this property and edits an event on the connected calendar. Set the event handler's **Action** to trigger this query.

Changes to subsequent events overwrite the contents of `changeset`.

### Enable event deletion

To enable users to delete events, add a **Remove event** event handler. This event triggers when a user selects an existing event and presses Backspace or Delete.

Clicking an event populates the `selectedEvent` property. Create a new query that accesses the `selectedEvent.id` and deletes the event on the connected calendar. Set the event handler's **Action** to trigger this query.

import Appearance from "/docs/_shared/_appearance.mdx";

Click **•••** to open the **Advanced Settings** of the **Appearance** section to edit other settings, such as **Use 24 hour time**, **Show event times**, and **Show all-day slot**.

---

## Reference dates and times in components

You can reference a configured date and time in various components, preserving the user's selected time zone.

import Components from '/docs/_shared/_dates-in-components.mdx';

---

## Getting started with embedded media

Use embedded media components in your app to include content, via URL or upload, with which viewers can view and interact. Retool provides the following components:

- [PDF](../../reference/components/presentation/pdf.mdx), which enables you to embed a PDF via URL, uploaded file, or JavaScript object.
- [QR Code](../../reference/components/presentation/qr-code.mdx), which generates a QR code automatically based on a URL.
- [Video](../../reference/components/presentation/video.mdx), which embeds a video from popular video services.

import WebPlayground from '/docs/_partials/_components/_web-playground.mdx';

## Features

Key features of embedded media components include:

- Add-ons to provide users with additional information about the component via tooltips and top bars.
- Flexible content options for PDF components.
- Event handlers triggered on click, and playback settings for Video components.
- Customizeable styles.

## Specify content options

The **Content** section of the Inspector contains settings that control the content of the component. Use this section to define the source of your embedded content.

Use one of the following **PDF source** options to identify your PDF:

- **URL**: A publicly-accessible URL for your PDF.
- **Storage**: A file that you can upload from your computer. Uploaded files are stored using Retool Storage.
- **JS**: A file object, usually the output of a query.

:::note

When using a URL, the PDF component could display a **PDF could not be loaded** error, and the browser console displays a CORS error. This usually occurs because your PDF is hosted on another server and doesn't have the [appropriate CORS headers](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS). There are several solutions to resolve this error:

- Upload the file to Retool Storage and use the **Storage** option instead.
- If you host the PDF, update the `Access-Control-Allow-Origin` header to include `https://.retool.com`.
- Create a REST API query that retrieves the PDF URL. In the PDF component, set the **File URL** to `{{ query1.data.base64 }}`.

:::

The **Add-ons** setting defines features that you can add to the component, like a tooltip.

The PDF component includes a **Top bar** add-on that is enabled by default. You can add a **Rotate button** and **Title** that are displayed alongside the page and download controls.

Enter the URL that you want your QR Code to represent in the **Value** setting. Retool automatically generates a QR code for you based on the URL.

The **Add-ons** setting defines features that you can add to the component, like a tooltip.

Enter the URL for your video in the **Video URL** setting. Retool supports popular video players, including YouTube and Vimeo, as well as direct links to video file types commonly supported by browsers.

To embed YouTube Shorts using the Video component, use the following URL format: `https://www.youtube.com/embed/{identifier_string}`, where `identifier_string` is the identifier at the end of the YouTube Shorts URL.

The **Add-ons** setting defines features that you can add to the component, like a tooltip.

## Configure user interaction

The PDF and QR Code components do not enable any user interaction. 

:::note

Additionally, PDF does not support interactive elements, such as links or form filling. To retain PDF interactivity, consider using an [IFrame](../../reference/components/custom/iframe.mdx) to embed the PDF directly from a URL.

:::

The Video component includes an **Interaction** section in the Inspector, which contains settings that control user interaction. Use **[event handlers](../interaction-navigation/event-handlers.mdx)** to listen for and handle interactions with the Video component, including playing, pausing, loading, or ending the video.

Utilize the other settings—**Autoplay**, **Loop**, **Volume**, and **Playback rate**—to control the behavior of the video.

import Appearance from "/docs/_shared/_appearance.mdx";

---

## Getting started with icons

import Shared from '/docs/_shared/_icons.mdx';

---

## Getting started with images

You can display images in Retool apps in a variety of ways. Multiple components support images and can support different methods for providing an image source.

Add images with the following components:

- [Image](../../reference/components/presentation/image.mdx)
- [Circular Image](../../reference/components/presentation/circular-image.mdx)
- [Image Grid](../../reference/components/presentation/image-grid.mdx)
- [Bounding Box](../../reference/components/special-inputs/bounding-box.mdx)

import WebPlayground from '/docs/_partials/_components/_web-playground.mdx';

## Features

Key features of image components include:

- The option to provide an image from URL, Base64 string, Retool Storage, or JavaScript file object.
- Event handlers triggered on image click.
- Style configuration.
- Interactive and customizeable annotations through the Bounding Box component.

## Specify content options

For each Image or Circular image, you can provide a URL, Base64 string, Retool Storage object, or JavaScript image object.

Image Grid enables you to manually [display images from URL or Base64](#display-images-from-url-or-base64), or you can dynamically map images from a data source. Refer to [Define option lists](../forms-inputs/option-lists.mdx) for more on mapping data.

:::note

If you are using [Source Control](../../../source-control/index.mdx), large images uploaded to the Image component are _not_ committed and stored in Source Control in order to reduce file size and adhere to industry standard best practices. Instead, Retool recommends hosting the image on a public URL and using the **URL** option for the image source.

:::

### Display images from URL or Base64

For Image and Circular Image, set the **Image source** setting to **URL**  to reference an image via URL or Base64 string. You can use the direct string, or reference it using JavaScript with embedded expressions. For example, if you're working with a Table and you want to reference an image in the selected row, you could use `{{ table1.selectedRow.image }}` as the image source.

For Image Grid in **Manual** mode, click each image and add the URL or string to the **Source** field.

### Display images from Retool Storage

For Image and Circular Image, set the **Image source** setting to **Storage** to reference a file from [Retool Storage](../../../data-sources/quickstarts/retool-storage.mdx).

Select a file that already exists in Retool Storage, or upload a `bmp`, `gif`, `jpg`, `png`, `svg`, or `webp` file directly from the Inspector. Uploaded files are added to Retool Storage.

export function RetoolStorageImage() {
  return (
    
      
    
  )
}

### Display images with JavaScript

For Image and Circular Image, set **Image source** to **JS** to reference a hard-coded JavaScript file object or the output of a query.

The file object must have the following schema:

```json
{
  data: "image/*",
  sizeBytes: 30000,
  name: "sample image",
  base64Data: "iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAIAAAD/gAIDAAABYElEQVR4nOzSoU3cARyG4bvm79oBmtSermlS17SiphNU1FQRHISwA4KEHVAIcIQJMKBBMQGSCwZFmOFNfo7nGeATb77l38GyGvLt8O/U1PmXP1NTz0c3U1MfpobeA7ECsQKxArECsQKxArECsQKxArECsQKxArECsQKxArECsQKxArECsQKxArECsYL11er/1NbLw3pqav92Z2rq+93e1JRnBWIFYgViBWIFYgViBWIFYgViBWIFYgViBWIFYgViBWIFYgViBWIFYgViBWIFYgXLr9cfU1vbp09TU78vTqemrjeXU1OeFYgViBWIFYgViBWIFYgViBWIFYgViBWIFYgViBWIFYgViBWIFYgViBWIFYgViBUs98dnU1tftx+npnZ/fp6aejzZTE15ViBWIFYgViBWIFYgViBWIFYgViBWIFYgViBWIFYgViBWIFYgViBWIFYgViBWIFbwFgAA//8hwRgJea5fggAAAABJRU5ErkJggg=="
}
```

## Configure user interaction

The **Interaction** section of the Inspector contains settings that control user interaction. Use [event handlers](../interaction-navigation/event-handlers.mdx) to listen for and handle clicks on each image.

:::note

For Image Grids in **Manual** mode, click each image to configure event handlers on the individual image.

:::

import Appearance from "/docs/_shared/_appearance.mdx";

### Resize images

Images scale to the size of the component. Click and drag a corner of the component to resize it.

You can resize [images displayed in other components](#display-images-in-other-components) using the `img` HTML tag by setting `width` and `height` values:

```html

```

You can also use percentages:

```html

```

Percentage values are evaluated based on the size of the Text or Table component used to display the image.

## Display images in other components

You can insert images into several other components, including Text and Table, using an HTML `img` tag.

### Display images in Text components

You can also display images in [Text components](../../reference/components/presentation/text.mdx) using an HTML `img` tag.

Set the `src` of the image tag to either a URL or a Base64 string. If you include a Base64-encoded string, prepend the `src` string with the following: `data:image/jpeg;base64,`.

You can also reference strings from elsewhere in your app using `{{ }}` embedded expressions. For example, `` pulls an image from `query1`, and prepends the string with `data:image/jpeg;base64,`.

### Display images in Table components

You can display images in [Table components](../../reference/components/data/table.mdx) using the HTML column type and the `img` HTML tag. Add data to your table and then set the column type to **HTML** on the column that you want to render images in. Use the **Mapped value** property to pass in the `img` tag and the image's source.

## Display an image with the Bounding Box component

The Bounding Box component has additional functionality beyond the basics of the other image components. The user can draw green, transparent boxes around certain areas of the image, categorize the boxes, and delete boxes.

In the **Content** section of the Inspector, identify the image using the **Image src URL** setting. Other image source types are not supported. Identify the **List of possible labels** as the possible categories for the bounding boxes, and the **Initial Bounding boxes** to set the defaults.

Bounding boxes are defined as an array of objects in the following format:

```JSON
  {
    "x": 364.33285687062585,
    "y": 455.1241526664781,
    "height": 234.01870447637899,
    "width": 460.11987944778207,
    "label": "Car"
  }
```

Once created, bounding boxes are stored in the `boundingBoxes` property.

The **Interaction** section of the Inspector contains settings that control user interaction. Use [event handlers](../interaction-navigation/event-handlers.mdx) to listen for and handle changes to the bounding boxes.

---

## Presentation and styling



---

## Getting started with Mapbox Map

import WebPlayground from '/docs/_partials/_components/_web-playground.mdx';

Use [Mapbox Map](../../reference/components/integrations/mapbox-map.mdx) to display points of interest on an embedded map using [MapBox](https://www.mapbox.com/). It supports custom markers using emoji and symbols, default coordinates and zoom level, and [GeoJSON](https://geojson.org/) to render custom shapes for highlighting locations.

## Features

Key features of Mapbox Map include:

- Display markers and objects on the map.
- Select default coordinates and zoom level.
- Configure interaction on viewport change, selection, and hover.
- Customize marker and object appearance.

## Specify content options

The **Content** section of the Inspector contains settings that control the content of the Mapbox Map. Use the **Latitude** and **Longitude** settings to define the default center of the map. Use this section to define the markers or shapes that you want to appear on the map.

:::note

You can display both markers and shapes at the same time, but you must use the **Points** setting for markers and the **GeoJSON** setting for lines and shapes.

:::

### Display markers

To display markers on the map, use the following settings:

- **Latitude field name** and **Longitude field name**, which define the object keys that Retool uses for the latitude and longitude value.
- **Points**, an array of points to render. Each point is represented by an object with keys that match those defined in **Latitude field name** and **Longitude field name**. 

For example, you could create an SQL query `getLocations` that returns all franchise locations in Texas. This table includes ten entries, each with a `latitude` and `longitude` value.

Add a Mapbox Map to the canvas, and set the **Points** value to `{{ formatDataAsArray(getLocations.data) }}`. This ensures that the data is formatted as an array and that you can set the points dynamically.

export function AddPoints() {
  return (
    
      
    
  )
}

You can also create a query that gets your current location information using the [getCurrentPosition()](../../../queries/reference/libraries/utils.mdx#method-getCurrentPosition) method, and use this query to populate your coordinates on the map. Note that your browser must have access to your location information in order to use this method.

### Display shapes

To display shapes, use the **GeoJSON** setting. Data must be formatted in GeoJSON format, in accordance with [RFC 7946](https://datatracker.ietf.org/doc/html/rfc7946). You can use the online tool [geojson.io](https://geojson.io/) to place lines or shapes on an interactive map and generate the corresponding JSON that is accepted by the **GeoJSON** setting.

The following example shows how you can use GeoJSON to create a polygon on the map:

```json
{
  "type": "FeatureCollection",
  "features": [
    {
      "type": "Feature",
      "properties": {},
      "geometry": {
        "coordinates": [
          [
            [
              -77.26840246337339,
              39.03015226809225
            ],
            [
              -77.26840246337339,
              38.75986554487136
            ],
            [
              -76.80535320479785,
              38.75986554487136
            ],
            [
              -76.80535320479785,
              39.03015226809225
            ],
            [
              -77.26840246337339,
              39.03015226809225
            ]
          ]
        ],
        "type": "Polygon"
      }
    }
  ]
}
```

## Configure user interaction

The **Interaction** section of the Inspector contains settings that control user interaction. Mapbox Map supports a series of event handlers that enable you to trigger a query on each occurrence. Choose an existing query to trigger, or create a new one.

For example, you could create a map that displays a marker for each franchise of your business, and you want to display the name of the franchise owner when you click on a point. Create a JavaScript query called `getDetails` that contains the code `return mapboxMap1.selectedPoint`.

Add a [Text](../../reference/components/presentation/text.mdx) component to the screen, and set the **Value** to `## {{ getDetails.data.manager_name }}`. Finally, update the Mapbox Map component so that **On point select** triggers `getDetails`.

export function MapboxEvent() {
  return (
    
      
    
  )
}

Alternatively, you can control the Mapbox Map component using the event handler of another component. For example, you can use a [Table](../../reference/components/data/table.mdx) event handler to set the center of the map based on the selected row.

## Customize appearance

You can customize the presentation of your component in the **Spacing** and **Appearance** sections of the Inspector. 

You can dynamically control the **Hidden** setting with a truthy value that evaluates to `true` or `false`. You can also control the `hidden` property for the component using the `.setHidden()` method from event handlers and JavaScript queries.

The **Zoom** setting defines the scale of the map by default. You can also set this to a dynamic value (such as the value of a [Slider](../../reference/components/number-inputs/slider.mdx)). Users can also zoom and manipulate the map themselves.

You can apply **GeoJSON Layer Styling** to your GeoJSON data, if applicable. GeoJSON uses CSS styles. Refer to the [Mapbox Style Specification](https://docs.mapbox.com/style-spec/guides/) for more information.

---

## Getting started with presentation components

Retool provides a variety of componenets that help you display information, alerts, and statuses. These components include:

- [Alert](../../reference/components/presentation/alert.mdx)
- [Progress Bar](../../reference/components/presentation/progress-bar.mdx) and [Progress Circle](../../reference/components/presentation/progress-circle.mdx)
- [Statistic](../../reference/components/presentation/statistic.mdx)
- [Status](../../reference/components/presentation/status.mdx)
- [Tags](../../reference/components/presentation/tags.mdx)
- [Text](../../reference/components/presentation/text.mdx)

import WebPlayground from '/docs/_partials/_components/_web-playground.mdx';

 

## Features

Key features of presentation components include:

- Dynamic values using embedded expressions.
- Event handlers to define interaction.
- Customizeable styles.

## Specify content options

The **Content** section of the Inspector contains settings that control the content of the component, including:

- **Value**.
- **Default value**.
- **Indeterminate**, which shows a loading state with unknown progress for Progress Bar and Progress Circle.
- **States**, which defines the list of possible values for Alert and Status.
- **Add-ons**, which provide additional informational elements such as tooltips or icons.

To set the possible values for Status and Tags, [define an option list](../forms-inputs/option-lists.mdx) either manually or by dynamically mapping an array of data. The states for Alerts must be defined manually.

## Configure user interaction

Progress Bar, Progress Circle, Status, and Text do not support user interaction.

Alert, Statistic, and Tags support user interaction. The **Interaction** section of the Inspector contains settings that control user interaction. Use **[event handlers](../interaction-navigation/event-handlers.mdx)** to listen for and handle interactions with your components.

:::note

Event handlers for the Tags component apply to all tags shown. Retool does not support individual event handlers for each tag.

:::

Alert has several options for user interaction. You can add an event handler that triggers when the user closes the Alert, or you can add a link—called an action—to the Alert that triggers when clicked. To do so, select the state that you want to change. Then click the **Action** add-on, and add an event handler.

export function AlertAction() {
  return (
    
      
    
  )
}

import Appearance from "/docs/_shared/_appearance.mdx";

---

## Design organization and app themes

You can create and manage themes from your organization's [Themes](https://login.retool.com/auth/login?source=docs&redirectOnLogin=settings/themes) settings, or create custom themes that apply to individual apps. Organization-level themes are only available on a Business or an Enterprise plan. Custom themes are available on all plans.

:::info

Organizations on the Enterprise plan can manage organization level themes using the [Retool API](../../../api/index.mdx) and can utilize [Source Control](../../../source-control/index.mdx) to track changes.

:::

## Create organization-level themes

Navigate to **Settings** > **Themes** to create an organization-level theme. Click **Create new** > **Theme** and enter a name. You can then select an existing Retool theme and primary color from which to start. Retool's template themes are optimized for high-contrast use.

The theme editor displays a live preview of your style options and how they apply to your apps. Style options are broken down into different sections in the **Theme** menu on the right.

:::info

Use cmd/ctrl + z to undo changes in the theme editor. Use cmd/ctrl + Shift + z to redo changes.

:::

### Define theme modes

Organization-level themes have the ability to contain several _modes_. Access the modes with the drop-down in the **Theme** menu. Retool can create a preset template for Light mode, Dark mode, or both.

Access the mode from within an app using `theme.setMode(‘modeName’, { persist: true })`. This method makes it easy to change modes based on user interaction—for example, you can create a Switch component that toggles between light and dark mode. The `persist` parameter maintains the mode selection for a user's subsequent sessions.

### Create colors

import Tokensvg from "/img/icons/token.svg";

The **Colors** section defines the primary and additional colors for your app. **User colors** are _tokens_ that can be reused throughout your apps. These tokens take the place of the secondary and tertiary colors in [app-level themes](#configure-app-level-themes).

:::tip

The [Retool Theme Generator](https://www.figma.com/community/plugin/1388634061939245347/retool-theme-generator) plug-in for Figma enables you to easily migrate variables to use in Retool themes. This tool eliminates the need for manually creating many user colors.

:::

To add new colors, click the **+** in the **User colors** section. Edit the name and hex value of your new color. Open the color settings, and select **Set value in all modes**.

Use the token icon  next to a **Core color** to map the value to a **User color**. Open the color settings, and select **Set value in all modes**.

### Apply typography settings

Use the **Typography** section of the theme editor to change the font style, size, and weight for your app. In the **User typography** section, define typography presets that can be reused as _tokens_ throughout your apps.

You can also add new fonts from Google Fonts or from a custom CSS file.

To add a custom font, click the **+** button in the **Fonts** section. Select the **Custom** tab, and enter your font information. The **Import URL** must be a link to a CSS file with at least one `@font-face` rule. Choose the font weights you want to include, and optionally set the font as the default.

For example, to use the [Patua One](https://fonts.google.com/specimen/Patua+One) font from Google:

1. Set the **Font family** to `Patua One`
2. Set the **Import URL** to `https://fonts.googleapis.com/css2?family=Patua+One`.

### Define metrics

Set any size requirements in the **Metrics** section of the theme editor. You can edit the default **Border radius** to be applied to components. You can also add new settings in the **User metrics** section, which are reused as _tokens_ throughout your apps.

### Add shadows

Use the **Shadows** section of the theme editor to create and edit shadows. You can use the **Default shadows**, or add a new shadow in the **User shadows** section. Shadows can be reused as _tokens_ throughout your apps.

:::note

Shadows are available for most, but not all components.

:::

Shadow values use [CSS box-shadow syntax](https://developer.mozilla.org/en-US/docs/Web/CSS/box-shadow). For example, `12px 12px 2px 1px rgba(0, 0, 255, .2)` creates the custom purple shadow in the following image.

To remove a shadow—in situations where you want to override a theme—set the value to `none`.

### Apply component-level theme overrides

To edit the settings for a component, select the component from the canvas or from the **Components** menu on the left. The **Theme** editor updates to show component-specific settings that override the organizational-level theme. For each of the settings, choose a custom value or a token.

## Configure app-level themes

:::note

App-level themes are available to users on all plans.

:::

Within an app, you can apply organization level themes or create a custom theme. Open the **App Settings** tab to get started. You can choose to apply an organization-level theme, or apply a custom theme for this app.

Any theme changes apply to the current app only, and you can't edit organizational level themes from within an app. Note that you can't define custom color tokens, font tokens, or metric tokens for a custom app theme.

---

## Getting started with the Timeline component

import Appearance from "/docs/_shared/_appearance.mdx";
import WebPlayground from '/docs/_partials/_components/_web-playground.mdx';

Use the [Timeline](../../reference/components/presentation/timeline.mdx) component when you want to display events in a Gantt chart format. This component is useful for project tracking, planning, and visualizing dates and date ranges.

## Features

Key features of the Timeline component include:

- Manually or dynamically define the events that appear.
- Configurable color, metadata, and tooltips for each event.
- Meta events and milestones to add to the Timeline.
- Event handlers triggered on click.
- Customizeable format and style of the Timeline. 

## Specify content options

The **Content** section of the Inspector contains settings that control the content of the Timeline, including the **Data source** and **Add-ons**.

### Define the data source

You can define the **Data source** as an array of data either manually or dynamically. To manually define the data, select **JavaScript** from the dropdown and manually enter your array. To dynamically define the data, select a query from the dropdown list. Refer to [Define option lists](../forms-inputs/option-lists.mdx) for more information about dynamically referencing arrays.

Next, define the mapped events for each `item` in your array:

- **ID**: The identifier for the event.
- **Title**: The title of the event. This is displayed on top of event ranges, and to the right of single-date events.
- **Start date**: The start date of the event. This value is `null` for single-date events.
- **End date**: The end date of the event.
- **Color**: The color of the event in the Timeline. You can manually set the color, or you can [reference a theme color](./themes.mdx#create-colors), such as `{{ theme.tertiary }}`.
- **Properties**: Any metadata that you want to associate with events, formatted as an object. Use properties to [group events](#create-groups). To expose all fields for grouping, set this value to `{{ item }}`.
- **Tooltip**: The format for a tooltip, displayed on hover. When this setting is left blank, the default tooltip format is `Start Date -> End Date (Duration)`.

### Define add-ons

Use the **Add-ons** setting to provide additional information about the Timeline.

#### Create groups

Use the **Grouping** add-on to organize events based on one of the values defined in the **Properties** setting. You can optionally define the event groups that you want to be displayed by default. Nested groups are supported, and you can reorder the groups to define which should be applied first. App users can also choose to maniupulate the grouping using the icon in the top right corner of the component.
  
export function Grouping() {
  return (
    
      
    
  )
}

 

#### Define meta events and milestones

You can define additional sets of events that appear on your Timeline using the **Meta Events** and **Milestones** add-ons.

Meta events appear in a separate section above the main Timeline. For example, you could use meta events to make note of when certain employees will be out of the office.

Milestones are single-date events that appear as vertical lines layered over the main Timeline. For example, you could use milestones to make note of certain important dates or accomplishments. The **Title** of milestones is displayed on hover.

Defining the data for meta events and milestones works similarly to [defining the data source](#define-the-data-source), but these add-ons do not support tooltip label changes or properties.

## Configure user interaction

The **Interaction** section of the Inspector contains [event handlers](../interaction-navigation/event-handlers.mdx), which listen for and handle interactions with your components. For the Timeline component, events trigger when users click the Timeline.

This section is also where you customize Timeline-specific settings, such as the **Timescale**, intervals, and initial date of the Timeline. Open the **Advanced** settings to adjust the month and day on which the quarter starts.

---

## Share data between app pages

Apps use global and per-page scopes for components, frames, and code.

- Globally scoped objects can be referenced across multiple pages. Retool continually evaluates globally scoped objects regardless of the page currently in view.
- Page-scoped objects can only be referenced within the same page. Retool only evaluates page-scoped objects when the page is currently being viewed.

If you need to share data between pages, you can use globally scoped variables, localStorage, or URL parameters.

## Share data using global variables

[Variables](../../queries/guides/store-temporary-data.mdx) temporarily store data for the duration of the user's app session. They can also include an optional initial value. 

To create a global variable, navigate to the Code tab and click **+ > Variable**. As the variable is globally scoped, you can update its value from any page using [event handlers](./interaction-navigation/event-handlers.mdx), or with the `setIn()` and `setValue()` [JavaScript methods](../../queries/reference/objects/variable.mdx#methods).

Store page data in a global variable.

You can then reference the global variable from any screen.

 
Reference a global variable in a page.

For example, you can use the Table component's **Double Click Row** event handler to store the selected row data in a global variable. You can then reference the global variable in another page, such as a component value.

If a user closes the tab, navigates away from the app, or opens the app in another tab, the variable reverts to its initial value.

## Store page data in localStorage

:::warning

localStorage does not encrypt data. Only use localStorage to store nonsensitive information. 

:::

[localStorage](../../queries/guides/store-temporary-data.mdx) temporarily stores data in the browser's localStorage. Unlike variables, localStorage is a persistent data store. Provided the user continues to use the same browser and has not cleared their browsing history or cache, data remains available.

Use [event handlers](./interaction-navigation/event-handlers.mdx) or the `setValue()` [JavaScript method](../../queries/reference/objects/localstorage.mdx#methods) to store data in localStorage. You can also clear app-specific data using `clear()`. 

## Append data as URL parameters

:::caution

Do not use URL parameters for sensitive information. URL parameters are plain text and often included when sharing URLs.

:::

You can configure URL query parameters on a per-page basis. As with [URL queries for single-page apps](../guides/app-management/customize-app-urls.mdx#configure-custom-url-query-parameters), pages can store values in the URL.

To use URL query parameters, select a page and then configure its **URL search params** and **URL hash params** key-value properties. You reference property values, such as `textInput1.value`, which dynamically updates the URL parameter. You can also access URL parameters for the page using `url`, such as configuring the default value of a component to the initial URL parameter value when launching the app.

Configure URL parameters.

---

## Retool Apps how-to guides

import Howto from '/docs/_partials/_doctypes/_howto.mdx';

:::tip Get started with web apps

If you are unfamiliar with Retool web apps and want to get started: 

- Read the [quickstart](./quickstart) to learn about the fundamental concepts.
- Follow a [tutorial](./tutorial/) to build your first web app.

:::

---

## Retool Apps documentation

Retool Apps enables you to quickly build and deploy web apps for your business. Connect APIs and databases, assemble user interfaces with drag-and-drop components, write queries that read and write data, and perform complex logic and transformations using JavaScript.

---

## Retool apps quickstart

import Image from '@site/src/components/Image';

This guide serves as an introduction to Retool apps. It covers many of the concepts and terminology you will come across as you build apps using the web-based IDE. After reading this page, you should have a good understanding of the fundamentals for building Retool apps.

import Browsers from '/docs/_shared/_supported-browsers.mdx';

## Create and edit apps with AI

Retool provides several features that use AI to help you create and edit Retool apps. The following mechanisms enable you to use AI as a development assistant:

- [Assist](./guides/assist/index.mdx) (currently in Public Beta): Use AI to generate or edit whole apps from within the [app IDE](./concepts/ide.mdx).
- [Ask AI](../queries/guides/ask.mdx): Use AI to write [JavaScript, SQL, or GraphQL queries](#read-and-write-data-using-queries). You can instruct Ask AI to generate a new query, edit a query, explain a query, or fix errors.

[Learn more](./concepts/ai.mdx) about the various AI-powered features that you can use in apps.
  
:::note

If you prefer not to use AI features, you can turn them off entirely in **Settings** > **Retool AI**.

:::

## Assemble the interface

A Retool app's user interface is comprised of components, pages, and frames. These interface elements function as objects with internal state.

- [Components](#components) are interface elements, such as a [text input field](./reference/components/text-inputs/text-input.mdx) or a [table](./reference/components/data/table.mdx), with which users interact.
- [Pages](#pages) are separate sections that contain their own code, frames, and components. Users navigate between pages which are often used for distinct use cases. For example, a customer support app might have separate pages to retrieve order information, check inventory, and process refunds.
- [Frames](#frames) are distinct areas of the canvas in which you add components. They provide you with flexible layout options for arranging your app's user interface. You can add frames to pages or use them globally.

Retool exposes properties, methods, and events with which you can interact. As you interact with components, their properties change. For example:

- Entering text into a [Text Input](./reference/components/text-inputs/text-input.mdx) component changes its `value` property value.
- Closing a [Drawer](./reference/frames/drawer.mdx) frame sets its `hidden` property value to `true`.

You also write code that interacts with data, transforms values, or controls behavior using JavaScript methods. For example:

- Use `setValue('Jenny Appleseed')` to set the `value` property value of a Text Input component to **Jenny Appleseed**.
- Use `setHidden(true)` to set the `hidden` property value of a Drawer frame to `true`.

### Components

You use Retool's drag-and-drop [component library](./reference/components/components.mdx) in the IDE to assemble and configure an app's UI. You position components anywhere on the canvas of a selected page, and can adjust their position and size.

### Pages

Retool only evaluates a page's objects when the page is currently in view. This allows for apps to serve multiple functions without impacting performance.

The **Pages** tab contains a list of pages within the app. The default page is the one that first loads when launching the app. You can explore and manage all pages, change the default page, import pages from apps, and view more details.

Explore pages.

Each page has a title and URL that you configure from the Inspector. The page URL is appended to the app URL and allows for deep-linking to specific pages. For example, a page with the URL of `customers` would be available at `https://example.retool.com/app/support-dashboard/customers`. Accessing the app using this URL would then bypass the default page.

Configure page settings.

The [Navigation](./reference/components/navigation/navigation.mdx) component, when added to an app with multiple pages, dynamically generates navigation items for each page. If you want to switch pages using another method, you can configure any event handler with the **Go to page** action, such as a button click.

The Header or Sidebar frames contain a Navigation component by default that's configured for page navigation when enabled in an app with multiple pages.

### Frames

import Frames from '/docs/_shared/_frames.mdx';

### Global and page scopes

Apps use global and per-page scopes for components, frames, and code.

- Globally scoped objects can be referenced across multiple pages. Retool continually evaluates globally scoped objects regardless of the page currently in view.
- Page-scoped objects can only be referenced within the same page. Retool only evaluates page-scoped objects when the page is currently being viewed.

#### Globally scoped objects

You can create **Global** code (e.g., resource queries or variables), which all pages can reference. Each page can interact with globally scoped code to trigger queries, set variable values, etc. For example, if you were building a customer support app then you could use a single globally scoped query to retrieve customer information rather than duplicate the same query across multiple pages.

You can drag code to either the **Global** or **Page** section in the **Code** pane to change its scope. You can also move code across pages using the **Move to page** contextual menu action.

The [Header](./concepts/ide.mdx#header-frame) and [Sidebar frame](./concepts/ide.mdx#sidebar-frame) are also globally scoped and persist across all pages. Any components within these frames inherit this behavior and are globally scoped. All other frames (e.g., Modal and Drawer) are page-scoped and only available within the app in which they reside.

export function Scopes() {
  return (
    
      
    
  )
}

All pages and their contents can reference globally scoped objects. Globally scoped objects, however, can only reference other globally scoped objects. For instance, a globally scoped query cannot reference a property for a component in a page.

#### Page-scoped objects

All code and components within a page are page-scoped, and cannot be referenced by other pages. If a page contains code or event handlers that would run on page load, these run whenever you switch to the page.

import SideBySide from '@site/docs/_partials/_side-by-side.mdx';

## Connect interface elements together

Many page, component, and frame properties are editable. You configure them in the IDE with either static values (`string`, `number`, `boolean`, `array`, and `object`) or reference other property values using `{{ }}` embedded expressions, similar to the use of [template literals](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals).

You reference property values using [dot notation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Property_accessors#dot_notation). In the following example, The **Default value** setting in the IDE references `{{table1.selectedRow.name}}`. As a result, `{{textInput1.value}}` always corresponds to the name from the selected table row.

  

  
  
  

The Text Input component references `{{table1.selectedRow.name}}` to display the selected user.

  

## Use JavaScript expressions for values

Retool performs [string interpolation](https://en.wikipedia.org/wiki/String_interpolation) and evaluates `{{ }}` embedded expressions as JavaScript. As a result, you can write JavaScript code—that evaluates and returns a value synchronously—almost anywhere. This enables you to dynamically set property values using transformations or conditional logic to build complex apps.

import Embedded from '/docs/_shared/_quickstarts/_embedded-expressions.mdx';

## Connect your data using resources

A resource is a saved set of user-configured properties that determines how Retool connects to a data source, such as a [PostgreSQL](../data-sources/guides/integrations/database/postgresql.mdx) database or [REST API](../data-sources/guides/integrations/api/rest.mdx). You create a resource for each data source you want to use with Retool, then write queries to interact with them.

Each resource has configuration options that Retool uses when interacting with a data source. This simplifies how you query data sources while ensuring access is secure.

When a resource query is run, Retool proxies the request to the data source, server-side, using the resource's configuration settings. This means only Retool directly connects to a data source, not your users.

## Read and write data using queries

A query is a piece of code you write to interact with a resource and perform CRUD operations. As with components, queries maintain an internal state and expose properties. Queries can also perform asynchronous actions and run simultaneously with other queries.

Queries are not part of an app's user interface. You reference the query's `data` property to read and display data in components for which users can interact. Queries can also reference component input values and write them back to the data source.

The Table component's **Data source** (`data`) property in the IDE is set to **query1**. Running the query retrieves the specified data in the query statement, which is then displayed in the table.

  

  
  
  

A resource query to retrieve data from a table.

The Table component's **Data source** is set to reference the resource query.

  

**query2** is set to update or create a record into the **sample_users** table. It uses a specified `id` to determine which record to update. Running the query updates or creates a record using the input values available in `{{form1.data}}`.

  

  
  
  

A GUI mode query to update or create a record.

  

## Connect the interface and code together

Retool maintains a dependency graph for each app. This represents all property values and where they're referenced. Whenever a property changes, all downstream references using embedded expressions automatically update. This is similar to how spreadsheet formulas work when referencing cell values; if a referenced value changes then the formula instantly updates its result.

The **Component tree** and **Code** panels contain the **Graph** pane. This visualizes the dependency graph for a selected component or query. You can use the graph to view the relationship between the selected component or query and its immediate dependents.

export function ArcadeGraph() {
  return (
    
      
    
  )
}

:::tip

Hover the cursor over the connecting points to display a tooltip with a list of dependencies and dependents.

:::

You can chain embedded expressions together by referencing values that also use embedded expressions. Any input value changes automatically propagate through the entire app. This makes it possible to build powerful applications with very few lines of code.

:::info Dependency cycles
The dependency graph enables Retool to prevent you from creating circular dependencies, where two or more properties rely on each other to function. If you attempt to reference values that rely on one another, the IDE displays a warning.

A circular dependency warning.

:::

The following example demonstrates how the dependency graph connects components and queries together within an app. The resource query references `{{textInput1.value}}` and runs automatically whenever this value changes. The table references `{{query1.data}}` and displays updated results in real-time.

  

  
  
  

  The resource query references `{{textInput1.value}}` and runs whenever it changes.
  

  

## Control and run queries with event handlers

You configure event handlers to perform actions whenever a specific event occurs, such as a button click or query error. There are numerous actions available, such as setting component values and triggering queries.

The following example uses an event handler to display a notification whenever a Saturday or Sunday is selected in the date input field.

  

  
  

The event handler shows an error notification when a date is selected, but only runs if the specified day is a weekend.

  

## Transform data using transformers

While you can use JavaScript within `{{ }}` embedded expressions, you may need to manipulate data and implement complex logic to produce values, such as filtering or joining data sets.

A [transformer](../queries/guides/transformers.mdx) is a reusable block of JavaScript. You reference property values using embedded expressions and the results of the transformation are output on the transformer's `value` property using a `return` statement.

The following example uses a transformer to split a list of names into first and last names. The Listbox component references `{{transformer1.value}}`, which provides an array of last names.

  

 
  
  

  The resource query runs whenever the limit is changed.
  

The transformer references `{{query1.data.name}}`, splits the full name, and returns the last name. It updates whenever the query data changes.

  

Transformers are maintained by the dependency graph and continually output a transformed value. If you make changes to any values in the dependency chain, the transformer automatically updates.

If you only need to transform query data, you can include transformation code directly in the query. The transformed data is then made available in the query's `data` property.

## Script apps with JavaScript

  

You can write JavaScript code to control app behavior, set property values, and more. Components and queries are globally scoped and include built-in methods to control their state. These methods can accept supported parameters, such as string or index values. In either case, you only need to use standard JavaScript notation, not `{{ }}` embedded expressions.

  
  

:::note JavaScript output
JavaScript queries can output data using a `return` statement.
:::

  

The following example adjusts the selected date by a specified number of days, then sets this as the value for the calendar.

  

  
  
  

The JavaScript code adjusts the selected date by the specified number of days, then uses `setValue()` to set the value of `calendarInput`.

  

## Embed content and build custom components

Retool's component library contains over 100 UI components, from input fields to image grids. For complex use cases that require UI components not currently provided, you can embed custom content or build a custom component. These components also have state and you can use `{{ }}` embedded expressions.

- [HTML](./reference/components/custom/html.mdx): A container for custom HTML and CSS code.
- [IFrame](./reference/components/custom/iframe.mdx): An embedded web page that supports permissions to allow or deny downloads, form submissions, microphone and camera access, and popups.
- [Custom Component Libraries](./guides/custom/custom-component-libraries/index.mdx): A library of custom-built, embedded components using React and TypeScript. Build once and then deploy into any app.

The following example demonstrates the HTML component.

  

  
  
  

HTML component example.

  

## Wrap up

Using these fundamental concepts, you can build complex web apps that:

- Contain polished user interfaces that can adapt to your needs.
- Dynamically reference values anywhere using dependency chains.
- Connect with almost any API and database to interact with your data.
- Perform actions using conditional logic in response to user events or script apps to control behavior.

The following example combines these concepts to create a user subscription dashboard that can filter, search, and edit users. Selecting a user in the table opens the Drawer frame with input fields prefilled with the selected row data.

---

## The Button Group component for Retool Apps

import MoreInfo from "./_more-info.mdx";

## Properties

## Methods

## Events

---

## The Button component for Retool Apps

import MoreInfo from "./_more-info.mdx";

## Properties

## Methods

## Events

---

## The Close Button component for Retool Apps

:::info Preset component

{frontMatter.sidebar_label} is a preset version of [Button](./button.mdx). It has been preconfigured with an icon.

:::

import MoreInfo from "./_more-info.mdx";

## Properties

## Methods

## Events

---

## The Dropdown Button component for Retool Apps

import MoreInfo from "./_more-info.mdx";

## Properties

## Methods

## Events

---

## Button components for Retool Apps



---

## The Link List component for Retool Apps

import MoreInfo from "./_more-info.mdx";

## Properties

## Methods

## Events

---

## The Link component for Retool Apps

import MoreInfo from "./_more-info.mdx";

## Properties

## Methods

## Events

---

## The Outline Button component for Retool Apps

:::info Preset component

{frontMatter.sidebar_label} is a preset version of [Button](./button.mdx). The `styleVariant` proprety has been preconfigured to `outline`.

:::

import MoreInfo from "./_more-info.mdx";

## Properties

## Methods

## Events

---

## The Split Button component for Retool Apps

import MoreInfo from "./_more-info.mdx";

## Properties

## Methods

## Events

---

## The Toggle Button component for Retool Apps

import MoreInfo from "./_more-info.mdx";

## Properties

## Methods

## Events

---

## The Toggle Link component for Retool Apps

import MoreInfo from "./_more-info.mdx";

## Properties

## Methods

## Events

---

## The Bar Chart component for Retool Apps

The Bar Chart component renders data in vertical bars. Bar Chart is useful for visualizing categorical data.

import MoreInfo from "./_more-info.mdx";

## Properties

## Methods

## Events

---

## The Bubble Chart component for Retool Apps

The Bubble Chart component renders data on an x-axis and y-axis, with bubble size relative to value. Bubble Chart is useful for visually identifying relationships between three dimensions.

import MoreInfo from "./_more-info.mdx";

## Properties

## Methods

## Events

---

## The Funnel Chart component for Retool Apps

The Funnel Chart component renders data in a funnel shape, with sections of decreasing size. Funnel Chart is useful for visualizing data in different stages of a process.

import MoreInfo from "./_more-info.mdx";

## Properties

## Methods

## Events

---

## The Heat Map component for Retool Apps

The Heat Map component renders data on an x-axis and y-axis, with a value for each point on the map. Heat Map is useful for visualizing intensity and identifying patterns or correlations.

import MoreInfo from "./_more-info.mdx";

## Properties

## Methods

## Events

---

## Chart components for Retool Apps



---

## The Line Chart component for Retool Apps

The Line Chart component renders data as a line on an x-axis and y-axis. Line Chart is useful for visualizing data over time.

import MoreInfo from "./_more-info.mdx";

## Properties

## Methods

## Events

---

## The Mixed Chart component for Retool Apps

The Mixed Chart component renders data on an x-axis and multiple y-axes, and it can combine several different chart types. Mixed Chart is useful for visualizing the relationship between several different datasets.

import MoreInfo from "./_more-info.mdx";

## Properties

## Methods

## Events

---

## The Pie Chart component for Retool Apps

The Pie Chart component renders data in a circle, with percentage-based slices. Pie Chart is useful for visualizing how different parts make up a whole.

import MoreInfo from "./_more-info.mdx";

## Properties

## Methods

## Events

---

## The Plotly JSON Chart component for Retool Apps

Use the Plotly JSON Chart component to build charts with raw Plotly JSON structured data. Plotly JSON Chart is useful for visualizing data when you need full control over the data and layout of the chart.

import MoreInfo from "./_more-info.mdx";

## Properties

## Methods

## Events

---

## The Sankey Chart component for Retool Apps

The Sankey Chart component renders data as links between nodes. Sankey Chart is useful for visualizing how data moves between different stages or categories.

import MoreInfo from "./_more-info.mdx";

## Properties

## Methods

## Events

---

## The Scatter Chart component for Retool Apps

The Scatter Chart component renders data as points on an x-axis and y-axis. Scatter Chart is useful for visualizing how data moves between different stages or categories.

import MoreInfo from "./_more-info.mdx";

## Properties

## Methods

## Events

---

## The Sparkline component for Retool Apps

The Sparkline component renders data as a line on an x-axis and y-axis. Sparkline is useful for visualizing data over time. It is a simplified version of the [Line Chart](./line-chart.mdx) component.

Refer to the [charts guide](../../../guides/data/charts.mdx) for more information on building charts.

import MoreInfo from "./_more-info.mdx";

## Properties

## Methods

## Events

---

## The Stacked Bar Chart component for Retool Apps

The Stacked Bar Chart component renders data in vertical bars, with stacked segments representing different groups or subcategories. Stacked Bar Chart is useful for visualizing categorical data and comparing data across multiple groups.

import MoreInfo from "./_more-info.mdx";

## Properties

## Methods

## Events

---

## The Sunburst Chart component for Retool Apps

The Sunburst Chart component renders hierarchical data in a series of concentric rings. Sunburst Chart is useful for visualizing parent-child relationships and how parts make up a whole.

import MoreInfo from "./_more-info.mdx";

## Properties

## Methods

## Events

---

## The Treemap component for Retool Apps

The Treemap component renders hierarchical data as nested rectangles. Treemap is useful for visualizing data with many categories and comparing the size and relationship of those categories.

import MoreInfo from "./_more-info.mdx";

## Properties

## Methods

## Events

---

## The Waterfall Chart component for Retool Apps

The Waterfall Chart component renders data as a series of steps with sequential positive and negative values. Waterfall Chart is useful for visualizing how incremental values contribute to a total.

import MoreInfo from "./_more-info.mdx";

## Properties

## Methods

## Events

---

## Retool Apps components reference



---

## The Collapsible Container component for Retool Apps

:::info Preset component

{frontMatter.sidebar_label} is a preset version of [Container](./container.mdx). It has been preconfigured with a collapsible body and [Toggle Button](../buttons/toggle-button.mdx) component.

:::

---

## The Container component for Retool Apps

import MoreInfo from "./_more-info.mdx";

## Properties

## Methods

## Events

---

## The Form component for Retool Apps

Form can automatically generate input fields from a database or Table component, and populate nested input field values using default data.

:::tip
For more information about using the Form component, refer to the [forms guide](../../../guides/forms-inputs/forms/index.mdx).
:::

## Properties

## Methods

## Events

---

## Container and form components for Retool Apps



---

## The JSON Schema Form component for Retool Apps

JSON Schema Form is based on the [react-jsonschema-form](https://github.com/rjsf-team/react-jsonschema-form) library. You define which input fields to include and how they are rendered using JSON and UI schemas.

The component can also populate nested input field values using default data.

:::tip
For more information about using the Form component, refer to the [forms guide](../../../guides/forms-inputs/forms/index.mdx).
:::

## Properties

## Methods

## Events

---

## The Link Card component for Retool Apps

:::info Preset component

{frontMatter.sidebar_label} is a preset version of [Container](./container.mdx). It has been preconfigured with [Icon](../presentation/icon.mdx) and [Text](../presentation/text.mdx) components.

:::

---

## The Stack component for Retool Apps

import MoreInfo from "./_more-info.mdx";

## Properties

## Methods

## Events

---

## The Stepped Container mobile component

:::info Preset component

{frontMatter.sidebar_label} is a preset version of [Container](./container.mdx). It has been preconfigured with multiple views and the [Steps](../navigation/steps.mdx) component.

:::

---

## The Tabbed Container component for Retool Apps

:::info Preset component

{frontMatter.sidebar_label} is a preset version of [Container](./container.mdx). It has been preconfigured with multiple views and the [Tabs](../navigation/steps.mdx) component.

:::

---

## The Wizard component for Retool Apps

{frontMatter.sidebar_label} groups component into a series of steps that follow a linear path or branch into multiple ones. It supports running queries on step change or reset, virtual steps that display a loading screen, and setting the initial step dynamically.

:::tip
For more information about using the Form component, refer to the [forms guide](../../../guides/interaction-navigation/wizard.mdx).
:::

## Properties

## Methods

## Events

---

## HTML

HTML supports most HTML tags and presentational attributes. Scripting is not supported but [event handlers](#events) are available.

To embed web pages, use an [IFrame](./iframe.mdx) component. For functionality that isn't available in other components, or to use React or HTML and JavaScript, use a [Custom Component](../legacy/custom-component.mdx).

import WebPlayground from "/docs/_partials/_components/_web-playground.mdx";

## Properties

## Events

To attach an event handler to a specific element in the HTML, add a `data--target` attribute. For example:

```html title="Click event handler"
Trigger event
```

---

## IFrame

IFrame supports permissions to allow or deny downloads, form submissions, microphone and camera access, and popups.

Use an [HTML](./html.mdx) component to include custom HTML and CSS instead of an embedded web page. You can also use a [Custom Component](../legacy/custom-component.mdx) to include custom React, HTML, and JavaScript.

:::tip
For more information about using the IFrame component, refer to the [iframes guide](../../../guides/custom/iframe.mdx).
:::

## Properties

## Methods

---

## Custom components for Retool Apps



---

## The Filter component for Retool Apps

Filter connects to a [Table](./table.mdx) component to dynamically filter its displayed data.

## Properties

## Methods

## Events

---

## Data components for Retool Apps



---

## The JSON Explorer component for Retool Apps

## Properties

## Methods

## Events

---

## The Key Value component for Retool Apps

## Properties

## Methods

## Events

---

## The Reorderable List component for Retool Apps

## Properties

## Methods

## Events

---

## The Table component for Retool Apps

Table renders an array of data, such as database records, as a tabulated list. Refer to the [working with tables](../../../guides/data/table/index.mdx) guide to learn more about using the Table component.

## Properties

## Methods

## Events

---

## The Calendar Input component for Retool Apps

## Properties

## Methods

## Events

s

---

## The Date Range component for Retool Apps

import MoreInfo from "./_more-info.mdx";

## Properties

## Methods

## Events

---

## The Date Time component for Retool Apps

import MoreInfo from "./_more-info.mdx";

## Properties

## Methods

## Events

---

## The Date component for Retool Apps

import MoreInfo from "./_more-info.mdx";

## Properties

## Methods

## Events

---

## The Day component for Retool Apps

:::info Preset component

{frontMatter.sidebar_label} is a preset version of [Select](../select-inputs/select.mdx). It has been preconfigured with a list of values.

:::

## Properties

## Methods

## Events

---

## Date and time components for Retool Apps



---

## The Month component for Retool Apps

:::info Preset component

{frontMatter.sidebar_label} is a preset version of [Select](../select-inputs/select.mdx). It has been preconfigured with a list of values.

:::

## Properties

## Methods

## Events

---

## The Time component for Retool Apps

import Timezones from "/docs/_partials/_components/_timezones.mdx";

import MoreInfo from "./_more-info.mdx";

## Properties

## Methods

## Events

---

## The Year component for Retool Apps

:::info Preset component

{frontMatter.sidebar_label} is a preset version of [Select](../select-inputs/select.mdx). It has been preconfigured with a list of values.

:::

## Properties

## Methods

## Events

---

## The Auth Login component for Retool Apps

Auth Login enables users to authenticate with a resource configured to use [custom API authentication](../../../../data-sources/guides/authentication/custom.mdx).

## Properties

## Methods

## Events

---

## Integration components for Retool Apps



---

## The Looker component for Retool Apps

Looker embeds a [Looker](https://looker.com/) visualization dashboard.

## Properties

## Methods

## Events

---

## The Mapbox Map component for Retool Apps

An embedded map using [MapBox](https://www.mapbox.com/) to display latitude and longitude coordinates as points of interest. It supports custom markers using Emoji and symbols, default coordinates and zoom level, and GeoJSON to render custom shapes for highlighting locations.

Mapbox Map can trigger queries when panning or zooming the map, and on hover start or end for points.

Refer to the [Mapbox guide](../../../guides/presentation-styling/mapbox.mdx) for more information about using this component.

## Properties

## Methods

## Events

---

## The Stripe Card Form component for Retool Apps

An embedded payment form to securely submit collect and submit card information to [Stripe](https://stripe.com/). A token is returned that can be used to charge the card.

## Properties

## Methods

## Events

---

## The Tableau component for Retool Apps

Tableau embeds a [Tableau](https://www.tableau.com/) visualization dashboard.

## Properties

## Methods

## Events

---

## The Alert component for Retool Apps

import Legacy from "./_callout.mdx";

## Properties

## Methods

## Events

---

## The Button Group component for Retool Apps

import Legacy from "./_callout.mdx";

## Properties

## Methods

## Events

---

## The Legacy Cascader component for Retool Apps

:::note

This component is considered legacy and will be deprecated in the future. Use the newer version of the [Cascader component](./cascader.mdx), which has more features available.

:::

## Properties

## Methods

## Events

---

## The Chart component for Retool Apps

:::note

This component is considered legacy and will be deprecated in the future.  Use the newer [Chart components](../charts/index.mdx), which have more features available.

:::

## Properties

## Events

---

## Legacy Checkbox Tree component for Retool Apps

:::note

This component is considered legacy and will be deprecated in the future. Use the newer version of the [Checkbox Tree component](./checkbox-tree.mdx) which has more features available.

:::

## Properties

## Methods

## Events

---

## Custom Component

This is a legacy component that is no longer supported. Use [custom component libraries](../../../guides/custom/custom-component-libraries/index.mdx) if you want to build custom components for Retool apps.

Custom Component supports passing data to and from the Retool app. It can be granted permission for popups, link redirection, and camera access.

:::info Use cases

You should only build a custom component if you need functionality that isn't already available in an existing component. You can also use an [IFrame](../custom/iframe.mdx) component to embed web pages or the [HTML](../custom/html.mdx) component to include custom HTML and CSS.

:::

## Properties

## Methods

## Events

---

## The Key Value component for Retool Apps

import Legacy from "./_callout.mdx";

## Properties

## Methods

## Events

---

## The List View component for Retool Apps

import Legacy from "./_callout.mdx";

List View supports unique row keys and displays rows using dynamic or fixed height. Rows can contain any combination of components to customize how data is interacted with or presented.

## Properties

## Methods

## Events

---

## The Breadcrumbs component for Retool Apps

import MoreInfo from "./_more-info.mdx";

## Properties

## Methods

## Events

---

## Navigational components for Retool Apps



---

## The Navigation component for Retool Apps

Navigation supports both horizontal and vertical orientation, and can include an image to display a logo next to menu items.

The component supports linking an item directly to other apps or pages. Doing so will automatically highlight the item (or its parent) when currently viewing its linked app, and hide it for any users who do not have access to its linked app.

import MoreInfo from "./_more-info.mdx";

## Properties

## Methods

## Events

---

## The Page Input component for Retool Apps

Page Input displays the total number of pages and automatically updates to reflect the currently selected page. Use [Pagination](./pagination.mdx) for navigating pages of data using numbered links.

import MoreInfo from "./_more-info.mdx";

## Properties

## Methods

## Events

---

## The Pagination component for Retool Apps

Pagination adjusts the number of pages shown to fit its width and automatically updates to reflect the currently selected page. Use [Page Input](./page-input.mdx) for navigating to specific pages of data using an input field.

import MoreInfo from "./_more-info.mdx";

## Properties

## Methods

## Events

---

## The Steps component for Retool Apps

Steps is a set of UI elements that represent sequential steps. It can be linked to [Container](../containers-forms/container.mdx) to create [Stepped Container](../containers-forms/stepped-container.mdx).

## Properties

## Methods

## Events

---

## The Tabs component for Retool Apps

Tabs is a set of clickable UI elements to represent tabbed menu items. It can be linked to [Container](../containers-forms/container.mdx) to create [Tabbed Container](../containers-forms/tabbed-container.mdx).

## Properties

## Methods

## Events

---

## The Currency component for Retool Apps

:::info Preset component

{frontMatter.sidebar_label} is a preset version of [Number Input](./number-input.mdx). It has been preconfigured with common options for currency input.

:::

import MoreInfo from "./_more-info.mdx";

## Properties

## Methods

## Events

---

## The Editable Number component for Retool Apps

Editable Number functions similarly to [Number Input](./number-input.mdx) but renders as plain text until clicked.

import MoreInfo from "./_more-info.mdx";

## Properties

## Methods

## Events

---

## The Number Input component for Retool Apps

import MoreInfo from "./_more-info.mdx";

## Properties

## Methods

## Events

---

## Number input components for Retool Apps



---

## The Percent component for Retool Apps

:::info Preset component

{frontMatter.sidebar_label} is a preset version of [Number Input](./number-input.mdx). It has been preconfigured with common options for percentage input.

:::

import MoreInfo from "./_more-info.mdx";

## Properties

## Methods

## Events

---

## The Phone Number Input component for Retool Apps

import MoreInfo from "./_more-info.mdx";

## Properties

## Methods

## Events

---

## The Range Slider component for Retool Apps

import MoreInfo from "./_more-info.mdx";

## Properties

## Methods

## Events

---

## The Rating component for Retool Apps

import MoreInfo from "./_more-info.mdx";

## Properties

## Methods

## Events

---

## The Slider component for Retool Apps

import MoreInfo from "./_more-info.mdx";

## Properties

## Methods

## Events

---

## The Alert component for Retool Apps

For more information about this component, refer to the [Informational components](../../../guides/presentation-styling/presentation.mdx) guide.

## Properties

## Methods

## Events

---

## The Avatar Group component for Retool Apps

Avatar Group adjusts the number of avatars shown to fit within its width or according to the specified maximum number, whichever is smaller. If there are more, the final avatar is a count of user photos that cannot be shown.

## Properties

## Methods

## Events

---

## The Avatar component for Retool Apps

## Properties

## Methods

## Events

---

## The Calendar component for Retool Apps

Calendar uses the [FullCalendar](https://fullcalendar.io/docs/event-object) library.

For more information on using the Calendar component, refer to the [getting started guide](../../../guides/presentation-styling/calendar.mdx).

## Properties

## Methods

## Events

---

## The Circular Image component for Retool Apps

:::info Preset component

{frontMatter.sidebar_label} is a preset version of [Image](./image.mdx). It has been preconfigured with a 50% border radius.

:::

## Properties

## Methods

## Events

---

## The Divider component for Retool Apps

## Properties

## Methods

---

## The Event List web component

## Properties

## Methods

## Events

---

## The Icon Text component for Retool Apps

## Properties

## Methods

## Events

---

## The Icon component for Retool Apps

## Properties

## Methods

## Events

---

## The Image Grid component for Retool Apps

## Properties

## Methods

## Events

---

## The Image component for Retool Apps

## Properties

## Methods

## Events

---

## Presentation components for Retool Apps



---

## The PDF component for Retool Apps

For more information about this component, refer to the [Embedded media](../../../guides/presentation-styling/embedded-media.mdx) guide.

## Properties

## Methods

## Events

---

## The Progress Bar component for Retool Apps

For more information about this component, refer to the [Informational components](../../../guides/presentation-styling/presentation.mdx) guide.

## Properties

## Methods

## Events

---

## The Progress Circle component for Retool Apps

For more information about this component, refer to the [Informational components](../../../guides/presentation-styling/presentation.mdx) guide.

## Properties

## Methods

## Events

---

## The QR Code component for Retool Apps

For more information about this component, refer to the [Embedded media](../../../guides/presentation-styling/embedded-media.mdx) guide.

## Properties

## Methods

## Events

---

## The Spacer component for Retool Apps

Spacer preserves white space in a layout whenever components positioned above are collapsed, hidden, or resized.

## Properties

## Methods

---

## The Statistic component for Retool Apps

For more information about this component, refer to the [Informational components](../../../guides/presentation-styling/presentation.mdx) guide.

## Properties

## Methods

## Events

---

## The Status component for Retool Apps

For more information about this component, refer to the [Informational components](../../../guides/presentation-styling/presentation.mdx) guide.

## Properties

## Methods

## Events

---

## The Tags component for Retool Apps

For more information about this component, refer to the [Informational components](../../../guides/presentation-styling/presentation.mdx) guide.

## Properties

## Methods

## Events

---

## The Text component for Retool Apps

Text supports [Github Flavored Markdown](https://github.github.com/gfm/) and most presentation attributes, such as images, links, and text formatting.

For more information about this component, refer to the [Informational components](../../../guides/presentation-styling/presentation.mdx) guide.

## Properties

## Methods

## Events

---

## The Timeline web component

For more information about this component, refer to the [Timeline guide](../../../guides/presentation-styling/timeline.mdx).

## Properties

## Methods

## Events

---

## The Video component for Retool Apps

Video supports popular video services (e.g., YouTube and Vimeo) and direct links to video file types commonly supported by browsers.

For more information about this component, refer to the [Embedded media](../../../guides/presentation-styling/embedded-media.mdx) guide.

:::note

To embed YouTube Shorts using the Video component, use the following URL format: `https://www.youtube.com/embed/{identifier_string}`, where `identifier_string` is the identifier at the end of the YouTube Shorts URL.

:::

## Properties

## Methods

## Events

---

## Container List View

Refer to the [Repeatable components](../../../guides/layout-structure/repeatables.mdx) guide for more information about how to use the Container List View component.

:::info Preset component

{frontMatter.sidebar_label} is a preset version of [List View](./list-view.mdx). It has been preconfigured with a nested [Container](../containers-forms/container.mdx) component.

:::

---

## The Grid View component for Retool Apps

Refer to the [Repeatable components](../../../guides/layout-structure/repeatables.mdx) guide for more information about how to use the Grid View component.

:::info Preset component

{frontMatter.sidebar_label} is a preset version of [List View](./list-view.mdx). It has been preconfigured to use a grid layout.

:::

---

## Repeatable components for Retool Apps



---

## The List View component for Retool Apps

Refer to the [Repeatable components](../../../guides/layout-structure/repeatables.mdx) guide for more information about how to use the List View component.

## Properties

## Methods

## Events

---

## The Cascader component for Retool Apps

Refer to the [selection input guide](../../../guides/forms-inputs/select-inputs.mdx) for more information on using this component.

:::note

Retool recently released this [improved version of the Cascader component](/changelog/cascader-v2). Use of the [legacy Cascader component](../legacy/cascader.mdx) is discouraged.

:::

## Properties

## Methods

## Events

---

## The Checkbox Group component for Retool Apps

## Properties

## Methods

## Events

---

## Checkbox Tree component for Retool Apps

:::info Preset component

{frontMatter.sidebar_label} is a preset version of [Checkbox Group](./checkbox-group.mdx). It has been preconfigured with common options for a nested tree layout.

:::

Retool recently released this [improved version of the Checkbox Tree component](/changelog/checkbox-tree-v2), and use of the [legacy component](../legacy/checkbox-tree.mdx) is discouraged.

## Properties

## Methods

## Events

---

## The Checkbox component for Retool Apps

## Properties

## Methods

## Events

---

## Select input components for Retool Apps



---

## The Listbox component for Retool Apps

Refer to the [selection input guide](../../../guides/forms-inputs/select-inputs.mdx) for more information on using this component.

## Properties

## Methods

## Events

---

## The Multiselect Listbox component for Retool Apps

Refer to the [selection input guide](../../../guides/forms-inputs/select-inputs.mdx) for more information on using this component.

## Properties

## Methods

## Events

---

## The Multiselect component for Retool Apps

Refer to the [selection input guide](../../../guides/forms-inputs/select-inputs.mdx) for more information on using this component.

## Properties

## Methods

## Events

---

## The Radio Group component for Retool Apps

Refer to the [selection input guide](../../../guides/forms-inputs/select-inputs.mdx) for more information on using this component.

## Properties

## Methods

## Events

---

## The Segmented Control component for Retool Apps

Refer to the [selection input guide](../../../guides/forms-inputs/select-inputs.mdx) for more information on using this component.

## Properties

## Methods

## Events

---

## The Select component for Retool Apps

## Properties

## Methods

## Events

---

## The Switch Group component for Retool Apps

## Properties

## Methods

## Events

---

## The Switch component for Retool Apps

## Properties

## Methods

## Events

---

## Agent Chat component

Use the Agent Chat component to invoke a Retool Agent from within an web app.

:::note

The Agent Chat functionality cannot be used from public apps, as calling an agent requires a logged-in user.

:::

## Properties

## Methods

## Events

---

## The Annotated Text component for Retool Apps

## Properties

## Methods

## Events

---

## The Bounding Box component for Retool Apps

## Properties

## Methods

## Events

---

## The Color Input component for Retool Apps

## Properties

## Methods

## Events

---

## The Comment Thread component for Retool Apps

For more information about using this component, refer to the [Comment Thread guide](../../../guides/forms-inputs/comment-thread.mdx).

## Properties

## Methods

## Events

---

## The File Button component for Retool Apps

Refer to the [File inputs guide](../../../guides/forms-inputs/file-inputs.mdx) for more information on how to use this component.

## Properties

## Methods

## Events

---

## The File Dropzone component for Retool Apps

Refer to the [File inputs guide](../../../guides/forms-inputs/file-inputs.mdx) for more information on how to use this component.

## Properties

## Methods

## Events

---

## The File Input component for Retool Apps

Refer to the [File inputs guide](../../../guides/forms-inputs/file-inputs.mdx) for more information on how to use this component.

## Properties

## Methods

## Events

---

## Special input components for Retool Apps



---

## The LLM Chat component for Retool Apps

Refer to [Getting started with the LLM Chat component](../../../guides/forms-inputs/chats/llm-chat.mdx) for more information about this component.

## Properties

## Methods

## Events

---

## The Microphone component for Retool Apps

Recordings are Base64-encoded in WebM format, and audio playback displays a progress bar and elapsed time.

## Properties

## Methods

## Events

---

## The Scanner component for Retool Apps

## Properties

## Methods

## Events

---

## The Signature component for Retool Apps

## Properties

## Methods

## Events

---

## The Timer component for Retool Apps

## Properties

## Methods

## Events

---

## The Editable Text Area component for Retool Apps

Editable Text Area is a multi-line input field for text values. It functions similarly to [Text Area](./text-area.mdx) but renders as plain text until clicked.

## Properties

## Methods

## Events

---

## The Editable Text component for Retool Apps

Editable Text is a single-line input field for text values. It functions similarly to [Text Input](./text-input.mdx) but renders as plain text until clicked.

## Properties

## Methods

## Events

---

## The Email component for Retool Apps

:::info Preset component

{frontMatter.sidebar_label} is a preset version of [Text Input](./text-input.mdx). It has been preconfigured with common options for email address input.

:::

## Properties

## Methods

## Events

---

## Text input components for Retool Apps



---

## JSON Editor

## Properties

## Events

---

## Password

## Properties

## Methods

## Events

---

## The Rich Text Editor component for Retool Apps

## Properties

## Methods

## Events

---

## Text Area

## Properties

## Methods

## Events

---

## The Text Input component for Retool Apps

## Properties

## Methods

## Events

---

## The URL component for Retool Apps

:::info Preset component

{frontMatter.sidebar_label} is a preset version of [Text Input](./text-input.mdx). It has been preconfigured with common options for URL input.

:::

## Properties

## Methods

## Events

---

## App event handlers reference

## Events

Support for each event depends upon the component or object being configured.

## Actions

import Actions from '/docs/_shared/_event-handler-actions.mdx';

---

## The Drawer frame

The Drawer frame is a container that slides into view when visible. You can add multiple instances of the Drawer frame that operate independently.

## Properties

## Methods

---

## Retool Apps frames reference



---

## The Modal frame

The Modal frame is a container that appears as an overlay in the center of the app when visible. You can add multiple instances of the Modal frame that operate independently.

## Properties

## Methods

## Events

---

## The Sidebar frame

The Sidebar frame is a container that appears as next to the Main frame. Sidebar can automatically configure an optimized mobile layout when viewing web apps on devices with smaller screens. Only one instance of Sidebar can be used.

## Properties

## Methods

---

## The Split Pane frame

The Split Pane frame is a container that appears next to the Main frame. Only one instance of Split Pane can be used.

## Properties

## Methods

---

## Apps glossary

import SharedGlossary from '../../_partials/_glossary.mdx';

---

## App IDE keyboard shortcuts

Use Retool's built-in keyboard shortcuts to quickly perform certain actions or control the interface. You can also configure [custom keyboard shortcuts](./keyboard-shortcuts.mdx) on a per-app basis that can trigger queries, interact with components, and more.

---

## Modules

A module is a type of app you can build and embed within other apps. Modules enable you to reuse components and code in multiple apps, and modules can pass data to and from the parent app. This can help reduce app maintenance as you only need to update a module; any parent apps immediately reflect the changes.

Refer to the [modules guide](../guides/layout-structure/modules.mdx) for more information about creating and using modules.

## Properties

## Methods

---

## The Current User object

:::tip Not available in public apps

`current_user` is `null` for publicly accessible apps that do not require authentication.

:::

## Properties

---

## The Retool Context object

## Properties

---

## The Theme object

## Properties

## Methods

---

## The Url object

## Properties

---

## The Viewport object

## Properties

---

## Retool CLI reference

import Cli from "/docs/_shared/_retool-cli.mdx";

---

## Built-in URL parameters

:::warning

[Public apps](../guides/app-management/embed-apps.mdx#public-apps) do not support built-in URL parameters.

:::

Use built-in URL search parameters to control certain app characteristics when launched, such as loading a specific release or environment. Append `?` to the app's URL and include the parameters as `key=value` pairs. Separate multiple parameters with `&`.

Refer to the [URL parameters guide](../guides/app-management/url-parameters.mdx) for information on configuring custom parameters for use in apps.

---

## Retool Apps reference

import Reference from "/docs/_partials/_doctypes/_reference.mdx";

:::tip Get started with web apps

If you are unfamiliar with Retool web apps and want to get started:

- Read the [quickstart](./quickstart) to learn about the fundamental concepts.
- Follow a [tutorial](./tutorial/) to build your first web app.

:::

---

## Retool apps tutorial: Save changes to data

import GoBack from "../_go-back.mdx";

Now that the table displays customer data, you can configure the form to make changes. Submitting the form then triggers a query that writes the changes back to the API.

## 1. Generate input fields

Inside the Form component, click **Generate from table** and select the Table component in the **Source** dropdown.

The form generator modal displays a list of table columns to review. All columns are selected and set as required fields by default. For the purposes of this tutorial, uncheck all columns except **First** and **Last**, then click **Generate form**.

The generated form and its input fields are automatically configured as follows:

- The form's **Data source** setting is set to `{{ table1.selectedRow }}`. This references the selected row in the table.
- The **Form data key** setting for each input component references the column name. For instance, the key for `firstInput` is set to `first`. This enables the form to automatically populate its value from the selected row.

The form and its input fields update whenever you select a different table row.

export function ArcadeInputs() {
  return (
    
      
    
  );
}

## 2. Create a query to save changes

Navigate to the **Code** tab and add another resource query to the **customersPage** section. Update the query to reference the form's input values and save the changes back to the API.

1. Select the **Sample Customers** resource.
1. Change the query action to **PATCH**. This API method is commonly used to make changes to existing data.
1. Reference the currently selected customer in the query's URL. You interact with specific customers using the mock API by referencing the customer ID. To obtain this, set the URL path to `{{ table1.selectedRow.id }}`. This dynamically references the ID of the selected customer in the table.

Next, reference the form's input values in the request body. Add the following key-value information to the **Body** section of the query:

| Key     | Value                    |
| ------- | ------------------------ |
| `first` | `{{ firstInput.value }}` |
| `last`  | `{{ lastInput.value }}`  |

When run, the resource query performs a PATCH request with the specified customer and updates the `first` and `last` values.

Click **Save** to save the query. Click **Test** to preview the results of the query.

export function PatchQuery() {
  return (
    
      
    
  )
}

## 3. Add an event handler to the query

To ensure that the table continually displays the most current customer data, this query should refresh the table when it successfully runs. Refreshing the table triggers the first query that retrieves customers, which will now include any saved changes.

Add an event handler to the query that refreshes the table when it successfully runs.

1. In the **Event handlers** settings of the query, add a **Success** event handler.
1. Select the **Control component** event action.
1. Select the **table1** component.
1. Select the **Refresh** method.

Click **Save** to save the query.

export function EventHandler() {
  return (
    
      
    
  )
}

## 4. Update the form to trigger the query

Now you're ready to update the Form to trigger the new query. Select the Form and use the Inspector to add an event handler. The event handler needs to run on the **Submit** event, control the newly created query, and trigger it.

Now that the form and query are complete, you can now select a customer and edit their details. When you click **Submit**, the query runs automatically and updates the selected customer, then refreshes the table.

export function ArcadeSubmit() {
  return (
    
      
    
  );
}

## Next steps

You've now completed a fully working Retool app that reads and writes API data. Part two of this tutorial explains how to extend the functionality of your app.

---

## Retool apps tutorial: Connect and query data

import GoBack from '../_go-back.mdx';

A resource is a set of user-configured properties that determine how Retool interacts with a data source, such as an API. You create a resource for each data source you want to use with Retool. You can then select the resource when writing a query so you can interact with the data.

## 1. Generate a mock API with customer data

## 2. Create a resource query

First, add a resource query to retrieve customer data. Before the query can do this, you must create the necessary resource. In this example, you will create a resource directly from the **Code** tab.

Click  in the left panel to open the **Code** tab. In the **customersPage** section, click  and select **Resource query**.

`query1` populates with a list of suggested [resources](../../../data-sources/quickstarts/resources.mdx) in your Retool organization for which you have access. 

## 3. Create a REST API resource

Retool supports most databases and APIs, and you can use built-in [integrations](../../../data-sources/tutorials.mdx) to connect to popular data sources with minimal configuration. 

The [mock API](#1-generate-a-mock-api-with-customer-data) is a basic REST API with no authentication. To create a resource for it:

1. Click the **Resource** dropdown and select **+ Create a new resource**.
1. Select the **REST API** integration.
1. Set the resource name to **Sample Customers** and then paste the copied API URL into the **Base URL** field.
1. Click **Create resource**.

The resource query updates to perform a GET request with the newly created resource. Click **Save & Run** to save the resource query and run it. The query then retrieves the list of customers from the API.

export function ArcadeQuery() {
  return (
    
      
    
  )
}

## 4. Update the table to display customer data

The Table component's **Data source** is currently set to use its own demo data. Select the Table and, in the **Inspector**, change the data source to `query1`. 

Once updated, the table automatically updates to display the query results. It also attempts to configure the column formats for each field, such as `email` and `id`. 

You can rearrange the columns or update the specified format from the **Columns** section of the Inspector. For example, the **Postal** column should be formatted as a string. If it appears to use the **Number** format, select the column and update its **Format** setting.

export function ArcadeTable() {
  return (
    
      
    
  )
}

## Next steps

You've now created a resource and can read with data using queries. Move on to the next section and learn how write changes back to the data source.

---

## Retool apps tutorial: Fundamental concepts

import PartOne from "../_part1.mdx";

Learn how to assemble your app's user interface.

---

## Retool apps tutorial: Assemble the user interface

import AddUI from "/img/icons/apps/add-ui.svg";

Retool apps group code, components, and frames into pages. Apps can contain a single page or you can build more complex apps using multiple pages.

Part one of this tutorial uses a single page to display and edit customer data. In [part two](../part-two/index.mdx), you will add another page to view customer subscription data.

## Edit the default page

All apps initially start with a single page, **defaultPage**. Update the details for this page to use it for customers.

1. Click  in the left panel to open the **Pages** tab. 
2. Right-click **page1** in the **Pages** pane and select **Rename**.
3. Rename the page to `customersPage`. The page name is the unique identifier for this object within a Retool app.
4. In the **Inspector** on the right-side of the IDE, update the **Page title** to `Customers`. The title is what Retool displays in the browser and in navigation components.
5. Change the **Page URL** to `customers`. The URL is the path at which the page resides.

export function ArcadeEditPage() {
  return (
    
      
    
  )
}

## Add a table and form to the page

Components are interface elements with which users interact, such as input fields and buttons. You assemble the app's user interface by placing components anywhere on the canvas of a selected page, and can adjust their position and size.

### Add the table

The [Table](../../guides/data/table/index.mdx) component enables you to display and interact with data using a feature-rich table.

1. Click  in the left panel to open the **Add UI tab**.
1. Click-and-drag the Table component to the canvas area.

The Table component's **Data source** initially contains demo data. In the [next part](./data.mdx) of this tutorial, you will write a query to retrieve customer data and display it in the table. 

### Add the form

This app uses a [Form](../../guides/forms-inputs/forms/index.mdx) component to edit customer details. When configured, the form's input fields will display details of the customer selected in the table. A form can generate the necessary input fields from a table so you don't have to build it from scratch. You will configure this later on.

Repeat the steps above to add a Form component then place it next to the table.

export function ArcadeAdd() {
  return (
    
      
    
  )
}

## Next steps

You've now assembled the user interface for your Retool app. Move on to the next section and learn how to connect and query data.

---

## Retool apps tutorial: Advanced features

import PartTwo from "../_part2.mdx";

Learn how to extend your app's user interface with multiple pages.

---

## Retool app tutorial: Map and filter data

import GoBack from '../_go-back.mdx';

The Table component in the subscriptions page currently displays all subscription data. The next step is to filter the results by selecting a customer.

## 1. Map customers to select options

For components that support multiple options, such as Select, you can either manually configure each option or [dynamically map them](../../guides/forms-inputs/option-lists.mdx) from an array of data, such as query results. Retool evaluates each item in the array of data and makes it available as `item` to define option lists.

Navigate to the subscriptions page and map the customer data to the Select component's list of options.

1. Click on the Select component to view its settings in the **Inspector**. 
1. Set the component to **Mapped** mode and set the **Data source** to `query1`.

Configure the following in **Mapped options**:

- Value: `{{ item.id }}`
- Label: `{{ item.first }} {{item.last }}`
- Caption: `{{ item.email }}`

When set, each option value references the customer's ID. The label for each option is the customer's full name, with their email address appearing as the caption.

export function ArcadeMap() {
  return (
    
      
    
  )
}

## 2. Connect the subscriptions API and update the table

:::info

When generating the mock API for subscriptions, the number of items should be equal to, or greater than, the number previously used for the customers API.

:::

Next, add a resource query and create a resource for the subscriptions API. This is the [same process](../part-one/data.mdx) you used to connect the customers API.

Once you're done, set the **Data source** for your new table to `query3`.

export function ArcadeSubs() {
  return (
    
      
    
  )
}

## 3. Filter table results based on the selected customer

The Select component contains a list of customers from which to select. When you select a customer, the component's `value` property is set to the customer ID.

The subscription data that you just added contains a `customer_id` field. Configure the Table component with a default filter so that it only displays subscriptions that correspond ot the selected customer. The table then automatically updates whenever you select a different customer.

1. Click on the Table component to view its settings in the **Inspector**. 
2. In the **Default filters** settings, click .
3. Set the **Column** to the **Customer ID** by which to filter.
4. Select the **=** operator.
5. Set the filter value to `{{ select1.value }}`.

export function ArcadeFilter() {
  return (
    
      
    
  )
}

## 4. Disable selection of customers without a subscription

Although the table now displays subscription data for a selected customer, there are some customers without any subscriptions. Update the Select component so that customers with no descriptions are disabled. 

You can do so by setting a mapped option to an embedded expression that performs conditional logic. For instance, you can configure the **Disabled** mapped option for the Select component to disable any customer options where the customer has no subscription.

Click the Select component and set the **Disabled** setting to the following expression:

```js
{{ query3.data.filter(subscription => subscription.customer_id === item.id).length === 0 }}
```

This expression filters the subscription data and checks if the current customer ID matches any of the customer IDs. If there are no results, the expression returns `true` and disables the customer option.

export function ArcadeDisable() {
  return (
    
      
    
  )
}

## Next steps

You've now mapped query data to a component and configured a default table filter. Move on to the next section and learn how to temporarily store values.

---

## Retool apps tutorial: Configure multiple pages

import GoBack from '../_go-back.mdx';

The second part of this tutorial makes use of multiple pages to interact with both customer and subscription data. Once complete, the app will also display subscription data for a selected customer.

## 1. Add a subscriptions page

Retool apps group code, components, and frames into pages. Apps can contain a single page or you can build more complex apps using multiple pages.

The app currently has a single page to display and edit customer data. You can add another page to view a specific customer's subscription data.

Click  and select **Page** to create a new page with the following details:

- Name: `subscriptionsPage`
- Page title: `Subscriptions`
- Page URL: `subscriptions`

export function AddPage() {
  return (
    
      
    
  )
}

## 2. Add a header with navigation controls

Now that the app contains multiple pages, users need a way to navigate between them. 

Frames are distinct areas of the canvas in which you add components. The Header frame is a globally scoped frame that, when enabled, is visible across all pages.

Header frame contains a Navigation component by default. This component is preconfigured with navigation controls for pages and displays options for both **Customers** and **Subscriptions**.

export function ArcadeHeader() {
  return (
    
      
    
  )
}

## 3. Convert the customer query to global scope 

Apps use [global and per-page scopes](../../quickstart.mdx#global-and-page-scopes) for components, frames, and code.

- Globally scoped objects can be referenced in all pages. Retool continually evaluates globally scoped objects regardless of the page currently in view.
- Page-scoped objects can only be referenced within the same page. Retool only evaluates page-scoped objects when the page is currently being viewed.

At present, `getCustomers` is a page-scoped query in the customers page and isn't accessible from the subscriptions page. Before you can use this query on the subscriptions page, you must convert it to a globally scoped query so that it's available to all pages.

You can quickly access many parts of the IDE using the Command Palette in the toolbar. 

1. Click on the Command Palette or press ModK to open.
2. Enter `query1` to look up the query.
3. Click **Select in editor** to jump directly to it.

Click-and-drag the query from the **customersPage** section to the **Global** section. 

export function ArcadeScope() {
  return (
    
      
    
  )
}

## 4. Add a select field and table to the page

The Select component is a dropdown input field that can dynamically map data to generate a list of options from which to select. This component is used to select a customer for whom to view subscriptions.

1. Right-click on the canvas area to open the contextual menu.
1. Select **Add components**. This method enables you to quickly add multiple components.
1. Click the **Select** component to add it to the canvas.
1. While the contextual menu is open, click the **Table** component.
2. Update the **Label** of the Select component.

export function ArcadeComponents() {
  return (
    
      
    
  )
}

## Next steps

You've now configured your app to use multiple pages. Move on to the next section and learn how map data to components and use table filters.

---

## Retool app tutorial: Pass data between pages

import GoBack from '../_go-back.mdx';

Both the customer and subscription pages enable you to select a customer and either edit their details or look up a subscription. Since pages function independently, the customer you select in one is not reflected in the other.

When you build apps, you'll likely want to use values from one page in another. You can use a globally scoped variable to temporarily store values and make them available across all pages. In this case, the variable stores the selected customer ID. You then update both pages to reference this value so that both pages always use the same selected customer.

## 1. Create a variable

A [variable](../../../queries/guides/store-temporary-data.mdx) temporarily stores data for the current app session and resets whenever the user returns. Add a globally scoped variable to the app.

1. Navigate to the Code tab.
1. Click  within the **Global** pane and select **Variable**. 

Variables can be set or updated at any time using event handlers or JavaScript methods. You can also specify an initial value, if required.

export function ArcadeVariable() {
  return (
    
      
    
  )
}

## 2. Configure the Select component

You can set a default value for most input components, such as Select. You can reference the variable's value, `variable1.value`, so that the Select component uses the saved customer ID.

The Select component supports event handlers that run whenever its value changes. You can configure the event handler to update the variable with the selected customer ID.

1. Click on the Select component and set **Default value** to `{{ variable1.value }}`. 
1. Add an event handler and select the **Change** event.
1. Select the **Set variable** action and then set **State** to **variable1**.
1. Select the **Set value** method and set the value to `{{ self.value }}`.

export function ArcadeSelect() {
  return (
    
      
    
  )
}

## 3. Configure the customers table

You can configure a Table component with a **Default key** to automatically select a specific row. This key corresponds to the table's **Primary key**. As with the Select component, you can also configure an event handler to update the variable when changing the selected customer.

1. Switch to the **Customers** page.
1. Click on the Table component and set its **Default row** to **Key**.
1. Set the default key to `{{ variable1.value }}`.
1. Add an event handler and select the **Select row** event.
1. Select the **Set variable** action and then set **State** to **variable1**.
1. Select the **Set value** method and set the value to `{{ currentRow.id }}`.

:::tip 

You can use `currentRow` as shorthand when referencing table data.

:::

After configuring both components, the currently selected customer is stored in the variable. You can select a customer in one page and the change is automatically reflected in the other.

export function ArcadeTable() {
  return (
    
      
    
  )
}

## Next steps

You've now extended the functionality of your app. Move on and learn how to make use of other features in Retool apps.

---

## Retool apps tutorial

Retool enables you to build web apps using a drag-and-drop interface and component library. You can connect your apps to almost any database and API to Retool, allowing your users to interact with data.

This tutorial is split into two parts and uses sample data. You only need to complete the first part of this tutorial to build a fully working Retool app. 

## What you'll learn

import PartOne from "./tutorial/_part1.mdx";

import PartTwo from "./tutorial/_part2.mdx";

## Get started

:::info

Familiarity with common technologies like APIs and JavaScript is expected to complete this tutorial. If you need to learn more about these technologies, Retool recommends the following resources:

- [Freecodecamp: How to use REST APIs](https://www.freecodecamp.org/news/how-to-use-rest-api/)
- [Codecademy: JavaScript](https://www.codecademy.com/catalog/language/javascript)

:::

Every Retool user account is bound to an organization in which you create apps.

- If you already have a Retool account, sign in to your organization. 
  - Cloud-hosted organization users can sign in using [Retool's login page](https://login.retool.com/auth/login?source=docs?source=docs). 
  - Self-hosted organization users must sign in using the organization's login page URL (e.g., `https://retool.example.com/auth/login`).
- If you aren't yet using Retool, you can [sign up](https://login.retool.com/auth/signup?source=docs?source=docs) and create a cloud-hosted organization for free. The creator of an organization is automatically designated as its administrator.

Once you log in, navigate to the **Apps** section. then click **Create** > **App** and set the app name to **Retool app tutorial**.

---

## Resource authentication

Retool supports a number of authentication methods for authorizing connections to your data sources. As you create a new resource, you can select one of the methods available and provide the necessary credentials.

import ApiKey from '/docs/_partials/_integrations/_descriptions/_api-key.mdx';
import Auth0 from '/docs/_partials/_integrations/_descriptions/_auth0.mdx';
import Aws from '/docs/_partials/_integrations/_descriptions/_aws.mdx';
import AwsV4Region from '/docs/_partials/_integrations/_descriptions/_awsv4-region.mdx';
import AwsV4AccessKey from '/docs/_partials/_integrations/_descriptions/_awsv4-access-key.mdx';
import AwsV4SecretKey from '/docs/_partials/_integrations/_descriptions/_awsv4-secret-key.mdx';
import AwsV4Role from '/docs/_partials/_integrations/_descriptions/_awsv4-role.mdx';
import Basic from '/docs/_partials/_integrations/_descriptions/_basic.mdx';
import Bearer from '/docs/_partials/_integrations/_descriptions/_bearer.mdx';
import Digest from '/docs/_partials/_integrations/_descriptions/_digest.mdx';
import SessionBased from '/docs/_partials/_integrations/_descriptions/_session-based.mdx';
import GoogleServiceAccount from '/docs/_partials/_integrations/_descriptions/_google-service-account.mdx';
import Oauth1 from '/docs/_partials/_integrations/_descriptions/_oauth1.mdx';
import Oauth2 from '/docs/_partials/_integrations/_descriptions/_oauth2.mdx';
import Oauth2Custom from '/docs/_partials/_integrations/_descriptions/_oauth2-custom.mdx';
import Oauth2AuthUrl from '/docs/_partials/_integrations/_descriptions/_oauth2-auth-url.mdx';
import Oauth2AccessTokenUrl from '/docs/_partials/_integrations/_descriptions/_oauth2-access-token-url.mdx';
import Oauth2ClientId from '/docs/_partials/_integrations/_descriptions/_oauth2-client-id.mdx';
import Oauth2ClientSecret from '/docs/_partials/_integrations/_descriptions/_oauth2-client-secret.mdx';
import Oauth2SkipConsentScreen from '/docs/_partials/_integrations/_descriptions/_oauth2-skip-consent-screen.mdx';
import Oauth2Scopes from '/docs/_partials/_integrations/_descriptions/_oauth2-scopes.mdx';
import Oauth2Prompt from '/docs/_partials/_integrations/_descriptions/_oauth2-prompt.mdx';
import Oauth2Audience from '/docs/_partials/_integrations/_descriptions/_oauth2-audience.mdx';
import Oauth2EnableAuthEndpoint from '/docs/_partials/_integrations/_descriptions/_oauth2-enable-auth-endpoint.mdx';
import Oauth2TokenLifespan from '/docs/_partials/_integrations/_descriptions/_oauth2-token-lifespan.mdx';

## API keys and tokens

:::tip Additional credentials may be required

Certain integrations may require you to provide additional credentials with an API key or token, such as a user ID.

:::

```yaml title="Example credentials"
API Key: 1234567890abcdef1234567890abcdef
```

## Auth0

```yaml title="Example credentials"
Domain: your-app.auth0.com
Client ID: eKf3N2Bsdke3N5Bjf7JsK2dne
Client Secret: 8e2b1f5d3a4c7b9e1f6d5c3a7b8e9f4d
```

## AWS security credentials

```yaml title="Example credentials"
AWS Access Key ID: AKIAIOSFODNN7EXAMPLE
AWS Secret Access Key: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
Region: us-west-2
Role ARN: arn:aws:iam::123456789012:role/SampleRole
```

## Basic

```yaml title="Example credentials"
Username: sampleUser
Password: samplePass123
```

## Bearer

```yaml title="Example credentials"
Bearer Token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.e30.y5T-dGVzZKjT6xZc2Z-s_hN5e_BjWe_u6nZ1P3Alw
```

## Digest

```yaml title="Example credentials"
Username: sampleUser
Password: samplePass123
```

## Google service account

```json title="Example credentials"
{
  "type": "service_account",
  "project_id": "sample-project-id",
  "private_key_id": "abcd1234efgh5678ijkl9012mnop3456qrst7890",
  "private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQChK.......\n-----END PRIVATE KEY-----\n",
  "client_email": "sample-account@sample-project.iam.gserviceaccount.com",
  "client_id": "12345678901234567890",
  "auth_uri": "https://accounts.google.com/o/oauth2/auth",
  "token_uri": "https://oauth2.googleapis.com/token",
  "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
  "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/sample-account%40sample-project.iam.gserviceaccount.com"
}
```

## OAuth 1.0

```yaml title="Example credentials"
Consumer Key: xvz1evFS4wEEPTGEFPHBog
Consumer Secret: L8qq9PZyRg6ieKGEKhZolGC0vJZ3Q1z0a4B6A8Zw
Access Token: 370773112-tk5k8d2gOUH2KB8f75xzG3Vr9P9fNrA3nURFPb11
Token Secret: kYjzVBB8Y0mklWf8bFS1C9Ln4J4reF9nnJj6lfF5
```

## OAuth 2.0

Some integrations, such as Google Sheets, include an option to share user credentials across an organization, allowing all users to interact with the resource as the user. This is known as _shared user credentials_.

import Tabs from "@theme/Tabs";
import TabItem from "@theme/TabItem";

OAuth-based authentication with individual user credentials. Each user is prompted to authenticate with Google using an OAuth flow, and API calls from Retool are made on behalf of the logged-in user. When using apps built using authenticated Google resources, users can only interact with APIs and data to which they have access.

For example, cloud-hosted Retool organizations can grant Retool either **Read and write** or **Read only** access to your [Google Sheets](../guides/integrations/google/google-sheets.mdx) data. This option determines the scopes passed with the OAuth request. Retool recommends **Read and write** so that Retool can read and write spreadsheet data (e.g., create new sheets or update cell rows).

To create other Google API resources, or to use Retool's Google integrations with self-hosted deployments, you create Google Cloud projects and OAuth 2.0 credentials with scopes you define.

OAuth-based authentication with shared user credentials. The user creating the resource is prompted to authenticate with Google using an OAuth authentication flow, and subsequent API calls from Retool are made on behalf of the user that completed authentication. When building apps on top of Google Sheets, _all users_ in a Retool organization can access and edit sheets that have been shared with the user who completed the authentication process.

## OAuth 2.0 custom application

```yaml title="Example credentials"
Client ID: 1234567890-abcdefghijklmnopqrstuvwxyz.apps.googleusercontent.com
Client Secret: ABCDEFGHIJKLMNOPQRSTUVWXYZ123456
Auth URL: https://accounts.google.com/o/oauth2/auth
Token URL: https://oauth2.googleapis.com/token
Redirect URI: https://yourapp.com/oauth2/callback
Scopes: ["https://www.googleapis.com/auth/userinfo.email", "https://www.googleapis.com/auth/userinfo.profile"]
```

### Include Authorization header with OAuth requests

You can also choose to include the client ID and client secret as a Base64-encoded string in the `Authorization` header.

## Session-based

---

## Data protection

To guard against risk, you must properly handle any potentially sensitive data. This includes:

- **Personally Identifiable Information** (PII). Such as:
  - Social security, driver’s license, state identification card, or passport number.
  - Racial or ethnic origin, political opinions, religious or philosophical beliefs, or union membership.
  - The contents of mail, email, and text messages unless a business is the intended recipient of the communication.
  - Genetic or biometric data. Data concerning health or data concerning a natural person’s sex life or sexual orientation.
  - Precise location information (e.g., geolocation data).
  - Any personal information that is not public.
- **Financial information**. Such as:
  - Account login credentials, including financial account, and in combination with any required security or access code, password, or credentials that would grant access to an account.
  - Debit or credit card information, including full card details.
- **Protected health information** (PHI). Such as:
  - Health records.
  - Confidential doctor or patient data.

Sensitive information can be, and often is, subject to strict legal requirements for handling and storage. Although Retool does not have a legal obligation to enforce this, storing these kinds of information may not comply with your own legal or compliance obligations.

Please refer to Retool's [terms and policies](/legal) for detailed information.

---

## Retool AI providers and models

import Support from "../guides/integrations/ai/_support.mdx";

AI providers use _models_ that are trained on specific types of data to make relevant decisions and perform tasks. For example, you would interact with one type of model to generate chat responses and a different model to generate images. AI models are also known as LLMs (large language models).

In general, a model is a collective group of model instances, each of which vary in terms of functionality or data. For example, there are different instances of OpenAI's GPT-4 model, such as `GPT-4o` and `GPT-4o-mini`. You can specify the exact model instance when configuring Retool AI.

Retool [supports a number of AI providers](#available-ai-providers-and-models) and their models for use with AI [actions](../../queries/quickstart.mdx#ai-actions) and [agents](../../agents/quickstart.mdx).

## AI tokens

:::tip Estimate token usage
Use OpenAI's [tokenizer tool](https://platform.openai.com/tokenizer) to calculate how many tokens would result from a passage of text.
:::

AI models use _tokens_, rather than characters or bytes, to represent the commonality of sequences and usage. In general, a single token can equate to approximately four characters.

The text you provide in an AI query is converted by the model into tokens, after which it can generate the necessary response.

## Retool-managed and self-managed models

LLM interactions work by making API requests with an AI provider. As with most other APIs, these requests require authorization credentials, such as an API key. Retool provides access to certain AI models using Retool-managed keys.

A Retool-managed LLM connection enables you to start building and testing with AI models straight away without needing any additional tools or services. Once you're ready to use your AI actions or agents in production, you then need to provide your own authorization credentials. The connection then becomes self-managed.

For instance, you can immediately start building an agent using `GPT-4o-mini` with the Retool-managed OpenAI connection. Once your agent is ready for use, you then update the OpenAI connection and provide your own OpenAI API key.

:::info
Some AI models are only available for use as self-managed connections.
:::

## Available AI providers and models

Retool supports a number of [AI providers](models.mdx) and their models for use with [AI actions](../../queries/concepts/actions.mdx) and [agents](../../agents/index.mdx). Agents only support models that allow tool creation. Refer to the following information to learn which models are supported with AI actions or agents.

### OpenAI

import OpenAi from "../../_partials/_ai/_models/_openai.mdx";

### Anthropic

import Anthropic from "../../_partials/_ai/_models/_anthropic.mdx";

### Google Gemini

import Gemini from "../../_partials/_ai/_models/_gemini.mdx";

### Azure OpenAI

Rather than selecting from a predefined set of models, you create and deploy an Azure OpenAI service with a [specific model](https://learn.microsoft.com/en-us/azure/ai-services/openai/concepts/models). You then provide the endpoint and model name when configuring the API credentials. Refer to [Connect to Azure OpenAI](../guides/connect/azure-openai) for how to get started.

Retool charges a flat-rate hourly fee with the self-managed LLM key [model price](#retool-agents-pricing) for Azure OpenAI.

### Amazon Bedrock

Amazon Bedrock is a managed service that can be configured for different AI models, such as Anthropic or Cohere models. The models available for use with Retool AI depend on the models you configure for Bedrock.

### Baseten

import Baseten from "../../_partials/_ai/_models/_baseten.mdx";

### Custom AI provider

You can connect a [custom AI provider](../../data-sources/guides/integrations/ai/custom-ai-provider.mdx) to Retool that adheres to an OpenAI, Anthropic, Google, or Cohere schema.

## AI usage rate limiting

Retool imposes the following rate limits on Retool-managed key usage. Rate limits are per organization, per hour (over a rolling time window).

| Feature                          | Hourly rate limit  |
| -------------------------------- | ------------------ |
| Retool Agents                    | 20,000,000 tokens  |
| Assist                           | 100,000,000 tokens |
| All other AI functionality [^ai] | 250,000 tokens     |

[^ai]: Including, but not limited to: [AI actions](../../queries/concepts/actions.mdx), [Ask AI](../../queries/concepts/ask.mdx), [AI-generated READMEs](../../apps/guides/app-management/app-documentation.mdx), [Retool Vectors](../quickstarts/retool-vectors.mdx), and the [LLM Chat component](../../apps/guides/forms-inputs/chats/llm-chat.mdx).

This rate limit is intended to prevent abuse of resources and to limit Retool-managed key usage to non-production instances. Each AI provider also imposes their own rate limits, which also apply to Retool users.

If you encounter a rate limit imposed by Retool or by an AI provider, you will receive a `429` error with a message describing the kind of rate limit that was hit.

Rate limits are cumulative, and they encompass all AI usage. For example, if you are using [Vectors](../guides/vectors/index.mdx), you may reach this limit very quickly. These rate limits apply to any customer using Retool-provided keys, regardless of deployment method or plan type. Retool is unable to make exceptions to or raise this rate limit.

If you do not wish to be subject to Retool-imposed rate limits, or are ready to use AI in production, [configure AI platforms](../guides/integrations/index.mdx) to use your own API credentials. Self-managed connections are not subject to Retool-imposed rate limits.

## Model usage and pricing

Retool's pricing for AI model usage differs between Retool features.

### Assist pricing

Refer to the [pricing page](https://retool.com/pricing) for information about Assist usage and pricing.

### Retool Agents pricing

Usage for [Retool Agents](../../agents/index.mdx) is measured by _hour_, and the hourly rate differs for Retool-managed LLMs and self-managed LLMs. Refer to the [Agents billing and usage](/support/billing-usage/agents) support documentation for more information.

Retool provides access to [OpenAI](https://openai.com), [Anthropic](https://www.anthropic.com), and [Baseten](https://www.baseten.co) AI models for Agents with a Retool-managed key.

Retool uses an hourly rate for billable usage of managed LLM connections. The exact cost varies depending on provider costs, as shown in the table below.

You can provide your own authorization credentials for each AI provider and the models supported by Retool. Retool also supports the use of [custom AI providers](#custom-ai-provider), allowing you to make use of many more models.

Once you are ready to use Retool Agents in a production environment, you must provide your own credentials. This ensures that your agents aren't restricted by the Retool-managed key limits.

To simplify billing, Retool charges a flat-rate hourly fee when using a self-managed LLM connection, as shown in the **Self-managed LLM/key** entry of the table below.

import Partials from "/docs/_partials/_agents-pricing.mdx";

:::note
Refer to [Agent usage](/support/billing-usage/agents) for more information about [Billing and usage](/support/billing-usage) for Retool Agents.
:::

---

## Retool Database usage and limits

Cloud-hosted organizations can store the equivalent of 5GB of PostgreSQL data, with unlimited tables and rows. The amount of storage space your data uses is dependent on how many records, fields, and tables you use, and the size of values. If you exceed your storage limits, reach out to Retool to request a limit increase.

For cloud-hosted organizations on the Free plan, Retool automatically pauses the Retool Database service after one month of inactivity. Any existing connection strings will no longer work. Once you start using Retool Database again, updated connection strings are available.

:::info
Retool Database is designed to be a convenient way to store data to use within or outside of Retool. It is not designed to be a highly-available, scalable database service, and you should use a separate managed database provider if your use case requires these guarantees.
:::

## Data redundancy

Retool Database uses multiple levels of redundancy, which include daily backups of all data in a PostgreSQL cluster managed by our cloud provider. Retool retains each of these backups for 7 days. Retool recommends implementing a secondary backup process that stores and retains data for a longer period of time as an additional guardrail.

If you have any questions or would like to know more, [contact Retool Support](/support).

## Staging data

Any changes you make with data in Retool Database cannot be reverted. Retool recommends using [multiple environments](../../org-users/guides/configuration/environments.mdx) with staging data to avoid accidental loss of data.

---

## Data streaming

Retool supports streaming data from resources for some data sources using [pull mechanisms](https://en.wikipedia.org/wiki/Pull_technology). When pulling data from resources, the Retool UI incrementally updates as soon as a row or chunk of data arrives. Without streaming, the entire data payload must be downloaded before it renders. Depending on network latency, bandwidth, and the size of the data, streaming can improve performance and user experience.

## Stream with Retool AI actions

Streaming is enabled by default for the [generate text](../../queries/concepts/actions.mdx#generate-text), [summarize text](../../queries/concepts/actions.mdx#summarize-text), and [generate chat response](../../queries/concepts/actions.mdx#generate-chat-response) actions. The `.data` properties of these Retool AI queries are continuously updated.

## Stream PostgreSQL responses

To enable streaming in PostgreSQL responses after you gain access, check the **Stream response data and update app model incrementally** checkbox in the **Advanced** tab of the query editor.

When streaming is enabled, a query's `.data` property updates one or more times, each with the latest partial download of the query response while it downloads. Each update is always a set of complete rows of data, so `.data` always spans from rows `1-N`, where `N` is the latest row received.

Without streaming, the `.data` property updates to the result of the query once, after the full download is complete.

---

## Resources concept guides

import Concept from '/docs/_partials/_doctypes/_concept.mdx';

:::tip Get started

If you want to connect your data to Retool:

- Read the [quickstart](./quickstart) to learn about the fundamental concepts.
- Follow a [tutorial](./tutorial/) to connect your data sources or use Retool's built-in solutions.

:::

---

## Configure API authentication

Retool supports many types of API authentication. You can use static authentication tokens or create auth that asks the internal user to provide credentials each time they access the application.

If none of the following options work for your API, you can use [custom API authentication](custom.mdx).

## API authentication

You can add authentication details for an API by navigating to the **Authentication** section of the Resource settings page.

This lists all the authentication types Retool support natively, and **Custom Auth** to create custom or multi-step flows.

### OAuth 2.0

Retool also supports the OAuth 2.0 authentication scheme. In OAuth 2.0, authentication details are _not_ shared between your internal users unless you enable the **Share OAuth2.0 credentials between users** option.

When the share credentials option is disabled, each of your internal users will be required to authenticate via the OAuth authentication flow. The Access/Refresh token that is returned by the OAuth identity provider will be encrypted and then associated with the user's current session with Retool. This allows you to delegate authorization and authentication to the OAuth Identity provider. If needed, you can set the token's lifespan in the **Advanced** > **Access token lifespan** field.

Here is a sample configuration of Retool connecting with Google's OAuth 2.0 API. Things to take note of:

- We added the header: `Authorization: Bearer OAUTH2_TOKEN` - the `OAUTH2_TOKEN` is a magic placeholder string that gets replaced with the access token at runtime. You can use this magic string in the header or in the URL parameters of the query.
- The OAuth callback URL is static and cannot be directly changed. Self-hosted organizations can dynamically set the callback URL, however, as the URL's domain corresponds to the value for the [BASE_DOMAIN](../../../self-hosted/reference/environment-variables/index.mdx#property-BASE_DOMAIN) environment variable.

### Cookie-based APIs

Retool also supports APIs that use cookies for authentication. In this scenario, the API authorizes a session by responding with a `Set-Cookie` header that contains an authorization token. The API then expects all future authenticated requests to send that same authorization token in the `Cookies` header.

Though Retool proxies all HTTP requests through the backend, Retool supports forwarding the cookies set by the API to the user's browser. This includes attributes such as the expiration date. The cookies are then stored in a `HTTPOnly` cookie in the user's browser, which is tied to the lifecycle of the user's current session. All future requests the user makes to the API have the same cookie added to their request.

To configure this, tell Retool the name of the cookie that should be forwarded onto the user's browser. You can also specify a URL to check the user's authentication status.

After you configure the cookies, you must create a login page in Retool that asks the user for authentication details and makes an API request to the login endpoint. After a successful login, the authentication cookie is parsed from the response and forwarded along onto the user's session.

#### Double cookie submit pattern

You can implement the double cookie submit pattern by passing a header with the prefix `COOKIE_`.

#### Forwarding cookies that are pre-set on your domain

:::note

Forwarding pre-set cookies is only available on [Self-hosted Retool](../../../self-hosted/index.mdx).

:::

You can automatically forward specific cookies that are pre-set on the domain you are running Retool on. For example, say you have Retool hosted on `retool.yourdomain.com` and you want to automatically forward cookies named `someCookie1` and `someCookie2` that are set on the `yourdomain.com` domain.

First, you'll need to add these cookies to an allow-list with an environment variable. If you want to allow forwarding for multiple cookies, add them as a comma-separated string as below.

```
FORWARDABLE_SAME_DOMAIN_COOKIES_ALLOWLIST=someCookie1,someCookie2
```

Next, in the resource settings page for the resource that should forward these cookies, add the cookies in the `List of cookies to forward` section, as below.

`someCookie1` and `someCookie2` will now automatically be forwarded for this resource whenever you make a request. If you want to include one of the cookies in a header, you can follow the double cookie submit pattern (`COOKIE_someCookie1`) described [above](./api.mdx#double-cookie-submit-pattern).

### Pass OpenID SSO provider tokens to API requests

If you use SSO with OpenID, Retool allows you to use the JWT obtained from the SSO process in your API requests. You can use environment variables that were set up when integrating Retool with your OpenID SSO provider by following the guide to [Custom OIDC SSO](../../../sso/tutorials/custom/oidc.mdx#reference-jwt-claims-in-retool-apps).

### AWS v4 signature-based authentication

You can also sign your API Requests using Amazon's v4 Signature Signing Process. To do that, you need to specify your AWS Region, Service Account Key, and Secret Key.

The AWS Service is an optional field that will be extracted from the base URL if left empty. Normally, the service corresponds to the subdomain of your API. For example, if you are making a request to a service hosted at `https://xyzabc.execute-api.us-east-1.amazonaws.com`, then your service should be `xyzabc.execute-api`.

For AWS v4, you can pass the authentication information in the query params or the `Authorization` header. By default, the **Authenticate using the HTTP Authorization header instead of query parameters** option is checked, as many AWS services only support authentication using the header.

---

## Custom API authentication

While Retool supports many [authentication standards](api.mdx) out of the box, you can also configure custom authentication flows with multiple steps for virtually any kind of API authentication.

All API resource authentication methods apply to [REST](../integrations/api/rest.mdx), [GraphQL](../integrations/api/graphql.mdx), [OpenAPI](../integrations/api/openapi.mdx), and [gRPC](../integrations/development/grpc.mdx) resources.

:::warning Custom authentication in public apps

Like OAuth 2.0 and other SSO based auth options, custom auth workflows store variables related to the authenticating user's Retool account. Because public app viewers do not use a Retool account, custom auth is not supported in public apps.

:::

This guide walks through building an example authentication flow. The API in the example returns an authentication token that's used in future requests. Users authenticate individually, so credentials aren't shared.

## 1. Create a REST API resource

On the **Resources** page in Retool, create a **REST API** resource, name it, and then click **Create resource**.

Navigate back to the resource configuration page after creating the resource.

## 2. Select an authentication method

At the bottom of the form, select **Custom Auth** as the authentication method.

## 3. Add a step to collect a username and password

Click the **Add new step to the auth workflow** and then select **Form (modal)**. By default, the step is configured for an email and password.

To test the step, click **Save changes** and then **Test auth workflow**.

Enter any email and password, and then submit the form. A debugging interface is displayed that shows the scope of what can be used in subsequent steps.

## 4. Add a step to retrieve an authentication token

Create another step and select **API Request**. Configure the step to make an API request to your server to obtain an authentication token.

This example sends a request to the `https://httpbin.org/post` endpoint, which returns the same data passed in the request.

When you save and click **Test auth workflow**, you'll see the new scope which includes the response payload from the API request.

:::note Error messages for invalid logins

If a user submits an invalid email and password combination, you should alert them that something went wrong. By default, Retool notifies the user with a stringified version of the JSON returned by your endpoint if your endpoint returns an error code. If your endpoint does not return valid JSON, Retool displays the returned data in the notification. For example, your endpoint could return something like `res.status(401).send({ error: 'Invalid username or password' })`.

:::

## 5. Define variables to include in requests

You can write JavaScript to interact with previous inputs in your custom auth flow. For example, you can pass variables defined in previous steps (e.g., `HTTP_BODY_MESSAGE`), as well as objects from the scope (e.g., `http1.headers`)

Create another step and select **JavaScript**.

Click **Save changes** and then click **Test auth workflow**. You can see the updated scope, response payload, and the JavaScript query results in the log.

:::note Custom authentication in workflows

If you intend to perform custom authentication using a workflow, you can only use [built-in JavaScript libraries](https://docs.retool.com/workflows/guides/blocks/javascript#available-libraries).

:::

## 6. Save the authentication token

Create a new step and select **Define a variable**. The **Variable name** is a string used to refer to the authentication token, and the **Value of the variable** is an expression that interpolates to the authentication token.

In this example, the `PASSWORD` variable is defined as the password that's echoed back by the server.

## 7. Pass the authentication token

Pass the defined variable from step five as a header. Scroll back up and then configure the **Headers** section like this:

Make sure to save the final configuration.

## 8. Use the resource in an app

There are a few different ways to use a resource with custom auth in an app.

### Add a login button

Create a new test app, and then add an **Auth Login** component. In the **Custom auth Resource** field, select the API resource you created.

Click the button to test the auth flow. Retool makes the API request using the resource you configured, and then securely associates the authentication token with your current session. All subsequent API requests will contain the authentication header.

### Use a login verification URL

You can also automatically prompt users to log in to your API. Navigate to the resource configuration page, and scroll down to the **Use a login test URL** checkbox. Enable the option, then set this to an endpoint that returns a response code in the 200-299 range when the user is authenticated, and a non-2xx status code (e.g., 401 Unauthorized) when the user is not authenticated.

When a Retool app that's connected to this resource first loads, it prompts the user to authenticate if they haven't already.

:::note Run auth automatically

If you set an auth login URL or a login timeout, users are prompted with a modal with the option to authenticate to any resources that have custom authentication enabled. If you want authentication to be triggered automatically instead, select the **Run this custom auth workflow without prompting the user** toggle on the resource configuration page.

:::

### Refresh auth workflow

You can configure a set of custom authentication steps that run automatically after any non-200 response when querying a resource. This is useful for refreshing a user's authentication session without them needing to refresh the page.

For example, if a user has been idle for some period of time and then attempts to query your resource, and their session has expired, the resource returns a 401 or 403 error code. Instead of returning an error, Retool can perform a refresh auth workflow that you define. If the refresh auth workflow is successful, Retool attempts to run the failed query again. Because the user now has a valid authentication session, the second attempt to run the query will succeed and the user can continue using the app without interruption.

You can also trigger this refresh auth flow based on a timeout (in seconds). Navigate to the resource configuration page, and scroll down to the **Refresh Auth trigger** dropdown. Select **Time-based expiration** and set the timeout value. Users are then prompted to login if they have not logged in within the amount of time you specify. This value can be set to a constant number or it can be set dynamically using a variable from a **Define variable** step.

Additionally, you have the option to trigger this refresh auth flow every time a query is run. In order to do so, select **Every query run** from the dropdown.

---

## Authenticate with Google APIs using OAuth 2.0

In some cases, you may need to use OAuth 2.0 credentials for a Google Cloud project. These can include:

- Accessing Google Sheets from a self-hosted deployment.
- Creating a resource to use a Google API for which Retool does not have a built-in integration.

If you are already using [Okta SSO using OpenID Connect](../../../sso/tutorials/okta/oidc.mdx) (OIDC), you can safely authorize Google SSO without presenting an additional sign in method. Retool only displays one SSO button on the login page and Okta has priority over Google when both are enabled.

## Prerequisites

This guide assumes you have an existing Google Cloud project. If not, [create a new project](https://console.cloud.google.com/projectcreate) first.

## Create OAuth 2.0 credentials

Follow Google's instructions on [creating OAuth client ID credentials](https://developers.google.com/workspace/guides/create-credentials#oauth-client-id). Use the following information to configure it for use with Retool. 

| Setting | Value |
| --- | --- |
| **Application type** | **Web application** |
| **JavaScript origin URI** | The base URL you use to access Retool (e.g., `https://example.retool.com` or `https://retool.mycompany.com` | 
| **Authorized redirect URIs** | `BASE_URL/oauth/oauthcallback` and `BASE_URL/oauth/user/oauthcallback`. `BASE_URL` is `http://oauth.retool.com` for Retool Cloud, and on Self-hosted Retool it is your organization's domain. | 

Once complete, Google displays the client ID and secret, and also makes it available for download in JSON format. You use these credentials to create Google API resources in Retool. 

## Enable APIs and define scope

Before you can create a resource in Retool, you must enable the desired APIs and define the scope of access that the credentials will request from users. 

First, enable any APIs you wish to use with this project from the [API library](https://console.cloud.google.com/apis/library). These will be accessible using the OAuth credentials once you define their scope.

Next, follow Google's guide to [configure the OAuth consent screen](https://developers.google.com/workspace/guides/configure-oauth-consent?hl=en) and define the scopes with which the OAuth credentials will request for any enabled APIs. For example, the `auth/calendar/events` scope for the Google Calendar API would allow Retool to view and edit all calendar events once a user completes authorization.

## Create a REST API resource

You can now create [REST API resources](../integrations/api/rest.mdx) for Google APIs, such as the [Google Calendar API](https://developers.google.com/calendar/api/guides/overview), with the following settings.

:::note

Google requires the URL parameter `access_type=offline` to obtain refresh tokens, so you should include these in your Authorization URL variable. Set the **Prompt** value to **Consent** to include `prompt=consent` in the URL automatically.

:::

| Setting | Value |
| --- | --- |
| **Base URL** | The base URL of the API (e.g., `https://www.googleapis.com/calendar/v3`). |
| **Headers** | A key-value pair set to `Authorization` and `Bearer OAUTH2_TOKEN`. |
| **Authentication type** | **OAuth 2.0**. |
| **Authorization URL** | `https://accounts.google.com/o/oauth2/v2/auth?access_type=offline` |
| **Access token URL** | `https://oauth2.googleapis.com/token` |
| **Client ID** | The client ID provided by Google. |
| **Client secret** | The client secret provided by Google. |
| **Scopes** | A space-separated list of scopes (e.g., `https://www.googleapis.com/auth/calendar.events`). |
| **Prompt** | **Consent**. |

---

## Configure resource authentication



---

## Use Kerberos authentication with Microsoft SQL Server resources

Self-hosted organizations can update their Retool deployments to use Kerberos authentication with Microsoft SQL Server resources. This requires creating the necessary Kerberos configuration file and making changes to the deployment configuration.

## 1. Prepare the Kerberos configuration file

This configuration file contains the required information to implement Kerberos authentication. Create a file named `krb5.conf` and use the following template, replacing placeholders with values for your environment.

```shell
[libdefaults]
    default_realm =                  # e.g., http://EXAMPLE.COM 
    dns_lookup_realm = false                      # optional
    dns_lookup_kdc = false                        # optional
    ticket_lifetime = 24h                         # optional
    renew_lifetime = 7d                           # optional
    forwardable = true                            # optional

[realms]
     = {                            # e.g., http://EXAMPLE.COM 
        kdc =               # e.g., kdc.example.com
        admin_server =      # e.g., kdc.example.com
    }

[domain_realm]
     =                 # e.g., corp.local = http://EXAMPLE.COM 
    . =                # e.g., .corp.local = http://EXAMPLE.COM 

[logging]
    default = FILE:/var/log/krb5.log            # Log file for Kerberos
    kdc = FILE:/var/log/kdc.log                 # Log file for KDC operations
    admin_server = FILE:/var/log/kadmind.log    # Log file for admin server
```

## 2. Add the configuration to your deployment

Update the deployment's `Dockerfile` to reference the Kerberos configuration file. 

```shell
FROM tryretool/backend:X.Y.Z