Build modules to reuse queries and components
Learn how to reuse groups of components and queries across apps.
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.
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, set permissions to restrict access, and manage releases and versions.
Considerations
Retool recommends using modules when you want to create a simple collection of components that can be reused between many parent apps.
Keep in mind the following considerations when working with modules:
- Modules do not necessarily improve the performance of your app. Retool apps run as single-page applications so there is no difference when using a module.
- While it is possible to nest one module inside of another module, this is discouraged and can make debugging very difficult.
- Module outputs must be formatted as strings.
- The user permissions for modules must be the same as those for the parent app.
- Modules inherit any custom CSS from the parent app, so custom CSS is not supported for modules themselves.
Modules vs. multipage apps
Modules can provide a similar experience to multipage apps but are functionally different.
You could build a shared navigation module using the Navigation component. Add navigation options that link apps, and then add the module to those apps. You must update the component in the module whenever you need to link to another app. This is useful if you need to link to apps from different teams that cannot be combined into a multipage app.
Multipage apps, in contrast, contain multiple pages. Each page runs only when visible and you can import existing apps as pages. Multipage apps are a true multipage experience and best suited when you want to centralize a number of separate apps into a single one.
In general, Retool recommends using multipage apps rather than shared navigation with modules.
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.
Clone apps to modules
Cloning an app to a module copies the app's components and queries to a new module, leaving the app unchanged. To clone an app to a module, click on the App actions menu and select Clone to module.
Copy components to modules
To copy components to a module, select the components— + Click—to include and select Duplicate to module.
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 panel. 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.
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.
- Data inputs
- Query inputs
A data input is a property—a string, object, number, or boolean—that apps can pass into your module. 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, add a data 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 displays a preview of a selected candidate's portfolio by using an IFrame.
To create this module:
- Add an IFrame component to the canvas.
- Open the Module settings and add a data input.
- Select the IFrame, and set the URL to
{{ input1.value.website }}
.
To add this module to the parent app:
- Drag the module onto the canvas.
- 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 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:
- Create a query that finds the total number of applicants.
- 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
toquery1
. - Add a Statistic component to the canvas.
- Update the Statistic component settings to use
{{ query1.data.id }}
as the value.
To add this module to the parent app:
- Drag the module onto the parent app's canvas.
- 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.
- In your module, create a data input. You'll use this input to watch for changes from the parent app.
- 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:
select id from {{ input1.value }}
- 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.
- 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 toparentQuery.data
.
To test out this behavior, download and import the 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.
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 }}
.