Skip to main content

Runnable Editor

The strength of Windmill's app editor is the ability to connect everything together:

On the bottom of the app editor, you can find the runnable editor. The runnable editor is a code editor that allows to create, edit, or manage the scripts or flows linked to components (runnables) and background runnables.

App Runnables panel

The panel is structured as follows:

  • Runnables list: a list of all the runnables. Click on a runnable to select it.
  • Runnable editor: the editor of the selected runnable.

When a runnable is selected, the runnable editor is displayed. It is composed of:

  • Header: The header of the runnable editor. It contains:

    • Editable name.
    • The status of the runnable can either be valid, invalid depending on the LSP response.
    • Cache button.
    • Windmill AI button.
    • A delete button.
    • Expand to Script Editor button.
    • A format button: Formats the code of the runnable. It's also triggered when you save the runnable.
    • A run button: Runs the runnable.
  • Runnable editor: The code editor of the runnable.

Types of runnables

There are 5 types of runnables:

  • Inline scripts: Scripts that are defined on the app editor, linked to a component.
  • Workspace/Hub: Runnables (scripts, flows) linked to a component, but defined in the workspace or the Hub.
  • Background runnables: Runnables that are not linked to a component, but run in the background.
  • Frontend scripts: Scripts that can be used to manipulate the client app state. They can either be linked to a component or be a background runnable. If they are a background runnable, they are not executed unless manually set to run when the app starts or reloads.
  • Evals: Evals are used to connect data sources to other components or parameters. They are only evaluated on changes to the outputs that have been identified.

They provide interactivity to your app.

Types of runnables

Inline scripts

Inline scripts are scripts that are defined on the app editor. They can be either attached to a component or detached.

Frontend scripts

Frontend scripts are JavaScripts that are executed on the browser.

They come with frontend script helpers that are functions and global objects to help you interact with the app.

Frontend scripts can read outputs and ctx of the rest of the app with for example:

// read outputs and ctx
console.log(ctx.email);

// access a global state store
if (!state.foo) {
state.foo = 0;
}
state.foo += 1;

// for reactivity to work, you need to assign a value and not modify it in place
// e.g: state.foo.push(1) will not work but 'state.foo = [...state.foo, 1]' will.
// you may also just reassign as next statement 'state.foo = state.foo'

// you can also navigate (goto), recompute a script (recompute), or set a tab (setTab)
// Inputs and display components support settings their value directly
setValue('a', 'Bar');

// Tables support setting their selected index (setSelectedIndex)

return state.foo;

Background runnables

Background runnables are scripts that are executed in the background. They are executed on the server. They can be triggered on app refresh or when their input changes. They are not attached to any component but their result can be shared among many components.

See Background runnables configurations at Runnable Execution.

Evals

Evals are used to connect data sources to other components or parameters.


Windmill parses your eval and frontend scripts using the swc parser compiled to wasm to extract any references to outputs. It allows Windmill to suggest dependencies for frontend scripts.


Re-evaluated on changes to

Evals are by default evaluated on changes to the outputs that have been identified. The auto-evaluation can be toggled off.

This option is available only when "Recompute on any input changes" is enabled.

Re-evaluated on changes to

This background script's input message will be re-evaluated on any change to the value of component b (a Text Input).

Transform to a frontend script

Clicking on the dedicated link, evals can be "transformed to a frontend script".

App Runnables panel

Creating a Runnable

There are several ways to create runnables:

  • Create an inline script from a component.
  • Select a script or a flow from the list of detached inline scripts, workspace scripts and flows, or Hub scripts.
  • Create a background runnable.

When you create an inline script attached to a component or a background script, the first thing you need to do is to choose the language of the script.

Types of runnables

The outputs of your script can be used by other scripts or components:


A component can have runnables attached to it. Depending on the component type, the runnables are executed at different times:

  • Data source: A runnable or another component's result is used as the runnable's data source. It executes on app start and app refresh. For instance, if the component is a table, the runnable's result is used as the table's data source.
  • Event handler: The runnable executes when the event is triggered. For instance, if the component is a button, the runnable executes when the button is clicked.
  • Validations: Only the Stepper component has validations. The runnable executes when the step of the stepper changes.

Runnable Configuration

The runnable configuration consists of:

App Runnables panel

Transformer

A transformer is an optional frontend script executed immediately after the component's script. It's used to perform lightweight transformations in the browser. It accepts the previous computation's result as result.

For instance, if component has a script that returns:

{
data: {
name: 'John',
age: 20
}
}

A transformer could be used to extract the data object:

return result.data;

Here is an example of a transformer used with a background script to automatically download a file upon input change (here the selected row of a table).


Configure Triggers

Run on Start and App Refresh

Two types of runnables can be configured to run on app start and app refresh:

You may want to disable this so that the background runnable is only triggered by changes to other values or triggered by another computation on a button.

Run on Start and App Refresh

Recompute on Any Input Change

Runnables can be configured to recompute whenever an input changes. This is useful for recomputing a component's runnable when an input changes.

Two types of runnables can be configured to recompute on any input change:

Inputs of runnables that are either connected to an output or evaluated can trigger a recompute. These are displayed in the Change on value section.

When "Recompute on Any Input Change" is enabled, it can be disabled at the input level, toggling off "Re-evaluated on changes to".

Recompute on Any Input Change

Trigger runnables on success

Button & Form components and background scripts can trigger other components to recompute. For example, a button can trigger a table to recompute. When the button is clicked, the table is reloaded.

Trigger runnables on success

Static resource select only / Resources from users allowed

Apps are executed on behalf of publishers and by default cannot access viewer's resources.

If the resource passed here as a reference does not come from a static Resource Select component (which will be whitelisted by the auto-generated policy), you need to toggle "Resources from users allowed".

The toggle "Static resource select only / Resources from users allowed" can be found for each runnable input when the source is an eval.

Static resource select only / Resources from users allowed

Manual Dependencies

Frontend scripts don't have any inputs. However, you can manually specify a frontend script's dependencies. This is useful when you want to recompute a frontend script when an input changes.

Manual dependencies are added clicking on Add dependency button and can be removed by clicking on the x button.

Manual Dependencies

Cache App Inline Scripts

Caching an app inline script means caching the results of that script for a certain duration. If the script is triggered with the same inputs during the given duration, it will return the cached result.


You can enable caching for an app inline script directly its editor settings. Here's how you can do it:

  1. Settings: From the Code Editor, go to the top bar and pick the Cache tab.
  2. Enable Caching: To enable caching, toggle on "Cache the results for each possible inputs" and specify the desired duration for caching results (in seconds.)

In the above example, the result of step the script will be cached for 5 minutes. If Inline Script 0 is re-triggered with the same input within this period, Windmill will immediately return the cached result.

Frontend scripts helpers

We expose a few functions and global objects to help you interact with the app from a frontend script.

ctx

You can access the context object with the ctx global variable.

console.log(ctx.email);

Context objects can be seen on the Output menu.

state

The app state is a client-side store that can be used to store data.

You can access the state object with the state global variable.

console.log(state);

You can update the state directly by manipulating the state object.

state.foo = 'bar';

goto

Use the goto function to navigate to a specific URL.

Syntax:

goto(path: string, newTab?: boolean)

Parameters:

path The URL to navigate to.

newTab (optional) Whether to open the URL in a new tab or not.

Example:

goto('/apps/1');
goto('https://www.windmill.dev/', true);

setTab

Use the setTab function to manually set the tab of a Tab component.

This works for all components that have multiple tabs (Tabs, Conditional tabs, Sidebar Tabs, Invisible Tabs, Stepper, Decision Tree).

Syntax:

setTab(id: string, index: string)

Parameters:

id string: The id of the component.

index string: The index of the tab to set.

Example:

setTab('a', 1);

Where 'a' is the id of the component and 1 is the index of the tab to set.


open

Use the open function to open a modal or drawer.

Syntax:

open(id: string)

Parameters:

id string The id of the modal or drawer component to open.

Example:

open('a');

close

Use the close function to close a modal.

Syntax:

close(id: string)

Parameters:

id string The id of the modal or drawer component to close.

Example:

close('a');

recompute

Use the recompute function to recompute a component.

Syntax:

recompute(id: string)

Parameters:

id string The id of the component to recompute.

Example:

recompute('a');

getAgGrid

Use the getAgGrid function to get the ag-grid instance of a table.

Syntax:

getAgGrid(id: string)

setSelectedIndex

Use the setSelectedIndex function to select a row in a table or an AG Grid table.

Syntax:

setSelectedIndex(id: string, index: number)

setValue

The setValue function is meant to manually set or force the value of a component. This can be convenient in cases where connecting components is not the easiest pattern.

setValue(id: string, value: any)

Note that it's a bad idea to mix dynamic default value and setValue together.

validate

Make a specific field of a form in a Validate state.

validate(id: string, key: string)

validateAll

Make all fields of a form in a Validate state.

validateAll(id: string, key: string)

invalidate

Invalidate a specific field of a form.

invalidate(id: string, key: string, error: string)

Parameters:

id string

recompute

Recompute a component.

recompute(id: string)

Parameters:

id string

clearFiles

Clear the files of a file input.

clearFiles(id: string)

Parameters:

id string

showToast

Sends a toast notification.

showToast(message: string, error: boolean)

Parameters:

message string error boolean

Policy

A viewer of the app will execute the runnables of the app on behalf of the publisher avoiding the risk that a resource or script would not be available to the viewer. To guarantee tight security, a policy is computed at time of saving of the app which only allow the scripts/flows referred to in the app to be called on behalf of. Furthermore, static parameters are not overridable. Hence, users will only be able to use the app as intended by the publisher without risk for leaking resources not used in the app.

To understand how to handle resources in apps, see Static resource select only / Resources from users allowed.