Skip to main content
RapidDev - Software Development Agency
retool-tutorial

How to Set Default Values in Retool Forms

Set default values in Retool forms by entering a {{ }} expression in the Default Value field of any input component in the Inspector. For static values, type the value directly. For dynamic defaults from a query or selected row, reference {{ query1.data[0].field }} or {{ table1.selectedRow.data.field }}. To pre-fill an entire form at once, call form1.setData() from a JS Query.

What you'll learn

  • Set static and dynamic Default Value expressions using {{ }} syntax on any input component
  • Pre-fill an entire form from a selected table row using form1.setData(table1.selectedRow.data)
  • Load defaults from URL parameters with {{ url.searchParams.myParam }}
  • Understand the difference between Default Value (initial render) and setValue() (runtime updates)
  • Reset a form to its defaults programmatically using form1.reset()
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Beginner8 min read15-20 minRetool Cloud and Self-hostedLast updated March 2026RapidDev Engineering Team
TL;DR

Set default values in Retool forms by entering a {{ }} expression in the Default Value field of any input component in the Inspector. For static values, type the value directly. For dynamic defaults from a query or selected row, reference {{ query1.data[0].field }} or {{ table1.selectedRow.data.field }}. To pre-fill an entire form at once, call form1.setData() from a JS Query.

Quick facts about this guide
FactValue
ToolRetool
DifficultyBeginner
Time required15-20 min
CompatibilityRetool Cloud and Self-hosted
Last updatedMarch 2026

Pre-filling Forms with Static and Dynamic Defaults in Retool

Default values save users time and reduce errors by pre-populating form fields with sensible starting values. In Retool, every input component — Text Input, Select, Number Input, Date Picker, and more — has a Default Value property in the Inspector that accepts both static strings and {{ }} expressions.

This tutorial covers four common patterns: hard-coded static defaults, defaults from query results, defaults from a table row the user clicked, and defaults from URL parameters. You will also learn form1.setData() for bulk pre-filling, and form1.reset() for returning a form to its default state after submission.

Note the key distinction: the Default Value property only runs at component mount time. If you need to update a field value after the page has loaded and the user has interacted, you must use setValue() from a JS Query — and remember that setValue() is asynchronous.

Prerequisites

  • A Retool app with at least one Form component containing input fields
  • Familiarity with the Inspector panel and {{ }} expression syntax
  • At least one Resource Query that returns data (for dynamic default examples)
  • Optional: a Table component for the selected-row pre-fill pattern

Step-by-step guide

1

Set a static Default Value on a Text Input

Select a Text Input component inside your Form. In the Inspector's General section, find the Default Value field. Type a static string directly, for example 'New York'. This value will be pre-filled whenever the form first renders or is reset. For numeric inputs, enter the number. For Select components, enter the option's value string (not the label).

Expected result: The Text Input renders pre-filled with 'New York' when the form loads.

2

Set a dynamic default from query data

Create a Resource Query named getDefaults that fetches the default configuration record from your database. Enable 'Run on page load' in the query's Settings tab. Then select the input component and set its Default Value to {{ getDefaults.data[0].fieldName }}. Retool evaluates this expression once at component mount, after the query finishes. For the query to be available at mount time, it must have Run on page load enabled or be triggered before the component renders.

typescript
1-- SQL query: getDefaults
2SELECT default_country, default_currency, default_tax_rate
3FROM app_config
4LIMIT 1;
5
6// Default Value expression on currencySelect:
7{{ getDefaults.data[0].default_currency }}
8
9// Default Value expression on taxRateInput:
10{{ getDefaults.data[0].default_tax_rate }}

Expected result: Currency and tax rate fields pre-fill with values from the app_config table on load.

3

Pre-fill a form from a selected table row

When users click a row in a Table component (table1) to edit a record, you want the edit form to pre-fill with that row's data. Select each input component and set its Default Value to the corresponding field on table1.selectedRow.data: {{ table1.selectedRow.data.first_name }} for firstNameInput, {{ table1.selectedRow.data.email }} for emailInput, and so on. Default Value re-evaluates when table1.selectedRow changes because Retool reactively re-mounts form inputs when their default changes.

typescript
1// Default Value expressions for edit form fields
2// firstNameInput:
3{{ table1.selectedRow.data.first_name }}
4
5// emailInput:
6{{ table1.selectedRow.data.email }}
7
8// roleSelect (must match an option value exactly):
9{{ table1.selectedRow.data.role }}
10
11// createdAtDatePicker:
12{{ table1.selectedRow.data.created_at }}

Expected result: Clicking a table row causes the edit form to fill with that row's values.

4

Read URL parameters as form defaults

Retool exposes URL search parameters via {{ url.searchParams.paramName }}. This is useful for deep-linking into a form with pre-filled context. For example, if your app URL is /app/orders?customerId=42, set the Default Value of customerIdInput to {{ url.searchParams.customerId }}. You can also use this pattern to pre-select a dropdown: set a Select's Default Value to {{ url.searchParams.type }} and it will pre-select the option whose value matches the URL param.

typescript
1// Default Value for customerIdInput (from URL ?customerId=42)
2{{ url.searchParams.customerId }}
3
4// Default Value for typeSelect (from URL ?type=enterprise)
5{{ url.searchParams.type }}
6
7// Provide a fallback if the param might be absent:
8{{ url.searchParams.customerId || '' }}

Expected result: Opening the app with ?customerId=42 in the URL pre-fills the customer ID field with 42.

5

Bulk pre-fill the form using form1.setData()

For situations where Default Value expressions are insufficient — for example, when a button click should load a specific record — use form1.setData() in a JS Query. setData() takes an object whose keys match the component names inside the form. Create a JS Query named loadRecord. In the query, call await getRecord.trigger() to fetch the data, then call form1.setData() with the result. Note: setData() is synchronous but triggers re-render — if you need to read the updated value immediately after, add a small await utils.waitForNextTick() or read directly from the data source instead.

typescript
1// JS Query: loadRecord
2// Triggered by a 'Load for editing' button click
3
4await getRecord.trigger();
5
6const record = getRecord.data[0];
7
8form1.setData({
9 firstNameInput: record.first_name,
10 lastNameInput: record.last_name,
11 emailInput: record.email,
12 roleSelect: record.role,
13 activeSwitchInput: record.is_active,
14});

Expected result: Clicking the Load button populates all form fields with the fetched record's values.

6

Reset the form to defaults after submission

After a successful form submission, call form1.reset() to clear all inputs back to their Default Value expressions (not to empty). This is cleaner than clearing each field individually. Add form1.reset() to the success handler of your submitQuery. If you want to fully empty the form (ignoring defaults), set each Default Value to empty first or call setValue('') on each component.

typescript
1// JS Query: submitRecord
2// Success event handler calls form1.reset()
3
4try {
5 await insertRecord.trigger({
6 additionalScope: { data: form1.data }
7 });
8 utils.showNotification({
9 title: 'Saved',
10 description: 'Record created successfully.',
11 notificationType: 'success',
12 });
13 // Reset to defaults (not empty) for the next entry
14 form1.reset();
15} catch (err) {
16 utils.showNotification({
17 title: 'Error',
18 description: err.message,
19 notificationType: 'error',
20 });
21 throw err;
22}

Expected result: After successful submission, all form fields return to their configured Default Values, ready for the next entry.

Complete working example

JS Query: loadRecord
1// loadRecord — fetches a record by ID and pre-fills the edit form
2// Trigger: 'Edit' button click with table1.selectedRow.data.id in scope
3
4const recordId = table1.selectedRow.data.id;
5
6if (!recordId) {
7 utils.showNotification({
8 title: 'No record selected',
9 description: 'Please select a row in the table first.',
10 notificationType: 'warning',
11 });
12 return;
13}
14
15// Trigger the fetch query with the record ID
16await getRecord.trigger({
17 additionalScope: { recordId }
18});
19
20const record = getRecord.data[0];
21
22if (!record) {
23 utils.showNotification({
24 title: 'Record not found',
25 description: `No record found for ID ${recordId}`,
26 notificationType: 'error',
27 });
28 return;
29}
30
31// Bulk pre-fill the edit form
32form1.setData({
33 firstNameInput: record.first_name,
34 lastNameInput: record.last_name,
35 emailInput: record.email,
36 roleSelect: record.role,
37 departmentSelect: record.department_id,
38 activeSwitchInput: record.is_active,
39 notesInput: record.notes || '',
40});
41
42// Open the edit modal if using one
43editModal.open();

Common mistakes

Why it's a problem: Setting Default Value to a query field reference but forgetting to enable 'Run on page load' on that query — the field renders empty because the query has no data yet

How to avoid: In the query's Settings tab, enable 'Run on page load'. For queries that require parameters not available at load time, use '' as the default and call setValue() from the appropriate event handler instead.

Why it's a problem: Using setValue() and trying to read the new value on the very next line — setValue() is asynchronous and the value may not have updated yet

How to avoid: Await setValue() before reading: await textInput1.setValue('new value'); then read textInput1.value. Or read from the data source directly instead of from the component.

Why it's a problem: Passing component labels (not names) as keys in form1.setData() — for example, using 'First Name' when the component is named firstNameInput

How to avoid: Use the component's Name property (visible at the top of the Inspector) as the key in setData(), not the Label. Names are camelCase identifiers like firstNameInput.

Why it's a problem: Calling form1.reset() expecting it to clear all fields to empty — it restores Default Value expressions, not empty strings

How to avoid: If you need to fully clear the form, either set all Default Values to '' or call setValue('') on each component individually before calling reset().

Best practices

  • Use Default Value expressions for form fields that should reflect query data at load time — they update reactively when the referenced query re-runs
  • Prefer form1.setData() over individual setValue() calls when pre-filling multiple fields from a single source — one call, one re-render
  • Always provide a fallback in Default Value expressions using || to handle null/undefined query results gracefully: {{ query1.data[0]?.fieldName || '' }}
  • Remember that Default Value only applies at component mount — for post-interaction updates, you must use setValue() or setData()
  • When using URL params as defaults, document which params your app accepts in the app description so teammates know the deep-link format
  • After submission, use form1.reset() to restore defaults rather than navigating away and back — it is faster and preserves the user's context
  • For Select components, the Default Value must exactly match an option's value (not its label) or the dropdown will render with nothing selected

Still stuck?

Copy one of these prompts to get a personalized, step-by-step explanation.

ChatGPT Prompt

I have a Retool edit form with the following components inside a Form component named form1: firstNameInput (Text Input), emailInput (Text Input), roleSelect (Select), and activeSwitchInput (Switch). When a user clicks a row in table1, I want the form to pre-fill with that row's data. Show me two approaches: (1) using Default Value expressions on each component referencing table1.selectedRow.data, and (2) a JS Query named loadRecord that uses form1.setData() to bulk-fill the form. Also show me how to call form1.reset() after successful submission.

Retool Prompt

In my Retool app, I have a form1 Form component with a roleSelect inside it. The roleSelect has options from a query. How do I set the Default Value of roleSelect to match the role field from table1.selectedRow.data? What expression do I write in the Default Value field?

Frequently asked questions

Does the Default Value update if the referenced query re-runs after the form has loaded?

Yes, in most cases. Retool reactively re-evaluates Default Value when its dependencies change, which effectively re-mounts the input with the new value. However, if the user has already typed in the field, the typed value takes precedence. Use setValue() if you need to force an override on a field the user has touched.

Can I use form1.setData() to set a Date Picker's value?

Yes. Pass the date as an ISO 8601 string or a JavaScript Date object. For example: form1.setData({ myDatePicker: '2026-04-15' }). The Date Picker component parses ISO strings and millisecond timestamps automatically.

What is the difference between Default Value and Placeholder in a Text Input?

Default Value pre-fills the field with an actual value that is included in form1.data. Placeholder is grey hint text visible only when the field is empty — it is not submitted as a value and disappears the moment the user starts typing.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Learning is great. Shipping is faster with help.

Our engineers have built 600+ apps on Retool and the tools around it. If your project needs to be live sooner than your learning curve allows — book a free consultation.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.