For the complete documentation index, see llms.txt. This page is also available as Markdown.

Expressions

Learn how Elsa Studio expression editors map to Elsa Server expression engines, how to access workflow variables, and which syntax choices are actually available in Elsa 3.8.

Elsa Studio lets you enter activity values in different expression syntaxes. The exact choices you see are not hardcoded in Studio alone: Studio asks Elsa Server for the available expression descriptors and renders those options in the property editor.

This matters for two reasons:

  • The list of available expression types depends on which expression features your server enables.

  • Some syntaxes shown in Studio are UI helpers, while others are real runtime expression engines.

How expression selection works

For most activity inputs, Studio shows a Default option plus one or more code-oriented expression types.

  • Default means: use the activity's normal UI editor for that field.

  • Literal and Object are built-in Studio/UI syntaxes used behind the scenes for plain values and structured values.

  • JavaScript, Liquid, C#, Python, and other custom types are provided by the server.

In the 3.8 sample server, the app enables these expression engines:

.UseCSharp(...)
.UseJavaScript(...)
.UsePython(...)
.UseLiquid(...)

If your server does not register one of these features, Studio will not offer that syntax.

In Elsa 3.8, C# and Python are only browsable in Studio when host code execution is allowed for those engines. If you do not see them in the picker, check your server configuration first.

Expression matrix

The following matrix separates what Studio shows from what the backend actually evaluates.

Studio choice
Source
Authoring style
Notes

Default

Studio UI mode

Field-specific editor

Not a separate runtime expression engine.

Variable

Server descriptor

Picker UI

Resolves to a selected workflow variable. Different from variables in JavaScript or Variable in C#.

Input

Server descriptor

Picker UI

Resolves to a selected workflow input. Different from getInput(...) or C# Input.

JavaScript

UseJavaScript(...)

Monaco code editor

Best general-purpose dynamic option in Studio.

Liquid

UseLiquid(...)

Monaco code editor

Best for text templates.

C#

UseCSharp(...)

Monaco code editor

Browsable only when host code execution is allowed.

Python

UsePython(...)

Monaco code editor

Browsable only when host code execution is allowed.

Custom types such as Secret

Feature-specific descriptor

Usually picker or custom UI

Only appears when the related backend feature is installed.

Studio also has hidden internal descriptors such as Literal and Object that support plain values and structured values behind the scenes.

Which syntax should you use?

Use the simplest option that matches the job:

  • Default for plain text, numbers, booleans, dropdowns, and other normal field editors.

  • Variable when you want the field to resolve directly from a selected workflow variable without writing code.

  • Input when you want the field to resolve from a selected workflow input without writing code.

  • JavaScript for most dynamic value composition and workflow-variable lookups.

  • Liquid for templated text output.

  • C# when you explicitly want Roslyn-based expressions and have enabled them for trusted authors.

  • Python only when your server is configured for Python.NET expressions and you need it specifically.

Picker syntaxes versus code-expression objects

This is the most common point of confusion in Studio:

  • Variable and Input in the syntax picker are expression types backed by picker UI.

  • variables in JavaScript is a runtime object injected into the JavaScript engine.

  • Variable and Input in C# are runtime proxies injected into the C# evaluator.

So these are related, but they are not the same thing.

Variable picker

Use Variable when a property should simply read from one workflow variable and you do not need any extra logic.

Example: choose the CustomerName variable from the picker for a text field.

Input picker

Use Input when a property should read directly from one declared workflow input.

Example: choose the OrderId workflow input from the picker for a downstream activity field.

JavaScript expressions

JavaScript is usually the most convenient dynamic syntax in Studio.

Reading variables

If variable wrappers are enabled and your variable names are valid JavaScript property names, you can use:

Elsa also exposes explicit helper functions:

Use getVariable when:

  • the variable name is not a valid JavaScript identifier.

  • variable wrappers were disabled on the server.

  • you want to avoid depending on the variables.SomeName convenience wrapper.

Writing variables

To assign a value from JavaScript, use:

Accessing inputs and outputs

Elsa's JavaScript helpers also include:

Practical JavaScript examples

Important wrapper limitation

The variables.SomeName style depends on two JavaScript runtime settings:

  • variable wrappers must be enabled.

  • variable copying must be enabled.

If either is disabled, use getVariable(...) and setVariable(...) instead.

C# expressions

C# expressions are available when the server enables the C# expression feature and allows host code execution for trusted workflow authors.

Elsa generates a variable proxy for C# expressions. In Elsa 3.8, both Variables and Variable work, but Variable is the preferred alias.

Reading variables

Writing variables

Accessing inputs and outputs

Accessing workflow metadata

You can also assign metadata in C# expressions when the scenario supports it:

Practical C# examples

When to prefer Get<T>()

Use Get<T>() when:

  • the variable name is not a valid generated property name.

  • you want explicit typing.

  • wrappers were disabled in C# options.

Liquid expressions

Liquid is best for text templating.

Elsa 3.8 registers workflow variables through the Variables object and workflow or activity inputs through Input.

Liquid also exposes workflow metadata:

Practical Liquid examples

Use Liquid when the result should primarily be formatted text, not when you need more procedural logic.

Python expressions

Python expressions are available only when the Python feature is enabled and host code execution is allowed.

Elsa injects these globals into the Python scope:

  • execution_context

  • input

  • output

  • outcome

  • variables

Typical variable access looks like this:

Accessing inputs and outputs

Practical Python examples

Python expressions are executed through Python.NET in Elsa 3.8. Keep them for intentionally enabled, trusted-author scenarios rather than as the default authoring path in Studio.

Feature-specific custom expression types

Studio can also surface feature-specific expression types beyond the core set above.

For example, Elsa's secrets feature contributes a Secret expression descriptor with its own custom UI instead of a code editor. The general rule is:

  • if a backend feature registers an expression descriptor and marks it browsable, Studio can show it.

  • if the descriptor provides a custom UIHint, Studio renders that custom picker/editor instead of Monaco.

That is why two Elsa deployments can show different syntax choices for the same activity field.

Variable naming guidance

If you want the most convenient cross-language experience in Studio, use variable names that are valid identifiers, such as:

  • OrderId

  • CustomerName

  • RetryCount

Avoid names that require quoting or special handling, such as:

  • order-id

  • customer name

  • 123value

Those names can still work, but you will need accessor methods like getVariable("order-id") instead of wrapper-style property access.

Variable storage and expressions

Expressions read the current value of workflow variables regardless of whether the variable is stored in memory or in workflow-instance storage.

In Elsa 3.8:

  • Memory storage keeps the value in memory for the lifetime of the current execution context.

  • Workflow Instance storage persists the value in workflow state.

  • The legacy Workflow storage driver still exists for backward compatibility, but Workflow Instance is the current persisted option.

Choose storage based on lifecycle and suspension/resumption needs; choose expression syntax based on authoring convenience.

  • Use Default unless you actually need dynamic behavior.

  • Use JavaScript for most dynamic field values in Studio.

  • Use Liquid for generated messages, templates, and text bodies.

  • Use C# and Python only for trusted-author scenarios where you intentionally enable host code execution.

  • Prefer identifier-friendly variable names so wrapper syntax stays simple.

Common pitfalls

"I don't see C# or Python in Studio"

Usually this means the backend did not enable that engine, or host code execution for that engine is disabled.

"variables.MyValue does not work in JavaScript"

Check these first:

  • the variable name is a valid identifier.

  • JavaScript variable wrappers were not disabled.

  • JavaScript variable copying was not disabled.

If any of those are not true, switch to:

"My field shows Default instead of a code editor"

That is normal. Default uses the activity's UI hint. Switch the syntax picker to a code-backed expression type such as JavaScript or Liquid if you want to write an expression.

See also

Last updated