Bubble Docs
Ask or search…
K

Dynamic expressions (beta)

This section covers how to set up dynamic expressions in Bubble
The expression composer is currently being updated and is available as a pre-release beta. This article covers the beta release; to read about the current stable release, please see the article below:
In this article we'll be diving into the world of dynamic expressions.
Dynamic expressions are like "live" formulas that update in real-time based on user input, database updates and other changes in your app.
Dynamic expression let you work with data dynamically and in many different ways. In the example above we are searching for users in the database and counting them. The result would be a live reflection of the count of users that updates automatically every time a user is added or removed.
Dynamic expressions are one of Bubble's basic building blocks and are used in a wide range of scenarios.
For example, let's say you're building an app that tracks customer orders. You might use a dynamic expression to calculate the total cost of an order, based on the price of each item and the quantity ordered. Or you might use a dynamic expression to automatically update inventory levels when a new order is placed.
Like formulas in a spreadsheet, dynamic expressions in Bubble update in real-time based on changes in your app and database. So if a customer changes the quantity of an item in their order, the total cost of the order will automatically update. Likewise, if the price of an item changes in the database, it will automatically update in your app.
By mastering dynamic expressions, you'll be able to create powerful, interactive apps that are constantly kept in sync with relevant data.

How dynamic expressions are structured

Although dynamic expressions can vary greatly in complexity, they essentially consist of three main components: data sources, operators and comparisons. Each individual use of any of these components is called an atom.

Atoms

A data source is any place from which Bubble can retrieve data, such as the database, the current user or an API.
Operators
Operators functions or actions that can be performed on the data source, such as counting, sorting and calculating.
Comparisons let you compare two compatible values, such as two numbers, data types or strings of text and get a yes/no result

Data sources

A data source is any place from which Bubble can retrieve data, such as:
  • A database search
  • The current user
  • An Option set
  • An API call
  • The current date/time
  • The user's current geographic position
This is not an exhaustive list, but it illustrates the point that data sources are not just database records, but any channel from you can draw some sort of information. Expressions always start with a data source, and you and then (often optionally) add one or more operators to that data source to transform, aggregate or manipulate the data in some way.

Operators

Here we describe what operators are in general. If you want the list and technical documentation on each operator, you may be interested in checking out the core reference entry on operators. Reference: Operators
Operators are all kinds of functions or actions that can be performed on the data source, such as filtering, sorting, grouping, aggregating, reducing, joining, and many others, depending on the data type you are working on.
As we saw in the example from earlier, the dynamic expression goes through two steps, separated by a colon (:):
  1. 1.
    Data source: we are using the Do a search for data source to search for users
  2. 2.
    Operator: we added the count operator to the search to instruct Bubble to count the results
An expression will always return the value of its last step. As such, the expression above would not return a list of users, but a number representing the count of users (such as 50).

Data-specific operators

Bubble will adjust the list of available operators based on what kind of data the data source returns. In the example above, the data source searches for users and returns a list. Since this is a quantifiable resource, we can instruct Bubble to count the number of entries in the list.
Bubble will list the operators that are relevant for the type of data returned by the data source. In this example the data source is Search for users (which returns a list of database records) and we can select different operators that apply to that data type.
Were we to change the data source to another type of data, such as a date, the list of available operators changes to reflect it:
In this example we've changed the data source to one returning a datetime, and Bubble changes the list of available operators to reflect it.
An operator will in many cases change the format of the data returned. Let's look at the two examples in the above screenshots:
  • In the first we are using an operator to count the results of a search, and the data switches from a list of database records to a number
  • In the second, we are looking at a datetime, and the returned data depends on the operator:
    • The formatted as 3/10-23 operator will turn the datetime into a text string such as "3/10-2023".
    • The < -range - > will turn two datetimes into a date range
    • The +seconds/+ minutes etc operators will add the number of seconds and minutes but keep the datetime format

Comparisons

Comparisons are the third component of an expression and are used to compare two values to reach a value that is either yes or no. You can see comparisons as a sort of question. In the two examples below, we first see how a human would read the question, and then how it would be formatted in an expression.
Is 5 a bigger number than 3? Answer: yes
This is how a human being would ask the question, and it's not open-ended: the answer will always be yes. In an expression, the same question would be laid out more mathematically and always have a clear answer:
5 > 3 = yes
The > symbol represents bigger than, and again the answer would be yes.
In these two examples, 5 and 3 are both data sources, and the > is the comparison. As such, we are comparing the value of two data sources to get to a yes or no (or true/false if you will).
Expressions are set up to work with dynamic data sources and operators, so let's compare two numbers in an expression that are the results of counting things. In our earlier example, we counted the number of users. Let's assume we have another data type called Task and compare how many there are of each.
The structure in this expression will be:
Data source:operator | comparison | data source:operator
Search for users:count | is | search for tasks:count
We are replacing the > with an is. In other words, we are not asking is one bigger, but changing the question to are they the same. This is what that looks like in an expression:
Let's assume we have 50 users and 20 tasks, in which case the question we ask is:
Is 50 the same as 20? Answer: No

Comparing other types of data

Comparisons can handle any type of data as long as they can be compared. For example, we could ask the question:
Is London the same as New York? Answer: No
In this example we're no longer looking at numbers, but comparing two strings of text. Since the two strings are not identical, we can conclude with the answer no. In a Bubble expression we might get those names from a data type called City, looking something like this:
In the expression above, we are combining multiple data sources, operators and a comparison to ask the question:
Is the name of the City saved on the current user the same as the name of the first city returned by this search? Answer: no
In this example we compared the names of the city (text) specifically, but we could also ask Bubble directly whether the two cities are indeed the same database record:
The question has now changed to:
Is the database record saved in the City field of the user the same as the database record found with this search? Answer: No
The two examples would yield the same result: no, they are not the same. However, if the two cities shared names (such as Paris in the US state of Texas and Paris the capitol of France), the first one would respond with yes and the second would respond with no: they have the same name, but they are not the same database record.
As we can see from that example, you should put some thought into comparisons to make sure that they can reliably give the right response.

Working with Dynamic expressions

Creating

Dynamic expressions can in many cases be mixed in with regular text to produce a coherent message to your users. In the example above for example, we set up a dynamic expression to count the number of users, and wrapped the expression in a text:
There are [expression] users in your app
To your users, the full text will simply be: "There are 50 users in your app", hiding the calculation going on under the hood.
To add an expression anywhere in a text field, set focus to that field and then click the “Add dynamic data” button or use the “/” keyboard shortcut.
Clicking the Add dynamic data button creates a dynamic expression. In the example above we have a text that says Number of users: and want to add an expression that returns the actual number.
Sometimes there will be a field with an empty expression.
  • To create an expression there, simply click on it
  • To delete it, click on it and then click backspace

Deleting

To delete a dynamic expression, click into the expression and then press backspace.

Editing

Each component of a dynamic expressions is called an atom. Each atom can be edited to change the final outcome of the expression. You can do this in two ways:
  • Click the part of the expression that you want to change. For example, if the expression reads Search for users:count, then both Search for users and count can be clicked for editing.
  • Use the arrow keys on your keyboard to navigate left and right in the expression. This works in the same way as when you edit text.
Both of these methods also allow you to insert an atom at the end of or, or in-between two existing atoms. For example, to apply some further filtering before we count our users, we could insert the filter operator in between the search (Search for users) and the aggregation (count). Since expressions are read left-to-right, that filtering would then apply before the count, changing the result of the expression.
The easiest way to accomplish this is to use the arrow keys to select where to insert the atom, and then click / to get the list of commands.
In this example we inserted the operator filtered to apply some additional filtering to the list of users before counting it. The overall structure of the expression is maintained.
To delete an atom in an expression, click on it or use the arrow keys to select it, and then click backspace.

Copy/pasting

There are two ways in which you can copy and paste expressions:

Copy one expression

The first way is to copy a single expression. To do that, right-click the expression and pick Copy expression.
Note that to paste expressions, you need to right-click an area that accepts an expression. This can be an existing expression, or you can click Add dynamic data and then paste it:

Copy all content in a field

If you want to copy not just one expression, but all content of a field, you can right-click outside of the expression but still inside the input field. Note that this will copy both the expression and regular text:
To paste it, you need to click inside of a field that accepts it, but not on an expression.

Expression use cases

Expressions, being such a central part of the Bubble platform, are used in a wide range of different scenarios. Let's look at a few:

In elements

Dynamic expressions can be used in elements to provide a value. We have already looked at how we can place it in a text element to display the result of the expression as a text.

Example 1: Set an initial date

In the example below we are using an expression to set the initial value of a date/time picker element:
The initial content field tells Bubble to show a date in the element when the page loads. In a humanly readable sentence, we are telling Bubble:
Set the initial content of this Date/time picker element to the current date and time, and then add one day
The result for the user would be that the default value of the element is at the current time tomorrow.

Example 2: Load a list of users and show them in a repeating group

When using a repeating group, we need to provide a data source, and this too is done by setting up an expression. For this, we could simply define a data source such as Do a search for Users, but to show how we can use expressions to be more complex, let's include an operator as well:
In this example we are using the intersect with operator to generate a list of users thats different than what we would get in the data source alone. In a humanly readable sentence, we are telling Bubble:
Search for one set of users and a second set of users and then return the list of users who are in both lists
If we assume that the two searches produce different results by having different constraints, we would be left with the list of users that are present in both of the search results.

In conditions (conditional expressions)

In this section we briefly explore how conditions work. If you are interested in learning more you can check out our dedicated article on the subject: Article: Conditions
Conditions, or conditional expressions, are expressions that are set up to return a value that's either yes or no and then use that answer to either do something or not do it:
This can be used to:
  • Style an element depending on whether the condition is true
  • Trigger a workflow to run whenever the condition is true
  • Ask whether a workflow/action triggered somewhere else should run

Example 1: hide an element if the user isn't logged in

For our first example, we'll place a condition on an element that will hide the element if the user is logged out. For example, if we have a Sign out button, it makes sense to hide it.
  1. 1.
    The first part is the expression: we use the data source current user and the operator is logged out to get a yes/no anwer
  2. 2.
    We instruct Bubble to change one of the element's properties: we set this element is visible to unchecked.
In a humanly readable sentence, we are saying:
If the current user is logged out, make this button invisible

Example 2: stop a workflow from running

In the second example, we'll look at how a conditional expression can determine whether a workflow should run or not when it's triggered. Let's say we have a form where users can create a new Task, but we don't want to create the contact if the user hasn't provided a name for the task:
In this example, the data source is the input element Input task and we've added two operators:
  • value instructs Bubble to return the value from that input (which would be the task name the user has provided)
  • is not empty gives us a yes/no answer to whether the value operator returns an empty result or not
In a humanly readable sentence, we are saying:
Only run this workflow if the user has provided a name for the new task

In workflows

In this section we briefly explore how workflows work. If you are interested in learning more you can check out our dedicated article on the subject: Article: Workflows
We can also use expressions in workflows to produce a particular result.

Example 1: saving aggregated data

Let's say that we run an app where users provide reviews for different products. In addition to the built-in user data type, we have a custom data type called Review which includes a number field where users give a score between 1-10.
As the user keeps adding reviews, we want to save their average score on their profile. Let's see how expressions can do that.
  1. 1.
    We have workflow that makes changes to the current user, and we've set it to make a change in a field called Average score
  2. 2.
    We then add the data source Search for users and we add a constraint that the Review must have been created by the data source This user.
  3. 3.
    We add the operator each item's Score which changes the result from a list of Reviews to a list of numbers (the score from each review)
  4. 4.
    Lastly, we add the average operator, which takes a list of numbers and calculates the mean value. The data type in the end is a number which is what the Average score field on the user expects
In a humanly readable sentence, we are saying:
Save the average score to the user by searching for all the users review and calculating the mean

Example 3: creating a date range from two date values

A date range is a piece of data that provides a range between to datetimes by storing a start and end. They simplify the process of determining if a datetime falls within a specific range, among other uses.
Let's say we have system where users can pick a start and end date for a vacation, and we want to turn that into a date range and store it in a custom state.
We'll use the value of the two date pickers as the data sources, and combine them using the < - range - > operator.
  1. 1.
    The first field is the element where we store the custom state – in this case we chose the page itself (index)
  2. 2.
    Then we choose the custom state. We created one and named it Date range.
  3. 3.
    Then we generate the date range by using the < - range - > operator.

How expressions are processed

Dynamic expressions are evaluated from left to right, which means that the system reads each part of the expression in order, one at a time, starting from the leftmost component and moving towards the right. This is different from the mathematical convention used in many other programming languages, which evaluates expressions based on the order of operations, such as parentheses, exponents, multiplication and division, and addition and subtraction.
This left-to-right evaluation order can sometimes lead to unexpected results if you are used to other programming languages, so it's important to keep in mind when writing expressions in Bubble. However, it can also make expressions easier to read and understand, since they are evaluated in a more intuitive way that matches the way we typically think about expressions in everyday language.
In order to maintain this left-to-right evaluation logic, Bubble constantly inserts parentheses into your expression. When you hover over your expression, you’ll notice that there are underlines that will change color depending on what part of the expression you hover. These underlines correspond with the “level” of your expression, equivalent to the level of parentheses.
Every atom that has the same color underline when you hover is part of the hovered atom’s level. Atoms that remain white but do not get colored underline treatment are sub-expressions within the colored expression. Atoms that are greyed out are part of the larger expression surrounding your colored expression.
Last modified 6mo ago