Dynamic expressions beta (discontinued)
Last updated
Last updated
The beta expression composer feature in Bubble will be discontinued effective April 10 2024. We apologize for any inconvenience caused and are grateful for all the feedback received.
You can read more about the background for this decision in the forum post below:
Forum post: Important updates about the beta expression composer
This article covers a beta release; to read about the current stable release, please see the article below:
Article: Dynamic expressions
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 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.
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.
A data source is any place from which Bubble can retrieve data, such as:
A
The
An
An
The current date/time
The user's
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.
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 (:):
Data source: we are using the Do a search for data source to search for users
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).
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.
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:
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 , 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
The +seconds/+ minutes etc operators will add the number of seconds and minutes but keep the datetime format
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.
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:
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:
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:
Comparisons can handle any type of data as long as they can be compared. For example, we could ask the question:
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:
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:
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.
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:
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.
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
To delete a dynamic expression, click into the expression and then press backspace.
Each 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.
To delete an atom in an expression, click on it or use the arrow keys to select it, and then click backspace.
There are two ways in which you can copy and paste expressions:
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:
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.
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:
Dynamic expressions can be used in 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.
In the example below we are using an expression to set the initial value of a :
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:
The result for the user would be that the default value of the element is at the current time tomorrow.
When using a , 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 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:
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 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
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.
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
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:
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:
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.
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.
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
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.
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)
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:
A date range is a piece of data that provides a range between to 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 .
We'll use the value of the two date pickers as the data sources, and combine them using the < - range - > operator.
The first field is the element where we store the custom state – in this case we chose the page itself (index)
Then we choose the custom state. We created one and named it Date range.
Then we generate the date range by using the < - range - > operator.
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.
A data source is any place from which Bubble can retrieve data, such as the database, the current user or an API.
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