You are on page 1of 134

Updated and Expanded Edition

All content is Copyright © 2017 YR Digital,


LLC and Maria Roman. All rights reserved.

All content is explicitly the sole opinion of the author, who often relies on of-
ficial Bubble documentation, reference resources, and personal experience.
Bubble and the Bubble logo are trademarks of Bubble Group, Inc. Coaching
Bubble is an independent venture and is not affiliated with Bubble.

The information provided within this eBook is for general informational purposes
only. While Coaching Bubble tries to keep the information up-to-date and correct,
there are no representations or warranties, express or implied, about the
completeness, accuracy, reliability, suitability or availability with respect to the
information, products, services, or related graphics contained in this eBook for
any purpose. Any use of this information is at your own risk. The methods
describe within this eBook are the author’s personal thoughts. They are not
intended to be a definitive set of instructions for this project. You may discover
there are other methods and materials to accomplish the same end result.
Coaching Bubble
Quick Tip Guide
Hello, and thank you for purchasing the Coaching Bubble Quick Tip
Guide (Updated & Revised). This ebook was designed to be a desk-
top-reference-guide. Keep it on your computer and refer back for con-
tinued support as you build your app.

While there are far too many Bubble techniques and methods to cover
in one place, this robust ebook provides an excellent, 34-lesson re-
source when you need to follow step-by-step instructions during your
development process.

If you’re active within the Coaching Bubble community, you might have
seen some of these tutorials before. Through Coaching Bubble Select,
my free email subcription service, I send out weekly tutorials just like
these. Many of the tutorials I’ve provided in the past are included in this
ebook in order to provide a convenient, navigable way to read through
them (because who has the time to scour through hundreds of old
emails in their inbox?).

The best way to make use of this ebook is by navigating using the ta-
ble of contents to the particular lessons of interest to you. To continue
adding to your knowledge base, sign up for Coaching Bubble Select
at coachingbubble.com to keep receiving tutorials just like these, com-
pletely free.

Enjoy, and happy building!

See you on the inside,


Table of Contents
Click to navigate

Section 1: Design

1. Create a Header for Repeating Groups 4

2. Create a Visually Engaging Micro-Interaction in Just a Few Steps 6

3. Group Your Inputs to Change Tab Order 8

4. Work With Option Inputs 9

5. Link Elements Differently, Even When They Look the Same 12

6. Use 4 Display Tips for Yourself & Users 14

Section 2: Data & Workflows

7. Create User Accounts & Passwords 19

8. Send Data With URL Parameters 24

9. Work With Custom Events 28

10. Work With Custom States 33

11. Understand 3 Database Essentials 39

12. Schedule API Worflows on a List 46

Section 3: External Services (APIs & Plugins)

13. Receive External Service Notifications & Trigger an Action in Your App 50
14. Work With Maps 62

15. Login With Shopify 64

16. Use “Is Development Version” For Smarter Testing Workflows 72

Section 4: Bubble Best Practices

17. Perform Error Handling 74

18. Take 5 Helpful Shortcuts 78

19. 5 Ways to Build Faster & Smarter 82

20. 10 Ways to Copy & Paste 85

Section 5: Bonus Tutorials

21. Test Forms Faster 90

22. Create Session Bookmarks 92

23. Show a Password During Signup or Login 94

24. Create Groups That Collapse & Expand 96

25. Work With the Dropdown Element 98

26. Copy a List of Things 104

Section 6 Continued Below


Section 6: Expanded Lessons

27. Register Groups & Teams on Your App 107

28. Track Mileage & Distance Between Locations 110

29. Avoid Null Search Mistakes 115

30. Create App Update Notifications 118

31. Make the Searchbox Element Function in 2 Ways 121

32. Search Using “AND” “OR” Constrains 124

33. Create a Breadcrumb Navigation Feature 126

All content is explicitly the sole opinion of the author, who often relies on official Buble
documentation, reference resources, and personal experience. Bubble and the Bubble logo
aretrademarks of Bubble Group, Inc. Coaching Bubble is an independent venture and is not
affiliated with Bubble.
Section 1: Design
1. How To: Create a Header for Repeating
Groups
This method is best for repeating groups with a single column and fixed cell layout, which is a very
common configuration for displaying many records in a spreadsheet-like manner.

This is frequently used for displaying database search results for reporting apps, CRM dashboards,
and other analytical use cases where the repeating group itself is fixed, and pagination is used to
move between result sets instead of scrolling.

Here’s an example:

Step 1:
• Increase the height of your repeating group cell to fit the header text.

• Place a group element inside the repeating group cell and make it the same width.

• Place your header text inside the group and position them with the same spacing as the actual
cell content.

• In the example, “Full Name” is positioned directly on top of the text element that’s displaying
“Current Cell User’s Full Name.” The header text is inside the group, whereas the dynamic text
is not.

Step 2:
• Add a condition to your group: When current cell’s index is not 1 > uncheck “this element is
visible”.
4
• Under the appearance tab of the property editor, check “Collapse this element’s height when
hidden.”

These settings will allow the group to only show in the first cell, regardless of how you sort your
repeating group content, and collapse in height in every other cell so you’re not left with a gap.

5
Step 3:
• Design your group! Now that you’ve isolated the header content, you can design it so that it
stands apart from the content in the first cell and the rest of the repeating group.

• Test responsiveness. Because the header group is inside the repeating group cell with the rest
of the dynamic content, it should have a much easier time behaving responsively. If you build
your headers with a separate repeating group or a group outside of the primary repeating group,
it will probably look great until the screen width changes and things fall “out of sync.” With this
method offers friendlier responsive results.

2. How To: Create a Visually Engaging


Micro-Interaction in Just a Few Steps
This section teaches how to create a micro-interaction for the ever-popular “Like” feature found on
most social networks.

6
To achieve this effect, we’ll do 2 things:

1. Create the workflow to show the large icon and then ani-
mate it away.
• When small icon is clicked > Show element (large icon) > Animate element (large icon)
• For example, you can use the Transition Bounce Out effect with a 1500 millisecond duration

2. Add conditional color changes and transition properties to


the smaller icon and text.
• Add a condition for whatever should deter-
mine the color change. For example, when
this content is added to the Current User’s
“List of Liked Images”
• Define your new color.
• Add a transition for font and icon color and
set the speed at which you want that fade to
happen. For example, you can use a 1000 ms
transition duration.

7
Micro-interactions should be designed into your app with a purpose. They should be useful to your
user in some way, letting them know their input or engagement was received and, in some cases,
updating them with new information (such as a file upload’s progress).

Think about your user’s experience, and use micro-interactions as a way to provide important
feedback. With the right balance of purpose and subtlety, your UX design could greatly improve in
sophistication.

3. How To: Group Your Inputs to Change Tab


Order
This quick tip lets you set up inputs so users can move between them in the order you want, with
the tab key.

Bubble’s tab order is left to right, then up to down, with priority given to inputs in the same group.

Here’s what tabbing through 4 ungrouped inputs would do: upper left, upper right, lower left, lower
right...

1 2

3 4

Placing the left two inputs in one group, and the right two inputs in another group changes the
order: Left group’s top input, then bottom, followed by right group’s top input, then bottom...

8
1 3

2 4

That’s it! If your app involves a form with inputs in strategic places, group them so that your users
can move through the form in a logical order.

4. How To: Work With Option Inputs


Checkboxes, Radio Buttons, Ionic Elements, and Alternatives

Bubble has a few different elements that let users make selections in your app.

The checkbox and radio button elements are built-in and you can install the Ionic Elements plugin
to get the ionic checkbox and ionic toggle elements.

In general, these elements are for making binary selections or selecting from a pre-determined list
of options.

This quick tip will also show you how to create your own “element” to have more control over
design.

9
Checkboxes are best suited for single or multiple yes/no values.

For example, you can use a single checkbox on a login form to accept terms and conditions. The
user either accepts or does not accept.

When “checkbox A” is changed and this checkbox is checked > sign up user > TOS agreement =
yes

Multiple checkboxes, on the other hand, can be great for things like custom food orders:
When button “submit order” is clicked > create order > pickles = checkbox Pickles is checked, to-
mato = checkbox Tomato is checked, etc.

There are a few ways to utilize the checkbox selection value


1. Use the “when input’s value is changed” workflow event.
- Create 2 workflows with 2 conditions: “...and when this checkbox is checked,” “...and when
this checkbox isn’t checked.”

2. Set the field value to “checkbox is checked” (this does not mean you’re setting the
value to “yes”).
- Checkbox is checked returns a yes or no value; yes if it is checked, and no if it isn’t.
So, if Checkbox “Pickles” wasn’t checked, “Pickles = Checkbox Pickles is checked”
returns “no.”

Radio Buttons are best suited for selecting a single value from a list of multiple options.

For example: A question on a registration form about a user’s highest level of education with the
options: high school GED, associates degree, bachelor degree, or graduate degree.

Another example could be income level: 0-20k, 21-40k, 41-60k, 61-80k, 81-100k, 100k+, etc.

Only one of these will be true for each user, and the radio button element only lets users choose
one value.

Radio buttons can also be dynamic, so keep in mind the field type to which you’re saving the
selection matches.

Another element with similar functionality is the dropdown element, but with radio buttons, the
selection made is visually identified among the rest of the options.

10
The Ionic Elements plugin installs the ionic checkbox and ionic toggle (plus ionic icons and ionic
range).

The ionic checkbox functionality is identical to Bubble’s built-in checkbox, but its design is updated
and offers a few color options.

The ionic toggle functionality is also identical (yes/no value), but instead of a checked or unchecked
input, it can be used for “on/off” values, like “published/unpublished” or “active/inactive” statuses.

Alternate “Element”
Use Custom States and Icons or Images

One downside to the above element options is that you can’t resize any of them.

The checkboxes can sometimes be super small and the ionic elements a bit large for web apps (of
course, this is personal preference). To be fair, the Ionic Components were created specifically for
mobile environments, so their sizes make sense on smaller screens when forms are bigger.

To appease my own sizing preferences, I usually create my own input elements with icons and/or
custom icon images since both icon and image elements can be re-sized, which you can do, too.

Another benefit to doing this is the actual design of the input can be almost anything you want.

For example, I like to use 2 Google Material Icons to create a nicer looking checkbox. The previous
image is a result of a custom state workflow (yes/no) and condition on the element to change the
icon based on the custom state.

The workflow would be: When the user clicks on the icon and custom state’s value is no > set state
> state’s value = yes.

With the Icon Condition being: When custom state’s value is yes > change icon.

11
5. How To: Link Elements Differently, Even
When They Look the Same

One of the best things about Bubble’s platform is there are many ways to achieve the same goal.

This is a great thing because it means you have options, and what might not be an efficient route
for someone else could be the best option for you.

This how-to demonstrates linking to an external website with 3 different elements: link, text, and
button.

The best part is, all three can be styled the exact same way.

This means you have the freedom to choose the functionality that works best for you without
compromising on aesthetics.

Below is a button, text, and link element.


Can you tell which element is which?

12
Property Chart

If you take a look at the property editor for all three of these elements, you’ll notice they each have
a different focus:

Text: the text display itself

Button: a simple component for triggering click workflows

Link: sending users to other pages (internal or external)

... and they can all link without the user knowing the difference!

Here’s a chart that compares properties side-by-side. See


what combination works best for your needs when deciding
which element to use for linking:

13
6. How To: Use 4 Display Tips for Yourself and
Users

Use <-modulo-> to Alternate Row Colors


Quick math vocab lesson: A “modulo” operation is one that returns the remainder of a division
between 2 numbers.

For example, 5 mod 2 = 1, meaning 5/2 has a remainder of 1. 5 mod 5 = 0 because 5/5 leaves no
remainder.

This operation can then be used to calculate which cell’s index in a repeating group is even or odd.

With that, we can alternate the colors of a shape inside the Repeating Group cell:

1. Put a shape into a repeating group cell.

2. Make it the full width and height, and color it.

3. Then, create a condition: When current cell’s index modulo 2 is 1 > change color

Because we’re dividing every cell index # by 2, every other cell will be divided evenly, and the
alternating cells will not.

Our modulo result will either be 1 or 0. When 1, change color.

14
(Image Continued)

Watch a video tutorial for


this technique on Coaching
Bubble’s YouTube channel.

Primary Fields Display

Whenever you have fields that are a custom type, you’ll notice the database will display those fields
by the unique id of the custom type entry or some other default field (that might not be as
useful to you when reviewing your data).

This is a User data type, where the Users’ names are: George Washington, John Adams, Tom
Mitchell, and Samantha Higgins.

There is another field under User called Friend, which is also a type User.

Each User has another User as a friend, but this view isn’t really helpful for seeing who the friend is.

So, we’ll change the primary field display for the User data type so that across all app data views,
any field that is a type User will be represented by a more identifiable value.

In a User case, a name is a great field to choose.

15
16
You can also change this field from the modify entry view by clicking on “change
search field” below any field that’s a custom data type.

Watch a video tutorial for


this technique on Coaching
Bubble’s YouTube channel.

Run As a User

This feature is incredibly useful. Select “Run as” next to any User in your database to see your app
in run mode (aka preview) mode as that user.

This is really helpful when you want to test pages that might display and/or do different things based
on who the user is.

Examples are user profiles, message threads (senders & receivers), storefronts as viewed by the
customer or the seller, etc. Even Facebook has a “view as public,” “view as friend,” etc. function.

The biggest time-saver here is not having to log in and out as different users because “Run as”
immediately logs you in as that user.

This also works in development and live mode. In fact, I use this to help troubleshoot and support
clients with their apps already in production as I can see exactly what their users are seeing.

This helps isolate issues much faster and reduce guessing. 17


Tooltips

You can create tooltips for any text, button or icon.

What’s really happening is it’s setting a title attribute to the element, but these can take dynamic
text, which makes it pretty flexible.

Set the tooltip text in the property editor, and whenever you hover over the element, the tooltip will
appear!

18
Section 2: Data & Workflows
7. How To: Create User Accounts and
Passwords
Bubble gives you a few different ways to create user accounts and passwords in your app.

From a straight-forward username & password action, to social logins, to assigning


temporary passwords, below is a breakdown of all your options:

Sign the User Up

This account action is the most straight-forward method of signing up a user.

An email address and password are required to create a User, but you can also offer an optional
password confirmation and save additional values to the User record with “Change another field.”

Keep in mind that Bubble encrypts the password values, so you won’t see these in your App Data.

To change a user’s email or password, use the Update User Credentials action.

19
Sign the User Up With a Social Network

This action requires a social network plugin to be installed in your app from the plugins tab.

Register your app with the network (usually through a free developer account).

You’ll receive your API keys or Client ID and Client Secret once registered. Enter whichever is
required in the Bubble plugin settings.

After you’ve registered, the developer console will ask you for basic details like app name, URL, or
logo.

You should also set the redirect URI at this point, which is the exact web address of the page that
triggers this action.

Remember to include the test version of your page so you don’t get errors during development.
This lets the network know the right app is trying to access it.

If you have an OAuth2 API configured in the API Connector plugin, those will also become available
as providers for this action.

20
Create an Account for Someone Else

This action lets you create a user record for another person by supplying an email address only.

It’s up to you to properly handle the creation of a password for the user if they need to log in.

You can do this either by Assigning a Temporary Password and/or sending a Password Reset Link
to the email address.

Create a User Record Manually

As with any data type in your app, you can manually add a User record to your database by
going to:

Data Tab > App Data > New Entry

Select User as the type of thing, and fill in your fields. An email address is still required here.

I caution you to use this method for quick testing purposes only.

As your app evolves, it’s very likely you’ll have workflows attached to the creation of a User, which
could become difficult to replicate through manual entries. Fields could get skipped, other data rela-
tionships could be missed, and you could easily end up corrupting your data.

Try to keep your user registration through workflows as much as possible.

21
Assign a Temporary Password

This action will generate a random text string for a User record to act as its temporary password.

You can retrieve the value of this password by using “Result of step [step # with Assign action]” for
sending it in an email to the User.

Users can log in with this password normally, but you have the option to force them to another page
where they can create a new, encrypted password.

Go to Settings Tab > General > “Redirect users who haven’t changed their password” and
select a page where you have an Update User Credentials action set up.

22
Send a Password Reset Email

This action will send an email with a unique link to the built-in reset_pw page.

Bubble generates a one-time-use-only token, which is included in the reset URL.

You can choose to generate the token only. Retrieve the token with:

“Result of Step [step # with Send Reset action]”, and create a custom navigation flow.

“Just make token, don’t send email”

Update User Credentials

This action lets you change the current user’s email and/or password.

Depending on how you’ve designed the page and inputs, you can do both in a single action, or
select one type of credential for separate actions.

The current user’s password is required to update either email or password, so you shouldn’t use
this to reset a password if they’ve forgotten it.

23
8. How To: Send Data with URL Parameters

A URL parameter is made up of two parts: key and value.


Take a look at this YouTube link: https://www.youtube.com/results?search_query=the+1975

The key is “search_query,” the value is “the+1975,” and the + symbol is an encoded space.

Sometimes you’ll see “%20”, which is how Bubble encodes spaces.

Parameters are appended to the base URL beginning with a “?” and strung together with “&.”

For example, here’s a URL with 3 parameters:

https://www.myapp.com/signup?referral-code=abc&age=24&gender=female

So, what do you do with parameters?

Here are two very common use cases for URL parameters, and in a moment we’ll talk about
making it all happen in your Bubble app:
24
1. Search Queries
Let’s say your index page contains an input for typing in a search term. When the search button is
clicked, the user is sent to a separate Search page.

In the same navigation action, you can send whatever the user typed into the input to that Search
page via the URL as a parameter. Then, on your Search page, you could have a repeating group
that’s constrained by the value of that parameter (remember, a parameter is key=value).

In the YouTube example, I went to YouTube.com, typed in “The 1975” into the search bar, and
clicked the search button.

I was redirected to the “results” page, and the list of video results were filtered by my search query.
I didn’t go to a special page on YouTube for “The 1975.”

It’s the same results page for all YouTube videos, but the content gets filtered by the URL parame-
ter value.

Note that going directly to https://www.youtube.com/results actually redirects you back to YouTube’s
index page, which is a great way to handle an empty parameter on a page that relies on them to
function properly.

2. Referral Codes
Instead of creating a parameter during a page change, you can simply append a single parameter
like myapp.com/signup?referral=NewYearPromo and share that link with potential users.

You can create a condition on the “signup” page to look for valid parameter values to trigger a
workflow that discounts the user.

Going to the signup page without the code would trigger the normal registration flow at full price.

How do you generate and use parameters in Bubble?


Generating a parameter happens inside a “Go to Page” action.

In this example, my key is “user” and the value is dynamic: the current user’s unique id.

The result of this URL will be something like myapp.com/forms?user=1234.

If I typed in a value like “Coaching Bubble,” the URL would be myapp.com/forms?user=Coach-


ing%20Bubble.

25
The value of a parameter can then be retrieved from the list of dynamic options: Get data from page
URL, where you’ll need to click on the expression once selected to define the parameter whose
value you’re looking for.

Remember, you can have multiple parameters, so define the one you specifically need for the
expression.

PRO TIP:
You can also change the value’s type.

In most cases, your parameter will work well as a text.

Sometimes, though, you may want it to be a number so that you can still do math with it, or
a date to sort and filter chronologically, for example.

You have all normal field types and custom data types available to you.

In my user = current user’s unique ID example, I am now able to set the value type to User,
and therefore pull user data directly from the parameter!

26
There are many ways to make good use of a single or multiple parameters in a URL. Here are a
few ideas to get you started:

1. Trigger workflows: when page is loaded and get data from url is xyz > specific action

2. Send custom state values to other pages

3. Send input values that aren’t being saved in the database to other pages

4. Create special promotion or referral links

5. Verify authorized visits to a page (i.e. if no parameter, redirect to index)

P.S. Parameter keys are cAsE sEnSiTiVe!

27
9. How To: Work With Custom Events
This “how to” is all about custom event fundamentals.

Custom events, by the way, are not the same as custom states.

Custom events are workflows whereas custom states allow elements to store a value of some
defined type.

Custom events are great for the following:

• Re-using an action sequence for multiple workflows so you don’t have to re-program the same
thing over and over again.

• Delaying a sequence of actions (with “schedule custom event”) when a workflow is triggered.

• Automating conditional sequences. This can become very powerful as everything is available to
you, like scheduling, APIs, API workflows, plugins, other custom events, dynamic data, etc.

Below is an example to demonstrate a universal benefit: consolidating action sequences.

Custom Event Concepts


Custom events can seem a little intimidating at first, but if you think of them as regular workflows
that you can trigger from other regular workflows (and even from other custom events), you’ll find
they become a powerful automation tool.

A few concepts to keep in mind:

• Custom events work at the page level, so you can’t trigger custom events on page B from page
A. However, you can trigger custom events in reusable elements from pages that contain those
elements.

• Custom events can (optional) receive a Thing’s data to run actions with that data. This is similar
to passing a Thing to a page.

For example, if the sequence of actions needs to make changes to a dynamic User record (that
is, if you won’t know which User), you’d define the event’s type as User and when the workflow
triggers the event, it will also define the User.

• Custom events can be triggered instantly or scheduled with a delay defined in milliseconds. An
example of this is giving the user time to “undo” something before making a database change.
28
• Custom events can run on conditions just like regular workflows.

Say you have a workflow that triggers 2 custom events; event 1 only runs when condition 1 is
true, and event 2 only runs when condition 2 is true.

This is one way to anticipate different outcomes from a single workflow. Of course, you could
also just have multiple workflows with the same conditional logic, but using a custom event is
beneficial when there is opportunity to minimize redundant action sequences.

• Custom events can run on conditions just like regular workflows.

Say you have a workflow that triggers 2 custom events; event 1 only runs when condition 1 is
true, and event 2 only runs when condition 2 is true.

This is one way to anticipate different outcomes from a single workflow.

While you could also just have multiple workflows with the same conditional logic, using a
custom event is beneficial when there’s opportunity to minimize redundant action sequences.

• Custom events can trigger other custom events. Be careful here as you may get yourself stuck
in a loop with events triggering each other, but with the right conditions set and flow mapped out,
this is where automation can really help you.

General Process
1. Create a custom event
(Add event > Custom > Create Custom Event)

2. Set up event by giving it a name, defining a data type (if the event will need to
access a Thing’s data), and setting conditions, if necessary.

3. Add actions just like any other workflow.

4. Select a trigger or schedule (delays the trigger) action from Custom Events.

Select the custom event, and if you defined a type of thing in the custom event setup, set the Work-
flow Thing to use (type must match).

29
1. 2.

3. 4.

30
Consolidating the Same Actions for Multiple Workflows
into One Custom Event

This example demonstrates the re-use of a single action sequence from multiple workflows.

I have 3 buttons on a page that save user data and do a few other things in the database like create
new Things, modify Things, and show a success alert message.

I want the same thing to happen for all 3 buttons, with the only difference being the User data
involved; each button runs this sequence for a different User.

Without a custom event, your workflows would look like this:

31
With a custom event, we can consolidate:

32
Rather than have all actions in all Save Button workflows, they instead each trigger the single
custom event that holds the actions.

Corresponding data is defined in the triggers so the custom event runs on different Things.

A huge benefit of consolidating is if you need to make changes to this sequence, you change it
ONCE (and not three times or significantly more, depending on your workflows).

This saves you time and leaves less room for error.

10. How To: Work With Custom States


Featuring the Tab Element & Multi-Select

Custom states allow you to modify an element’s behavior and/or appearance based on the value of
that state.

These values can also be used in regular workflows.

Basic example: “If CustomState1 equals Value1, then Change The Color Of My Button”

Intermediate example: “If CustomState2 equals ListofUsers, then Delete Those Users”

Custom states work like the built-in states “hovered”, “focused”, or “pressed,” where the name of
the state are those labels and their values are yes or no.

You can think of custom states as another type of element state-change you define with values.
These values are not stored in the database, and the element response you create is for the user
currently interacting with it.

That is, if John in Australia is hovering over a button, which makes it turn red, it’s not going to be
seen by Jane in Canada, unless she hovers over it herself.

You can also think of them as post-it notes!

Using custom states is (generally) a way to temporarily hold some information so you can have it
readily available to do something within the same page visit.

Therefore, keep in mind that unless you set a state upon page load, refreshing or changing pages
clears all custom state values; the post-it is taken down.
33
Another thing to note is that custom state values work like field values in your database in that they
need to be a defined type (text, number, User, custom data type, etc.) and can either be set to a
single value or a list of values.

We’ll look at a couple combinations below:

The Tab Element


Bubble has a built-in element that’s a great
example of custom state use: The Tab Element, which
you can find at the very bottom of your elements list.
This element is a template for creating tabbed groups.
Click on a tab (button), see a different group. Let’s add
this element to the page and study the style conditions
for the Tab 1 button.

34
Here’s what we know about the second condition, “when Group Tab’s current
tab is 1”:

• Group Tab is the name of the element with the custom state
• Current tab is the name of the custom state
• 1 is the value of the state and is either a type number or a type text.
• The value is not a list because the expression reads “current tab is,” whereas if it were a list, it
might say, “current tab contains.”

This condition allows us to change the Tab 1 button’s appearance when the value of the state is 1.
The other tab buttons will have the same conditions, but for different values; Tab 2 will change when
state is 2, Tab 3 when 3, etc.

This element template also comes with the workflows created for you, which should hopefully help
you understand how to modify custom states. In this case, when a tab button is clicked, change the
value of the state.

The final piece is changing the visibility of the groups


based on the state using conditions.

35
Here it is alltogether. I’ve also added a text element to dis-
play the value of the custom state so you can see it chang-
ing as well.

36
Creating a Custom State
Custom states are created and modified through workflows, but again, they don’t store information
in your database unless you specifically create a workflow to set database values to equal custom
state values.

In this how-to, we’re going to create a multi-select feature using a single custom state. Below is a
repeating group with a list of users, and we want to select multiple users to delete them all at once,
instead of one by one.

Here’s what the result looks like:

What’s Happening?

• Click on a user name to add it to a list of Users.


• Click on Delete Selected to delete the list of Users from my database.
• Condition: When a user is in the list, change the name text to red.
• This List is our custom state.

To make this happen:

1. Create a state by adding a “Set State” action in your workflow.

37
2. Select the element for the state.

In this example, it’s the repeating group. It doesn’t really matter what element you choose because
as long as the element is on the page, you’ll have access to its states (even if it’s not visible).

However, stay organized by choosing a relevant element.

3. Name the state.

4. Define its type.

5. If the value is a list, check list.

Our example is a list of users. In the Tab Element example, it was a single numerical value.

6. Set the value for this workflow.

You want to ADD the current cell’s user to this


custom list of users, so the value should be the
existing list PLUS the current cell’s user.

If this is the first user added to the list, the formula


will still work properly because if the existing list
contains 0 entries, then 0 entries plus 1 entry = 1
entry.

This is all you need to do for the repeating group


since the workflow and state value are both
reading “current cell’s user” and will apply
accordingly when you click on different users.

A really cool application of this multi-select is in a message inbox environment, where you can
select multiple messages to delete, archive, move, etc.

Your custom states are essentially temporary field values that expire once the page is changed, but
they can be incredibly powerful for creating enhanced element responses without ever modifying
your database.

38
However, there are plenty of applications where you might want to save those custom state values.

For example, a shopping cart and checkout workflow could employ custom states to “temporarily
store” a customer’s cart of items and save them to the database as a permanent record when they
make a purchase.

A few other things to note:

• You can send the value of custom states to pages as data just like you would any other value
(via URL or in a Go to Page action).

• State values can also be dynamic, like the result of a database search with filters and modifiers.

• You can use multiple custom states together to create even more complex responses; the val-
ue of one could be another modified, or the value of one can be dependent on the existence of
another, etc.

11. How To: Understand 3 Database Essentials


Structure
Your Bubble database is organized by data types.

Types contain fields whose values can be text,


numbers, files, dates, geographic addresses, yes/no
(Boolean), and even other data types which allow for
powerful cross-referencing.

Every app comes with a built-in User type, which you


would use for users who sign up for your app with an
email and password.

The email is required and is used as the unique


identifier for every User.

Additional types you create are considered Custom


Types.

Create a new field and select the type drop-down to see


all your field type options. 39
Defining a field as another one of your data types will allow you to link to an entire entry of a data
type.

For example, we could link a salon Service to an Appointment, and when viewing Appointment
details, we would also have access to its Service’s Cost.

Setting a field type to be a list means that you can link to multiple entries of a data type.

For example, a User’s list field called “Followers” that’s also type User would allow you to create
powerful connections like fans, friends, co-workers, etc.

Let’s say you’re the owner of a salon (structured below) and you need to fill out a form every time
you want to add a new Service to your menu, a new Stylist to your staff, and a new
Appointment to your schedule.

Your database structure reflects that form structure. The fields within each type make up the form,
and in this example you have a different form for Services, Stylists, and Appointments.

40
Saving Data
Saving data is essentially filling out the form for a type and submitting the entry. There are 3 ways to
do this:

#1 Manually Adding Data

You can manually add new entries to your database by following this path:

Data Tab > App Data > New Entry > Select Your Data Type

Manually adding & modifying is OK for working with a few entries and your own admin purposes,
but if you plan on doing a lot of bulk management, you’re better off setting up API workflows.

Plus, you’re probably more interested in allowing users of your app to work with data, which brings
us to the more common form of saving data…

PRO TIP
API Workflows are available on the Personal Plan and above.

Navigate to:

Settings Tab > API > Check “This App Exposes a POST/Workflow API”

...and your page menu will make API Workflows available.

Create the workflow, and use it on your database by following:

Data Tab > App Data > Bulk

#2 Adding Elements & Creating Workflows

Add input form elements to your page to allow your users to input information like text, file
uploads, and dates.

Every element has a few different formatting options, so explore the property editor for each
element to see the different ways you can format text, date & time, drop-down labels, etc.

Note that text elements have a handful of different content formats that affect how your data gets
saved.
41
While you can create workflows
as soon as an input’s value has
changed (i.e. any input you see in
the list on the right), let’s keep this
straight-forward and create a work-
flow off a button click.

Creating a new thing in your database is as easy as filling out your own form. Select the type of
Thing you want to create, and for each field available in the data type, you can set their values to
the values entered into those input elements.

42
Bubble refers to each data entry as a “Thing.”

So, when you create a new Thing, Delete a Thing, or Make Changes to a Thing, you’re referring to
an entry within a type.

For us that would translate to “Create a new Appointment, Delete a Stylist, Make Changes to a
Service, etc.”

PRO TIP
I highly recommend resetting (clearing) inputs in this same workflow to give your
users an interactive UX.

Showing an alert message with success text and changing the page are other
ways to give users “feedback” and let them know something has happened.

#3 Upload By CSV

This is for development, paid plan use only as Bubble does not currently support CSV uploads as
part of a workflow.

Navigate to: Data Tab > App Data > Upload

Make sure your first row contains column labels. Select the data type and then map the fields
you’ve set up in your database to the corresponding column headers.

Retrieving Data
Now that we’ve saved the data to our database, we’ll want to call it back up to modify or display on
our page. The following steps can be applied to any element or workflow that requires you to estab-
lish data source or is displaying dynamic data (in other words, anything that needs to retrieve data
from your database).

Whenever prompted to enter dynamic data, you’ll get a list of options with compatible types you can
access. They may be the current page, the current user, an input, a plugin, etc.

I want to highlight database searches since that’s the true “teaching you how to fish” concept here.
Understand how to search your own database, and everything else will follow suit.

For a full list of available data sources, visit https://bubble.is/reference#Data.DataSources

43
Doing a search means defining the file path directly to the thing (or things) you want.

You’re probably familiar with uploading or downloading things online and navigating through your
computer’s folders to reach the file you need.

The expression composer is very, very similar to that - plus, you can filter your search.

I’ve found there’s usually more than one way to perform a database search, but some routes are far
more efficient than others.

Try to keep it as direct as possible. If your expression is getting too complicated, try it another way
or consider re-structuring your database.

Things To Know About Database Searches


• Search for [Thing] will return every entry in that data type.
• Click on Search for [Thing] and add constraints to your search to filter down the results.
• Click on More after Search for [Thing] to access the Thing’s fields. Ex: “Search for
Appointment’s Date” will return a list of all date values for each Appointment entry.

44
• Clicking on More also allows you to access modifiers, which are denoted by a colon and can
change the value and/or format of the data.
Ex: “Search for Service’s Cost: Sum” will return a sum total of cost values for every
Service entry (Yes! You can perform and save math operations!)

• The modifier, first item, will return the first item in the result list.
If you filter your search properly, you can return the exact entry you’re looking for.

• Pay attention to what your expression is ultimately looking for since your Type (data or field)
will need to match the Thing to modify, the element type, etc.
If your expression turns red, it means your data types are incompatible. Ex: Your search
expression leads to an image when your element type is a text.

Example Searches & Results

Element: Text
Type: Text
Data Source: Search for Services’s Cost:formatted as Currency
Constraints: Label = Haircut
Result: $45.00

Element: Repeating Group


Type: Appotinment
Data Source: Search for Appointments
Constraint: Stylist = Current User
Result: The repeating group will return all of the Appointments that the current user is a stylist for

Element: Group
Type: Appointment
Data Source: Search for Appointment:first item
Nested Element: Text
Dynamic Text: Parent Group’s Appointment’s Services Cost:Sum - 10:formatted as Currency
Result: $35.00
English: The total cost of an appointment’s services subtracted by 10. The group serves as a
parent data source for anything inside of it. Instead of searching the database, the text element
just pulls from its parent because the parent already did the search.
45
Element: Calendar
Type of Events: Appointment
Data Source: Search for Appointments
Constraint 1: Stylist = Current User
Constraint 2: Date > Current Date/Time
Constraint 3: Date < December 31, 2017
Result: The events that appear on the calendar will be appointments scheduled from now to the
end of 2017, where the current user is the stylist.

PRO TIP
To make the calendar dynamic and display appointments based on a selected
stylist, use a dropdown element to list all stylists.

Add a Stylist constraint to the calendar data source:

Stylist = Dropdown’s value

Whenever the value changes, the events displayed will as well.

Knowing how to work with your database properly will come with practice.

Try everything - all the modifiers, constraints, sorting options, routes to an


entry...and see what happens!

You can build social networks, project management, and complex directories on Bubble thanks to
the vast flexibility and control you have over database structure.

12. How To: Schedule API Workflows on a List


Let’s walk through scheduling an API Workflow on a dynamic list of Things.

Here’s a common, real-world example: When you order multiple products on Amazon, it’s very
likely the products will be coming from a variety of sellers. 46
It’s also likely these products will ship at different times.

When checking out, Amazon asks you if you want to receive separate shipments, or wait until the
most delayed shipment so the products can be grouped together and you receive fewer
packages.

Let’s say your app is Amazon and a buyer chooses to receive things as they become available, so
they opt for separate shipments.

Because the number of products is unknown to you, and you certainly don’t want to enforce a limit
on the buyer, you’ll need to schedule an API workflow on his Product List so that for every ordered
Product (Thing), a new Shipment (Thing) is created.

Step 1: Turn on “This app exposes a Workflow API”

Step 2: Navigate to the API Workflows Page

47
Step 3: Add a New Endpoint & Set Up Create New Shipment
Workflow
You’ll need to define keys so the new shipment is associated with the appropriate Product, Order,
and customer (User).

Then, add a Create New Shipment action and use these keys for field values.

48
Step 4: Schedule the Workflow on a List

For this example, we’ll add this


action when a “Submit Order”
button is clicked on a checkout
page.

An order is created for the


customer, which contains a field for
List of Products. This is the list we’ll
be scheduling the API workflow on.

Then, for the Schedule API Workflow action, fill


out the details.

Note that for the “product” key (setup in Step 3),


the value is “This Product.”

When the workflow runs, a new shipment will be


created for every product in the list, and any field
containing the key “product” will be a different
value for each Shipment.

49
Bringing it all together:
Let’s say: Joe ordered headphones, a coffee mug, and a book.

When Joe clicks “Submit Order,” a single order will be created to document Joe as a customer,
his list of purchased products, a total amount, payment details, and other info.

This will also trigger an API workflow to create a Shipment record for headphones, a record for
coffee mug, and a record for book.

Each shipment’s order field, customer field, and delivery address field will be the same, but the
product field will be different.

Section 3: External Services


(APIs & Plugins)
13. How To: Receive External Service
Notifications & Trigger an Action in Your App
This tutorial is all about configuring an API workflow to receive a webhook from an external service,
which you can then use to trigger an action in your app.

A webhook is an event notification.

When something happens in your account at some service, the service will create a notification.

That notification is like an email. It’s a message with different pieces of data: sender info, subject,
and body.

The notification gets sent to a URL that’s “listening” for a message. Think of the listener URL as the
email address the email gets sent to.

In Bubble’s case, the listener URL (some services will call it the “notification URL”) is an API
endpoint that we create in the API workflow area.

The address for your API endpoints follows these formats:

https://[app-doman]/api/1.1/wf/my_endpoint for Live


https://[app-domain]/version-test/api/1.1/wf/my_endpoint for Development
50
What does this mean in English, how do you use this, and why should you care?

Ok, let’s take Stripe as our external service example.

There are many events that could happen within a Stripe account:

• A customer’s card is charged


• A charge failed
• An invoice was created
• A subscription trial has expired
• And so many more...

The exact moment one of these events occurs, Stripe can send a webhook to your Bubble app.

When your Bubble app receives the webhook, it can then trigger a workflow, such as sending an
email or making a change to a Thing in your database.

Think of a webhook as a data email, where your app is the inbox and
your API endpoint is the email address.

You can have multiple endpoints to receive webhooks from multiple services.

You can also create conditions based on the data in the webhook so different actions are
triggered.

Most webhooks will send data for the event type (this is like the email’s subject line), so you can
identify what kind of data it contains.

So, you can have one endpoint to receive Stripe webhooks, but multiple actions with conditions
based on the event type.

Webhooks allow you to receive data in real-time because they’re sent as soon as the event
happens. This is why they’re very helpful for creating notifications and instant updates in your app.

Let’s set up an API endpoint*** to receive a Stripe webhook.

We’ll create a new endpoint and name it “stripe-webhook.” (We’ll ignore all
privacy settings for this tutorial.)

51
Now that we’ve named it, we know what the full notification URL is.

We’re only going to use the test version for now:

https://coaching-sandbox.bubbleapps.io/version-test/api/1.1/wf/stripe-webhook

Now, we’ll just add a simple action to create a new customer Thing in the database.

52
But wait, how do you save anything?

How do you access the data in the webhook and parse them out into each of the customer fields?

The first option is to set up manual parameters.

For example, you could create a parameter labeled “email” because the Stripe documentation and
the customer.created event type includes an “email” parameter.

But that’s a lot of work for potentially over a dozen fields, and it also has room for error. What if you
misspell the parameter key or the value you need is deeply embedded within several nested arrays
of data? You probably don’t want to mess with that.

53
Let’s use Parameter definition: detect data instead.

I’m not knocking manual parameters - they’re highly useful - but for webhooks, the detect data
feature is so much more convenient for capturing every parameter the webhook will send.

Here’s how we’ll detect the data parameters:

First, hop over to the Stripe dashboard.

Stay inside the test environment and navigate to:

API > Webhooks. Then, click on Add Endpoint.

In the Add a webhook endpoint window, paste in the API endpoint URL.

I already mentioned earlier how this URL should be formatted for your app’s endpoint, but befor
adding that in here, Bubble requires an “initializing” step.

54
In order for Bubble to automatically detect the data for all future Stripe webhooks sent to this
endpoint, you need to send a test webhook to your API endpoint.

Bubble will then do an initial detection and internally remember the data structure that Stripe’s
webhooks will use. So, let’s get Bubble ready to initialize...

Back in the API workflow page, we’ll click the detect data button in the endpoint settings:

Bubble will present a window that looks like it’s waiting for something.

(It’s listening! It’s waiting for a webhook!)

And it has provided a unique endpoint for this initializing step only. Click on the url to copy it, and
then paste in Stripe’s Add a webhook endpoint window.

55
You can see that this “unique endpoint” is really just the endpoint you’ll use going forward with
“/initialize” at the end.

After pasting in the initialize endpoint, you can select which type of event you want to receive from
Stripe.

Some services don’t give you a choice like this - it’s often all or nothing, but better webhook
services will give you options. Stripe gives you the option to receive all event types or to select
specific ones.

For this tutorial, we’ll just select the customer created event:

56
Now click Add endpoint. The endpoint will appear in the dashboard’s list of webhooks. Click on it
to open up details, and then click on send test webhook.

Now...the moment of truth. Click on send test webhook in the new window and head back to the
Bubble page to see the detection window change to a webhook response window!

Once you review the data and make sure the value types are correct, click save, and then navigate
to “Request Data” in the dynamic values list of the Create Customer action.

You can see in the image below that instead of a manual email parameter, you can navigate to
Request Data’s Object Email.

You should now have the correct value saved to your new Customer record in Bubble.

Watch the following steps in


video format.

57
58
59
Note that you can now add a condition to this action so that it only runs when Request Data's type
is "customer.created".

60
If you go back to the Stripe page, you can see the response Bubble sent back to Stripe. Success!

The last thing you should remember to do is either add another webhook in Stripe with the same
endpoint URL without /initialize and/or edit the existing initialize URL to remove “/initialize”.

Remember, that was only there for the initializing step.

Once you initialize successfully, you no longer need it and that’s no longer the URL to the endpoint.

When you send a test webhook now, your workflow should run and whatever actions you set up will
fire immediately.

There are so many things you can do with webhooks, and if the service offers it, you should be able
to find all the possible events in the service’s documentation so you can really leverage all of the
data being sent.

***You must be on a paid plan to use API workflows properly.

Also, you must enable POST workflows under:

Settings > API to access the API Workflows page in your page list.

61
14. How To: Work With Maps
Bubble’s built in Google Map element is rather versatile. We’re going to walk through some of the
property and workflow options available once you’ve added a map to your page.

1. Markers
You can choose not to have markers for your map, but if you do, Bubble gives you the option to
have a single marker or a list of markers.

With a single marker, you’ll need to define the address, either by “hardcoding” into the property
editor, or by retrieving a dynamic value.

With a list of markers, you’ll need to define the data type your list of addresses is saved to, and
then indicate the field.

You have a few options for displaying title windows as well.

2. Dragging & Zooming


The map can be as interactive or as static as you want.

You can allow your users to have full control over moving around the map, or you can specify zoom
level and disable zooms so that it’s fixed on an address.

62
3. Custom Marker Icon
Upload your own marker icon to customize the map even more!

This can either be a direct upload from you or a dynamic image pulled from your database.

I recommend the Noun Project for finding really unique and high quality icons.

4. Map Type & Style


These are my favorite properties to play around with.

Remove the preset style on your map, and you’ll have access to the map type and style. Your map
type options are roadmap, hybrid, satellite, and terrain.

The style selection is more expansive, giving you plenty of color schemes to choose from so maps
will fit within your app’s branding.

5. Map Workflow Actions


Once you’ve added a map to your page, a new workflow event and a couple actions become
available to you.

You’ll be able to trigger an action “when a map’s marker is clicked,” and you’ll also be able to
change the markers, clear the markers, and adjust the zoom.

These don’t need to go together, but a good example is, “when a map’s marker is clicked >
adjust the zoom” so that it centers itself on the marker’s area.

You’ll also be able to use “This marker’s Thing” when creating workflow conditions or setting
values. 63
That’s it! Explore all your options so your maps are customized to your app and your users get a
seamless experience.

If you’re looking for more advanced map functionality, Google has a pretty extensive Map API that
can go a long way: Create polylines, generate directions, etc. If you’re comfortable with APIs, take
a look at the reference site.

15. How To: Login With Spotify


STEP 1: Create Your Developer Account
1. Go to developer.spotify.com/my-applications

2. Login with your Spotify account (create a new one if needed). This account is what will man-
age any apps you want to use with the Spotify API.

3. Create a new app. When your users sign in with their Spotify Credentials, they’ll see the App
name and description you create here as the application requesting authorization to access their
profiles.

64
4. After creating, scroll down to make note of your Client ID and Client Secret keys. You’ll
need these when we setup the plugin in Bubble. Also, treat these keys like passwords, so keep
them private!

5. Next, enter in your Redirect URIs. This should be the URL you’re initiating the Sign Up/Login
workflow from.

Once the user has logged in, they’ill be redirected back to this page, and if you want, you can
create a Navigation workflow to take the user elsewhere in your app. That navigation will only
happen if the login is successful.

Important Notes:

• When you initialize the API at the end of this setup, you’ll need to keep the debugger on, so be
sure to include “debug_mode=true” in your URL in the URI list. “https://coachingbubble.com/ver-
sion-test/spotify?debug_mode=true”

• Remember to include a URL that does not contain “version-test” for when your app is live.

• Don’t have any navigation actions in the workflow when you initialize the API. Complete the
setup all the way through initialization first.

65
6. Save your settings.

STEP 2: Set Up The API Call


1. If you haven’t already, install Bubble’s API Connector by going to the:

Plugins Tab > Add Plugins > API Connector

2.Add an API and we’ll start filling this out. Below is what your call settings should look like to
sign up/log in a user with Spotify and get a few profile details. We’ll define each piece next.

66
API Name
This is your own label for the API you’re using.

Authentication
This is the method of gaining access to the API you want.

OAuth is the preferred method for gaining access to user accounts on behalf of that user.

App ID & App Secret


This is your Client ID & Client Secret, which you will find in your app setting with the platform.

67
Dev. App ID & Dev Secret
Some platforms will give you separate keys to use while testing. If you have all four fields filled out,
Bubble will know which to use when you’re initiating workflows in development or live.

Scope
Scopes specify the data you want to access. Look at the platform’s documentation for what they
make available and how to compose them.

If you’re familiar with using social signups yourself, you may remember that you are always asked
permission before accepting. The permission box also details how your data will be used.

You can add multiple scopes to this field as well.

The screenshot above shows, “user-read-private,” but it could also say, “user-read-private user-
read-email user-read-birthdate.” You can find Spotify’s list of scopes here: https://developer.spotify.
com/web-api/using-scopes/

Authentication goes in the header (Bearer)


More often than not, you’ll keep this checked. It’s an OAuth standard for the platform to give you an
access token in exchange for your request.

68
This API uses the refresh token mechanism
You’ll also keep this checked in most cases, but refer to the platform’s API documentation to
confirm.

When the platform grants you authorization, you are given an access token (to access the API) and
a refresh token. A refresh token allows you to renew an access token at any time, which
essentially means you can log the user in without needing to re-authenticate them every time.

That permissions dialog won’t pop-up every time a user wants to log in. To fully disconnect, they’d
have to revoke your app’s access in their account settings on the platform in question.

The following three fields are URLs you need to look up in the platform’s documentation. Here are 2
places I suggest you always look first: the authorization reference and the endpoint
reference (specifically for users).

Here are Spotify’s:

https://developer.spotify.com/web-api/authorization-guide/
https://developer.spotify.com/web-api/endpoint-reference/

Login dialog redirect - to access the permissions dialog


https://accounts.spotify.com/authorize

Access token endpoint - to gain access to the API


https://accounts.spotify.com/api/token

User profile endpoint - to gain user profile data


https://api.spotify.com/v1/me

Call Name
You can have multiple calls per API so you don’t have to enter all the authorization info
every time. For example, one call is for getting the user’s email and another is for creating a
playlist. Name your call appropriately.

Use as Data
In our case, we’re wanting to obtain user account information to read inside our app: email address,
profile pictures, followers, age, etc. We want data.

Using as Action is for when we want to change the data via a POST workflow, like creating a
playlist.
69
JSON
This is the API framework Bubble is most compatible with. Review platform documentation to
confirm the API can be formatted in JSON.

GET
This is the HTTP Method for requesting data to read. In our example, we’ve entered the same User
Profile Endpoint, which gives us access to the user scopes entered above.

STEP 3: Initiate the Workflow


We’re going to follow the instructions outlined in the warning we currently see:

1. Created a button on the page to initiate this workflow.

2. When the button is clicked: Signup/login the user with a social network > Select your API

70
71
3. Preview your page (KEEP THE DEBUGGER ON) and click on the button to initiate the API.

You’ll see the login dialog from Spotify, and upon accepting, Bubble will display a success
message.

You’re done!

16. How To: Use “Is Development Version”


For Smarter Testing Workflows
Have you ever found yourself including “version-test” links in your workflows only to go back and
change them all to live links for when you deploy your app? Me too.

Have you ever hard-coded your own email address to test email workflows only to go back and
change it to a dynamic value or someone else’s email so your app runs properly? Guilty!

Stop the madness!

I’ve seen some users handle this live vs. test workflow by looking for “version-test” in the current
page’s URL, which does work.

However, with the addition of conditional actions, I want to share a power-combo of features to
make this a much easier process.

Plus, you’ll be less likely to let test values go into


your live workflows.

Is Development Version
This is at the very bottom of your dynamic options list.
See the screenshot to the right.

“Is development version” returns a yes or no value.

If the user is previewing the app (i.e. development mode, “version-test” in the URL), then Is
development version = yes. If the user is experiencing the live app, then Is development
version = no.

Create Conditional Workflows/Actions/Elements


72
Creating one condition for when development version is “yes” and one for when it’s “no” will allow
you to keep test and live workflows separate. This is super useful when you want test emails sent
to you instead of very important people that don’t need to read “testingggggggg” all the time.

It’s also a time-saver for when workflows contain links to your app; because your app links are
different in test vs. live, you won’t need to go back and forth to change them.

This is what the power-combo could look like:

Trigger the workflow in test, you get one email.

Trigger in live, you get the other. Magic!

This will help consolidate your testing flows in other areas as well.

Section 4: Bubble Best


Practices
73
17. How To: Perform “Error Handling” in
Bubble
This section shows you how to anticipate errors and customize any alerts so they both fit in with the
rest of your app’s tone and help users understand what their options are when they
encounter one.

The two core features we’ll go over are language settings for application messages and the
un-handled error workflow event.

Error handling deserves just as much attention as any other user experience feature.

Just like success alerts, loading screens, and empty states, proper communication with your users
when they encounter an error should help them take action to resolve efficiently.

Language Settings
Go to Settings > Languages to find a long list of application messages.

These are the messages that Bubble will display in the browser’s default alert window when
something related to core functionality occurs.

Examples include when a confirmation password field doesn’t match, when a required field is
missing, or when the app has been updated and the user needs to refresh, etc.

The left side of this settings page tells you the description of the event and the event’s CODE (IN_
PARENTHESES), which we can use in workflows.

The right side has the actual messages in editable input fields that you can change to make your
own.

This is your opportunity to provide actionable steps to let your users fix the problem. For example,
when a password doesn’t meet the password policy; if not already stated elsewhere on the page,
remind them in this message what the policy is.
74
When an un-handled error happens
You can override the default alert popup for these messages by creating a workflow.

You’ll need to specify which error the workflow should respond to, and then you can show an alert
element or anything else that you think is appropriate for the condition.

If you’ve created an un-handled error event workflow, and the condition is met, the default alert will
not display, so it’s up to you to display something to your users that provides actionable feedback if
necessary.

This event can be found under the general events list as pictured below.

Fill in the “and when” field and use the code found under the language settings to identify the error
for this workflow.

Next is an example of a workflow set to display an alert message (rather than the default browser
popup) when the current user’s session has expired and they’re no longer logged in.

75
Page Errors vs Element Errors
There are actually 2 versions of this error handling workflow available.

What was described above is for page-wide errors, but you can also apply the same concepts and
workflows to errors that happen specific to an element.

For example, when a button is clicked and its workflow encounters an error.

76
Below is a similar setup for when a user tries to log in with an email address that’s not in the
database. This workflow is for the Log In button set to sign in the user with email and password.

77
Note that the page-wide event would work for the button workflow, too, since it covers all
instances of the error condition.

The element event, on the other hand, is beneficial for handling an error for specific cases.

For example, Button A encounters an error and displays Alert A and Button B encounters the same
error and displays Alert B.

18. How To: Take 5 Helpful Shortcuts

1. App Search Tool


You can find the app search tool in the upper right corner of your editor. This thing is awesome for
big apps.

You can search elements and workflows from a few different angles like type, actions, and usage.

Below is a quick search on a project management app. “Tasks” in this app have due dates and that
field alone (among dozens) is seen in over 140 places!

When you select a search result, you’re taken to the exact page, tab view, and element or
workflow.

It saves a ton of time when trying to hunt down that one thing that does that one thing...

78
2. Right-Click Inputs to Start/Edit Workflows
The property editor for inputs doesn’t offer a “Start/Edit Workflow” button like it does with text and
buttons.

Inputs are workflow-capable, too! Whenever you want to create a workflow from the design view for
an input value change, you can just use the right-click menu and select “Start/Edit Workflow” at the
very bottom.

When you do this with an input, you’re taken to the workflow page and the “When an input’s value
is changed” workflow is set-up for you with that input already selected. It saves you a few clicks
compared to clicking on Workflow tab > Add Event > When Input is changed > Select Input.

This is a small convenience in the moment and a big win over time.

79
3. Make This Page New Index
This might not be used too often, but many new users seem to panic a little when they realize the
index page they’ve started creating is not actually the page they want for the index, a.k.a. your
home/start/default page for your app’s domain.

Before you go duplicating and deleting and creating more work for yourself, just right-click
anywhere on the page you do want to be the new index.

At the bottom of the menu is an option to “Make this page new index.” Select that, you’ll get a
prompt to confirm, and the switch is made! Your old index will be renamed to “old_index.”

4. Auto-Binding
Auto-binding allows you to automatically save an input’s value to a field in your database once the
input’s value has changed.

This works for all input forms. Type in text, make a dropdown selection, toggle an ionic button - and
as soon as you do, the input’s value will update a database field of your choosing.

Remember, the input needs to be a compatible type with the field you’re binding to.
For example, a dropdown selection of users should auto-bind to a field that is type User.

On the other hand, a radio button won’t be able to auto-bind to a field that is type file or image.

80
Some Bubblers feel hesitant with this feature because it’s
essentially an overwrite with no warning. Watch the YouTube Tutorial
for Auto-Binding
Auto-binding should be used when immediate overwriting is
expected and has low-impact on the data being overwritten.

When used in a safe environment, auto-binding is a fantastic


time-saver as it saves you from building workflows and cre-
ates “faster” user experiences.

Allowing users to change their name or quickly change yes/no values are pretty low-impact and
great to use with auto-binding. Other scenarios involving careful review of information, like when
emails or other workflows are involved, might need more consideration before implementing.

Also, you’ll need to define privacy roles in order to allow users to auto-bind. You’ll notice when you
first set up an input field to do this, a warning message will appear right below “field to modify” in
the editor, instructing you to set the roles.

5. Replace By Another Element


This is a great feature that allows you to change elements to other types, with minimal re-formatting
required. I’ve often converted fully formatted groups to popups, repeating groups to groups, date
pickers to regular inputs, etc.

Right-click on any element to see the option and then select the new element.

Say you’re converting a group that contains a dozen carefully formatted elements to a
repeating group. The data involved would be better served by this change.

In the same example, let’s say the original group was retained in full in the first cell of the repeating
group. You could continue, only needing to adjust data source and nothing more.

81
19. How To: Build Faster & Smarter With These
5 Best Practices
1. Label Everything!
Get in the habit of staying organized from the
moment you create a new app. This means
labeling every single element, naming your
workflows, creating folders for your workflows,
color coding, and caring for the nomenclature of
your database.

Come up with an app-wide system that makes


sense to you. (I like to keep the name
of the element type in front and then label
accordingly.)

For example:

• “Input Email Login”


• “Input Email Form”
• “RG [abbreviated for Repeating Group] Email
Subscribers”
• “Popup Email Confirmation”

All of these involve email addresses in some way,


but by keeping the element type in its name, you
can quickly rule out entire elements when looking
at the Element Tree & drill down from there. You’ll
thank yourself when your app gets huge.

2. Design, Structure, Repeat


Users often ask what the best approach is for building & designing an app.

Should you wireframe the design first or set up the database first?

It often leads to a chicken/egg situation. You can’t start working with data if you don’t have
elements on the page to initiate the workflow, but sometimes you can’t set up elements without
data to populate them.

82
A continuous cycle of designing & building, task by task, keeps you moving forward efficiently.

Design a little, structure a little, create some workflows, and repeat. It doesn’t even need to happen
in that order. The point is, you do enough to keep moving forward & learn what needs to happen
next. You’ll surprise yourself with how much you hadn’t anticipated until you test & iterate.

For example, if you’re building an inventory management system, start small with your core feature:
defining the inventory data.

This kickstart to the database will tell you how to design elements to work with that data, which may
then trigger new data fields to be created, all of which will inform relationships between data, then
more integrated interfaces, and bigger and bigger until you’ve built a system...until you’ve built an
app.

3. Create & Use Styles


This may require conditioning if you don’t typically work with design software, but taking the short
moment to create and use your styles as you design is a fantastic way to maintain consistency.

Apply full formatting in literally 2 clicks!

Nearly every element on your design page allows you to modify its appearance in the property
editor. I’m talking about fonts, colors, borders, shadows, etc.*

If an element comes with a preset style, you can remove and modify from there. Once you’ve
formatted the element to your liking, select the styles dropdown > create new > name it, and you’re
done!

Now that style will appear in the dropdown list to apply to elements of the same type. You can also
go back and edit the style by going to the Styles tab.

*You can’t save element dimensions as part of a style. Dimensions in general are also not a
dynamic property (i.e. You can’t set a condition on a shape to be dynamic dimensions).

4. Group Elements for Better Responsive Behavior


Always, always group your elements.

Groups are the containers that organize elements, so when the screen size changes, elements
within move together.

83
There are many different parameters to optimize responsive behavior, but knowing how to
group is a solid foundation.

5. Take Notes
Most users don’t take advantage of this
feature enough, but the notes feature (development
use only) is especially helpful for larger apps and
collaborating.

I once built a project management app centered


around to-do items, where each item could be any
one of about a dozen “types.”

It could be a task, sub-task, goal, project,


brainstorm, calendar event (which had further
sub-types), etc.

By taking notes, I was able to keep track of how I


had assigned types AND case sensitive spelling.

The bigger your app gets, the more you’ll need to


keep track of yourself.

84
20. How To: Copy & Paste in 10 Different Ways
Copy and paste is critical to efficient development. No one wants to waste time duplicating work.

Thankfully, Bubble offers over 10 copy and paste functions throughout the editor for duplicating
properties, elements, and workflows. Below is a breakdown of all your options.

1. Regular Copy and Paste


This is your standard function that behaves how you’d
expect in any application.

Copy elements or selected text and paste on the page to


create a duplicate.

PRO TIP: To paste an element inside a group or a re-


peating group cell, make sure to select the group first.

2. Copy and Paste with Workflows


This function allows you to copy elements and any
workflows associated with them so your pasted
elements still have workflows attached.

Workflows aren’t transferred with regular copy and


paste.

It’s helpful to use this when creating repetitive


interfaces.

Take tabbed groups as an example.

You can copy and paste with workflows to create a


new tab button, and instead of setting up the
workflows over and over again to change custom
states, all you have to do is edit the state value.

85
3. Copy and Paste Formatting
This function allows you to copy appearance properties of an element.

This is similar to using Styles, where you create appearance presets for elements. However, this is
useful for cases where you don’t need to create an entire style or just don’t have one created yet.

This is helpful when duplicating intricate formatting involving borders, colors, line spacing, etc.

4. Copy and Paste Style

This function is the exact same as selecting a Style from the Style dropdown in the property editor.

A Style is a preset that you can create in the Style tab of your editor or right within the property
editor.

With this form of copy/paste, you’re just taking the Style used in one element and telling
another element to use the same Style.

86
In order for this function to become
available, your element to copy must
first have a Style applied.

BONUS: You can also


copy and paste Styles
themselves in the Style
tab.

5. Copy and Paste


Conditional Formatting
Just like the copy/paste formatting function,
this function works with element conditions
only.

Copy all the conditions created in the Condi-


tions tab of the Property Editor for an ele-
ment and apply them to another element.

Keep in mind that this doesn’t add to any


existing conditions on the 2nd element, it
replaces all conditions defined in the 1st
element.

87
6. Copy/Paste Events...

Save mountains of time by right-clicking on an


event (in workflows) and selecting “copy” to
grab the entire event and it’s actions to
duplicate it.

Paste on top of any event and the new


event will show up right next to the original.

...and Actions!
You can also copy and paste individual
actions themselves.

7. Copy and Paste Expressions Part 1


Did you just spend an hour composing a complex expression?

Good job on getting it done. Now you can copy it completely and paste it in other dynamic fields
and not worry about missing a constraint!

This function becomes available to you when you right-click on an existing expression to replace
it or in the initial prompt to enter dynamic data.

See the next function for a very important distinction about where you click!

88
8. Copy and Paste Expressions Part 2
When you’re working with a text input
or elements that allow for multi-line
values (e.g. text, multi-line input, rich
text editor, HTML), right-click in the
text box itself to see multiple paste
options.

Because these multi-line values can


get long, Bubble has given you a way
to paste in between values and text
so you’re not completely overwriting
existing stuff.

Try it out so you know which to use!

9. Copy and Paste Data Types


This one’s a true miracle...When you copy and
paste a data type, you’re duplicating its name
and all its fields.

This is extremely handy when you have multiple


data types that are similar in structure and have
many fields that you won’t need to recreate.

10. Copy and Paste APIs in the API Connector


Did you know you can copy and paste APIs in the API Connector plugin?

Bonus: You can also “duplicate” calls within an API configuration.

89
Bonus Bonus: Copy your API and paste it right into the plugin editor to create
a plugin out of your great work!

Section 5: Bonus Tutorials

21. How To: Test Forms Faster With This Trick

Make things simple: Click a button to pre-fill your inputs with test data.

This still allows you to modify your fields if needed.

It also allows you to load the page as a live user would - without data to start - so you can test the
actual process of filling out the form (tab order, invalid alerts, etc.).

There are few ways you can set this up. Keep reading to decide which is right for you.

90
Load a Test Record
Create a test record that holds values for all
the possible fields, and set each input’s initial
content to that record’s data.

In the example here, all the inputs are inside a


group set to a User type.

The Load Test Data button triggers the Dis-


play Data action to send the Test record to that
group.

Use a Custom State to


Change Initial Content
Have the button set a custom yes/
no state. In the image to the right,
when the Load Test Data
button is clicked, a state called “Test
Mode” is set to “yes.”

A condition is then added to each


input, so when Test Mode is Yes, its
initial content will be whatever you
want.

This method is better if your inputs


aren’t necessarily saving directly to
a Thing’s field, so you have more
flexibility over what’s pre-loaded.

91
That’s it!

This pre-loading method has proven to be a huge time-saver for long forms that need to be tested
frequently. The setup is worth it!

(Because of this, I like to keep the Test Buttons available at all times.)

Another quick trick is to keep the button visible on your page in test mode but hidden in
live.

Un-check “This element is visible on page load” and add a condition: When ‘Is development
version is yes’ > this element is visible.

22. How To: Create Session Bookmarks


Many apps that have users moving through a sequence of steps (like a form or a quiz) can benefit
from a session bookmark feature.

This would allow the user to exit the sequence, either from having changed the page or closing the
browser, and come back right where they left off.

Let’s use the most natural environment for a bookmark as an example - a book! Say your app is
an e-reader and you want to let the user click through pages of content at their leisure, then come
back to a spot of their choosing or automate it to the last page they viewed.

Below is a breakdown of how to accomplish this feature.

Sample Structure
The context of this example is a 1x1 fixed repeating group, where each cell displays one “page” of
book content, all on a single Bubble app page.

The data structure could look something like this:

Book
• Title (text)
• Author (text)
• Pages (list of Pages)

Page
• Content (text)
• Page Number (number)
• Book (Book) 92
Bookmark
• Book (Book)
• Saved Page (number)
• Reader (User)

User
• Name (text)
• Books Reading (list of Books)
• Bookmarks (list of Bookmarks)

Method 1: Bookmark Button


With the button method, the user will click a button (inside the repeating group cell) to save the
page number to a number field on the User type. Whenever the page loads, the repeating group
will display the corresponding content in the cell.

1. When bookmark button is clicked > Make changes to [Bookmark] > Saved Page = Current Cell
Book Page’s Page Number.

2. When page is loaded > Go to Page* > Book Page repeating Group, Page = [Bookmark]’s Saved
Page.

[Bookmark] = Search for Bookmarks: first item, with constraint: Book = Current Page Book, Reader
= Current User

*Element Actions > Repeating Group

Method 2: Auto-Save
With the auto-save method, the app will update the bookmark every time the user advances the
repeating group. Assume there are next and previous buttons inside the repeating group cell (as
there would be for Method 1, as well).

1. When next or previous button is clicked > Show Next* > Make changes to [Bookmark] > Saved
Page = Current Cell Book Page’s Page Number.

2. When page is loaded > Go to Page* > Book Page repeating Group, Page = [Bookmark]’s Saved
Page.

[Bookmark] = Search for Bookmarks: first item, with constraint: Book = Current Page Book, Reader
= Current User

*Element Actions > Repeating Group

93
A Couple Extras
1. Instead of searching for the bookmark with the “Do a search for” function, you can use
the Bookmark list field under the User type and filter the field down to the right Bookmark.

For example, make a change to Current User’s Bookmarks :filtered :first item.

Keep in mind, this method would force the retrieval of the bookmark to happen on the client side
(due to the :filtered modifier), whereas Search constraints all happen on the server side.

However, if your app’s context only has 1 bookmark per user, this is a more ideal method of
retrieving the bookmark because it’s a more direct path on the server.

2. A great use case for this feature is saving a user’s “view” preference.

For example, your app is a 10-tabbed dashboard. Let your user indicate which tab they want to see
first every time they load the app.

Have a button inside each tab group, and when clicked, save that tab’s number to the user’s
“Saved Tab” number field. When the page is loaded, show the group that corresponds with that
field. This can be done with custom states and/or visibility conditions on the groups.

23. How To: Show a Password During Signup


or Login

Create 2 Groups
• Set the type of content to “text”
• Leave the data sources blank
• 2nd Group only: Uncheck “This element is visible on page load”
• Place Group on top of Group 1. You can toggle this group’s visibility in the elements tree.

94
Input 1
• Add an input field inside Group 1
• Set the Content format to “Password”
• Set Initial content to Parent Group’s text
• Workflow: When this input’s value is changed > Reset Group 2 > Display data in Group 2: data
to display = this input’s value.

Input 2
• Add another input field with same dimensions inside Group 2
• Set the Content format to “Text”
• Set Initial content to Parent Group’s text
• Set Background style to “full color” (white, in this example)
• Workflow: When this input’s value is changed > Reset Group 1 > Display data in Group 1: data
to display = this input’s value

Add Eye icon from Ionic Elements plugin


• Add a condition to this icon: When Group 2 is visible > Change icon to “eye-disabled”
• Workflow: When icon is clicked > toggle Group 2

Here’s what you just created:


By default, users will be able to type a value into the regular password input.

Clicking on the eye icon toggles visibility of the group that contains the text input, which covers the
password group.

Both inputs are set up to reset and update the other group with the input value. This allows the user
to start a password with one input and continue in the other.

Your signup workflow should only use the concealed input (type = password) for the password field.

Watch a video tutorial for


this technique on Coaching
Bubble’s YouTube channel.

95
24. How To: Create Groups That Collapse &
Expand
This is a fun one that can be used in so many different parts of a web app.

In the example below, we have a collapsible group within a repeating group cell.

This is a group feature, so you


can do this wherever you have
a group on the page. There are
some rules though, which we’ll
go over below.

Check out the


structure. Inside the
repeating group cell
are the following
elements:
1. A single group that contains
user photo, user name, and the
collapsing group. This single
group exists so we can create
an outset shadow to make each cell looks like a separate “card.”

2. The photo and user name is straightforward; they’re image and text elements.

3. The 2nd group within is where the magic happens. This group contains another text element to
display the Latin text.

96
To make a group collapse, check Collapse this element’s height when hidden in the property
editor under the Appearance tab.

This collapses the height of element to 0px when it’s not visible and allows any elements below it to
shift up and take its place (again, only while it’s not visible).

If you have other elements within the same horizontal area of the page that the collapsible
group is in, the height won’t collapse because those other things are in the way.

Then, check Animate the collapse operation in order to create the sliding effect.

You also have the option to use a fade in/fade out effect. Without this, the group will just pop in and
out instantly with no animation to it.

Next, depending on your use case, you might


want to turn off the group’s default visibility
by un-checking This element is visible on
page load.

This is so when the user first sees the page,


the group is hidden and everything starts as
collapsed.

Finally, we need a way to toggle the group’s


visibility so that, in turn, it toggles collapsing
and expanding the group height.

The example is set this up with a single work-


flow: when the user’s name text is clicked
> toggle collapsing group.

This action can be found under the element


action options. All this does is switch be-
tween visibility states for an element. If visi-
ble, it’ll toggle it to not visible, and vice versa.

97
This feature is awesome for things
like:
Watch a video tutorial for
• Menus and sub-menus. this technique on Coaching
• Conversation threads.
Bubble’s YouTube channel.

• Extra details within a repeating group like the previous


example.

• Dynamic forms (check out Coaching Bubble’s Night &


Day Dashboard Template to see this in action within
the signup/login form).

• Generally keeping pages clean and condensed.

25. How To: Work With the Dropdown Element

This section covers the dropdown element and shows you how to build a few different
web-standard features with it: menu navigation, form entry, and a layered selection to display data
(ex: category & sub-category).

These examples will make use of both static and dynamic options, so if you’re lost on how to make
a dropdown element do things with your Bubble data, read on!

Dropdown Properties
The dropdown is an input element
that allows users to see a list of
options. These options can be static
or dynamic values.

Static options are ones you enter in yourself, one


per line, and they will be the same list of options
for every user engaging with the element.

This is great for menu navigations or simply any


list that doesn’t need database information to
display choices.

98
Dynamic options are created from a list in your database: users, companies, texts, numbers, etc.

Set the data type, define the list, and select the field within the type to serve as the caption.

In the example below, the dropdown should display a list of managers from the database.

The type is user. The choice source is a search of users with a constraint: Manager field = yes.

The option caption is the Name field so the actual text you see in the dropdown displays the
managers by their name.

99
Create a Menu Dropdown to Navigate to Other Pages
The workflows for creating page navigation from a dropdown selection are painless and easy in
Bubble, and below are two ways to do this:

1. When an input’s value is changed (select your dropdown input), and this dropdown’s value is
ENTER STATIC VALUE...

2. Go to page - select the page for this value.

This workflow will change the page as soon as the selection is made.

If you want to use a button to trigger the workflow:


1. When an element is clicked (select button element) and when Dropdown’s value is ENTER
STATIC VALUE...

2. Go to page - select the page for this value.

Remember, any time you’re setting and referencing static values, they’re case sensitive!

100
Using a Dropdown in a Form
Saving the value of a dropdown selection is just as easy as the page navigation. We’ll use dynamic
options in this example.

The form is a registration for a 5k running event. Individuals can register as a part of a team, and
we want them to choose a team from a dropdown (these teams are in my database already as a
custom data type).

After setting up the dropdown similar to the manager example above, we’ll create the following
workflow off a button click to add a new Runner (another data type).

1. When Button Register is clicked > Create a new Runner

2. Set each field to equal the values of each of the inputs, including Team.

3. The Team field under Runner is type Team and is not a list - that is, it expects 1 team value only.

The value of the dropdown selection is a single entry as well, so the value of the Team field under
Runner is equal to the dropdown value. The selected team will be saved for the new Runner entry
in the Team field.

Conditional Dropdowns with Dynamic Values


Now we’re going to build a Category > Subcategory selection using 2 dropdowns to then display
some data.

Here’s what the result will look like:

101
102
The data source for the Sub-Category dropdown is a list field within the Category data type.
Here’s the data structure:

Category: Data Type


• Title: text
• List of Sub-Categories: Sub-Categories
(Entries include Food, Furniture, Vehicles, Colors)

Sub-Category: Data Type


• Title: text
• Image: image
(Entries include Pasta, Ice Cream, Salad, SUV, Bus, Racecar, Purple, Yellow, Green, Chair, Sofa,
Desk)

Through workflows outside the scope of this walkthrough, we can add specific Sub-Categories to
their relevant Category’s List of Sub-Categories field.

Example: Pasta, Ice Cream, Salad were all added to the Food entry.

We can also add images to the Image field of each Sub-Category and set a condition on the
Sub-Category dropdown element to only be visible when the Category’s dropdown has a value.

Since the data source of the Sub-Category dropdown is dependent on the Category’s value, it will
change whenever you change the Category selection. In turn, the image’s source is dependent on
the Sub-Category’s value.

Below are the setups for the dropdown elements and the image element.

Note: No workflows are involved to create this data display!

103
Take dropdowns to the next level!
Here are some quick ideas for how to make dropdowns do crazy powerful things for your app:

• Filter options: use static values to allow your users to filter a repeating group by their selection.
When dropdown value is “A-Z” > display list with an ascending sort

• Select user settings and save that value to the user to create customization. When dropdown
value is Spanish > set user’s language field = Spanish

• Select available booking times by choosing dynamic date values saved in your database. Ex:
Professional’s list of available dates. When Book Appointment button is clicked > Create new
Request: Requested Time (type date) = dropdown’s value.

26. How To: Copy a List of Things (2 Cases)

Copy a list of things allows you to duplicate up to 100 database items in one workflow action.

Below are 2 use cases for the feature, implemented in Bubble apps:

104
“Order Again” Feature
Example: You can use Copy a list of things
for a food delivery app that allows cus-
tomers to re-order the same meal items
they’ve
purchased before.

This would apply to any kind of inventory


scenario where creating a new purchase
order can pull from a previous order.

Some real-life examples include a custom


pizza order, a recurring business supply
fulfillment, or even frequent travel
arrangements.

By copying a list of data items, you can save your user the time of filling out an order form all over
again while still giving them the flexibility to modify the new order.

In the food app example, customers might create Cart Items and add them to their Order.

The app might offer Customers the chance to look up previous Orders. The customer could also
click a button to place that same order again. Behind the scenes, that button click would do the
following:

1. Create a new order


2. Copy a list of Things, where the list is the previous order’s list of Cart Items
3. Make a change to the Result of Step 1 (new order): Cart Items (list field) add list Result of Step 2

Now the customer can review their new order, which contains a duplicate list of Cart Items.

Changing quantity, removing, or adding would follow the same checkout flow since all data is
independent of the original order.

Task Templates Feaure


This copy feature can also be used in a project management app, for example.

Users could create a task with a preset list of sub-tasks that could be modified independently.
105
For example: one user, a business management consultant, might require the same 10 tasks for
every new client: sign contract, schedule kickoff meeting, review budget, etc.

With that in mind, you could create a system where users would set template tasks and sub-tasks.
The consultant could create a task called New Client and her list of sub-tasks (sign contract, etc.).
These would both be used as the source for copies.

Now, every time she gains a new client, she’d click a button, which would launch the following
workflow sequence:

1. Create a new Task: field values = the same as the template task
2. Copy a list of things: list = template list of sub-tasks
3. Make a change to Result of Step 1: sub-tasks add list Result of Step 2

Both task and sub-tasks could now have their own notes, due dates, and other independent values.

You can apply the same principle to other recurring data with unchanging list items like monthly
reports with line items.

Things to Note
1. Bubble limits the number of items to copy to 100 entries (at the time of writing).

2. If the Thing being copied contains a list field that’s a custom type, the value of that list will be the
same as the original. In other words, lists within the Thing to copy won’t be duplicated as well.

Here’s an example:

Line Item
• Title (text)
• Description (text)
• Quantity (number)
• Suppliers (list of Suppliers)

Copying a list of Line Items won’t duplicate the list of Suppliers in your database. The new Line
Items’ Suppliers value will be the same as each of their originating Line Item.

106
t

Section 6: Expanded Lessons

27. How To: Register Groups & Teams on Your


App
Whether you have co-workers joining a project management account or a group of friends plan-
ning a trip together, team registration will look different than individual registration on your app. And
whether your app involves team/group registration right now or not, it’s a great method to have in
your toolbox whenever you need to use it.

Individual user registration looks something like this:

1. Enter an email address


2. Enter a password
3. Confirm the password (optional)
4. Enter user details, like first and last name (optional)
5. Click on a confirmation email link (optional)

It’s a pretty standard and straight forward flow when you’re signing up one person at a time.

But what happens when that person is part of an organization or group who all need to work togeth-
er on your app?

You can create a SAAS product with Bubble’s sub-app system; separating organizations is already
done for you when users register to each sub app.

But what if sub-apps aren’t a great fit for you?

Take project management apps for instance: Typically, an organization is created, and team mem-
bers sign up to work together on projects created for that organization only.

107
Then you have to ask yourself:

• Who creates the organization?


• How do you prevent a team member from creating a duplicate organization if it already exists?
• Should there be different sign up forms?
• If your app is a paid product, who pays for it? Is there a primary contact?
• How do you handle user roles from the moment a team member joins?
• Who can create projects? Who shouldn’t be allowed to edit?
• Should team members be invited, or are they free to join an organization on their own?

There’s a lot to keep in mind, and these are just some of the first questions you need to answer
when creating a team registration.

If you’re scratching your head wondering what’s best for your team registration, check out the
example flows outlined for you below. (PS: I’ve implemented all of these in various apps with great
results!) Use these as a starting point, and then modify them for your own app’s needs.

1. Invite Team Members


With this method, one user creates the organization and sends email invites to other people who
may or may not sign up under the same organization.

1. User A signs up and creates an Organization and is set as the Primary Contact for that Organiza-
tion.

2. Under a settings interface, User A will send email invitations to multiple email addresses (their
team members).

3. The email link will contain URL parameters for the user’s email address as well as the orga-
nization. It will take them to the same user registration page User A used. E.g. myapp.com/sig-
nup?member=john@company.com&team=google

4. Since you can extract the email address from the URL, you can pre-fill the email field for the
team member. They’ll enter a password, and in your signup action, you can set the Organization for
them as well - no need to create a new one.

5. Take them straight to their dashboard, where they can see their Organization’s data + other team
members.

Alternate method: User A can also send team members a more generic link that doesn’t contain a
user email, but is still unique to the organization so anyone who receives the link can sign up to the
team by entering their own email address.
108
2. Create Team Members
With this method, the primary contact creates user accounts for other people before sending an
email invite.

1. User A signs up, creates an Organization, and is set as the Primary Contact for that Organiza-
tion.

2. User A creates an account for someone else with User B’s email address. They also assign a
temporary password and send an email to User B with a link to the sign up page and the temporary
password. They repeat this for Users C, D, E, and so on.

3. Since Users B, C, D, E are now in the Bubble database, User A can save some kind of user role
to a custom field so these team members can login with permissions already set.

For example: User B’s Role field = “Manager” and is allowed to create projects; User C’s Role field
= “Contributor” and can only edit projects assigned to them.

3. Team Members Join by Lookup


With this method, all potential users search for their Organization first before signing up.

1. User A searches for their Organization. If there are no results, they sign up, create an Organiza-
tion, and are set as the Primary Contact for that Organization.

2a. Users B, C, D, E see that their Organization already exists (created by User A), and proceed
through a signup flow that has the Organization already set for them.

2b. Users B, C, D, E see that their Organization already exists (created by User A), and request ac-
cess to the Organization by sending an email to User A first. They’ll be created as Users, but won’t
be allowed to log in until User A has approved them. User A will see a list of requests in their app’s
dashboard.

Key points to remember


1. If your app is a paid product, make sure there’s always one contact responsible for billing.

2. If your app is a paid subscription, you can limit how many team members are added before up-
grading. You can also charge extra per user. Bubble’s Stripe plugin allows you to create Subscrip-
tion Items, which is great for this.
109
3. Use Privacy Roles, Workflow Conditions, and Element Conditions to create various user permis-
sions for team members.

4. Make sure all data has a reference back to the overall Organization it falls under so users don’t
see data from other Organizations. For example, constrain a search for Assignments or Projects by
an “Organization” field, where this field = the Current User’s Organization.

5. Make your data searches a bit easier by having a “Primary Contact” field under Organizations
that equals the User who first created it, as well as a “List of Team Members” field which is a list of
all other related Users.

6. If you use the lookup method, make sure your search results are descriptive enough for the user
to feel confident they’re choosing (or not finding) their organization. Include a logo or a city along
with the name in case there are organizations with the same name.

Putting it to work
There isn’t one single best method for creating group or team registrations. There are tons of op-
tions. The key is finding which technique is right for you and your app.

The absolute best way to decide on the right fit is to try it out! Walk through the process of each
registration method and see which will fit your needs most directly.

At the end of the day, you want to implement a registration process that’s easy for both you and
your team/group/users.

28. How To: Track Mileage & Distance Between


Locations
So many Bubblers use their apps for tracking inventory, logging deliveries, or even monitoring mile-
age. Whether it’s for food delivery, home based appointments, trip planners, or even companies
who deal with product shipping on a regular basis, these elements are vital to track.

A recent trend with apps like these is building out features that allow you to plan routes by showing
a list of locations for the day. Not only that, most Bubblers want to be able to show the distance
between each location, too.
110
Whether you’re planning for yourself, a user, or a driver (or even if you just need to help keep track
of mileage runs on a leg-by-leg basis), this is something you can do in Bubble with some creative
logic.

This feature is useful for apps that do the following:


• Pickup and delivery routes for supplies or products
• Location-based appointments like a home cleaning service
• Trip planning to calculate road trip or even air miles between stops
• Mileage logging for tax reasons

Follow this example to build out the feature:


Below is a repeating group of Delivery records from my Bubble database.

The Delivery data type has a Location field (type = geographic address). I’ve added a few records
and saved different New York addresses to each Delivery so we can get varying distances.

You can see they all have different zip codes:

111
I’ve also added an input (content type = geographic address) at the top so the user can type in an
Origin Address. That way, the first delivery can be measured from the starting point.

The secret is in a hidden input field placed inside the cell


The purpose of this input is to assist in the expression that displays the distance text (we’ll get there
in a minute).

Set the content type of the input to integer and the initial content to current cell’s index - 1.

Make sure the input is not visible on page load.

You don’t ever need to see it, but you can still use the value of the initial content even when it’s
hidden.

112
What on earth does this do?
The current cell’s index refers to each cell’s place in the list. The first item’s index will be 1, the
second item’s index will be 2, and so on.

The initial content here is subtracting the index by 1.

This gives you a way to identify the previous cell.

Ultimately, you’ll be calculating the distance between the current cell’s delivery location and the
previous delivery location.

Take a look at the distance text:

Don’t worry, we’ll break this down. Here’s the full expression:

Current cell’s Delivery’s Location: distance from RepeatingGroup Delivery’s List of Deliv-
erys: item # Input Hidden’s value’s Location:formatted as [number] miles

Here’s the breakdown:

113
Current cell’s Delivery’s Location

This is just the current delivery’s location field. The value of this alone is the same as our other text,
which just displays the delivery address.

:distance from

Because we’ve started this with a location value, our modifier options are going to be for manip-
ulating addresses. “Distance from” will open up the secondary window you see on the left (image
above), where you’ll enter the next part.

RepeatingGroup Delivery’s List of Deliverys: item # Input Hidden Value’s Location

We have the first location address (current cell). Now we want the address to calculate distance
from, which will be the previous cell’s location value.

To retrieve this value, we’ll take a specific item from the repeating group’s full list of Deliveries.

With item #, you can specify which item in the list you want. This is where the hidden input comes
in. That input’s initial content is the item number we’re looking for (current cell’s index - 1). So now
we’ve identified the previous cell’s Delivery and its Location value.

:formatted as [number]

Back in the main expression area, this was added to cut off the resulting calculation to 2 decimals.
When you select this portion of the expression, you’ll get a secondary window that lets you define
how the number should be formatted.

miles

This is just plain text typed in!

Don’t forget to add a condition to the distance text.


If the current cell’s index is 1, your distance calculation is going to use the value of the origin input
instead of a previous cell (because there is no previous cell).

In your app, this origin could be the current user’s location.

114
How can this help you?
This feature can add a whole new level of functionality to your app. And while it might seem a little
tricky to implement, by following these steps, you can track mileage, deliveries, and more right
away. Bubble is so versatile, and it’s niche uses like these that help show exactly how you can use
it to expand your app.

29. How To: Avoid a Null Search Mistake


How frustrated would you be if users on your app started receiving or seeing information they
shouldn’t? What if your app accidentally triggered emails to the wrong people, or showed certain
users inaccurate information during searches?

At best, it might be irritating. At worst, you could end up with damage control on your hands. Users
on your app should see relevant, accurate, and safe information. If they don’t, you could have a
huge mess on your hands.
115
Here’s the thing: Bubble handles a specific kind of database search in a tricky way that could have
unfortunate consequences if not correctly understood. This method for running searches is being
looked at by Bubble, and they’re working on changing it so you don’t ever run the risk of inaccurate
search results again.

For now, though, the job of making sure the search results you want to show up are the ones that
do is up to you, which is what this tutorial is all about.

Searching your database with a possible null result


If you perform a search of your database with any kind of constraint and that constraint returns a
“null” result (empty), then Bubble does not return a null result for the search overall.

Instead, it ignores the constraint and performs the search without it.

In some cases, you might want that. In other cases, it could lead to inaccurate results.

Understanding the issue


Let’s say you have a data type for Project, which contains some relevant fields like Name, Due
Date, and Team Members (which is a list of Users).

Each User also has a field for Project, so we have a one-to-one relationship between User and
Project, too.

Project
• Name (text)
• Due Date (date)
• Team Members (list of Users)

User
• Name (text)
• Project (Project)

Now let’s say you select a User from a dropdown and see a list of all Team Members based on the
User’s Project.

Take a look at the search expression below:

116
Search for Users with constraint: Project = Dropdown User’s Project.

Here’s what happens: If the selected User is not part of a Project (meaning the Project field is
empty), then the Search for Users is going to ignore the Project constraint and return all Users in
the database.

With this particular search, there are no other constraints, so all Users are returned. If there were
other constraints, it would still apply those if possible. For example, if there was another constraint
for User’s Creation Date, it would return all Users whose Creation Date falls within the date con-
straint (but still ignore the Project constraint).

Bubble is saying, “Oh, it looks like there are no Projects meeting your full criteria, here are your
results are without it.”

Think about the possible consequences of an ignored constraint!

• Emails going out to the wrong people


• Data being seen by users who shouldn’t see it
• Inaccurate information being provided to your users
• To avoid all this, we need Bubble to say, “It looks like there are no Projects that meet your full
criteria, so we don’t have any results for you.”

Ignoring the constraint might be your desired behavior. Maybe you want to perform searches with
optional filters. Right now, though, we can’t tell Bubble which we’d prefer. Because of this, you
should know how to adjust the search to have null results taken into account.

How to control the search results:


1. Add a condition to account for the null result.

Using the example above (Search for Users with constraint: Project = Dropdown User’s Project),
you could add an “Only When” statement: Only when Dropdown User’s Project is not empty.

2. Change your data source.

Instead of “Search for Users”, approach it like this: Dropdown User’s Project’s Team Members.

If the User’s Project field is empty, there’s no where else for Bubble to go, so this won’t return any
other Users from the database.

You can also combine #1 and #2 to use a different data source under the condition “when Project is
empty.”
117
3. Create Privacy Roles.

If your data structure allows, you can prevent search access to unrelated records by creating the
appropriate privacy roles under Data > Privacy tabs in your editor.

For example, When Current User’s Project is not This User’s Project > Uncheck “Find this in
Searches”.

However you end up handling your search results, try to take into account all possible scenarios.
Take yourself through all the logic paths you can think of, test them, and make sure you’re getting
the results you want. By walking through these steps, you could be avoiding hours, days, or much
more of damage control.

30. How To: Create App Update Notifications


Over time, your app will grow and evolve. What you’re building or producing now will be improved
in the future because you’ll learn from experiences (yours and users’) along the way.

As you build and iterate, you want your current users or team to know when updates have been
added.

Take the “Gift Box” feature in Bubble, for example. When Bubble introduces a new feature to the
platform, the gift box on your menu bar indicates a change has been made. You can then click on
the gift box and learn about the new feature.

Tons of apps integrate this type of “What’s New” tab or icon - we’ll call it a “Changelog” (although
it’s sometimes referred to other ways, like “release notes”). Changelogs work as simple, app-wide
notification systems that subtly alert users when a new update is released. If you don’t already have
a way to notify users of updates (or if you need a more efficient way), try using this lesson to create
a changelog in your own app.

How to build a changelog


Below is a sample structure for a data type called Update. It allows you to save a title, description,
publication date, image (if applicable), and type field (we’ll get to that in a moment). Using the data
structure, create a page in your app that’s just for new updates.

118
• Title (text)
• Description (text)
• Published (date)
• Image (image)
• Type (text)

In addition to this data type, add a new field to the User type called Update Seen that is a yes/no
value.

Your workflow should contain these two actions: Create a new Update (set all fields) > Make a
change to all Users (Update Seen = No).

Then add a way for users to see that a new update is available. It can be an icon, text, small ban-
ner at the top of the page, or whatever else makes sense for your app. These generally work well
when they’re kept relatively subtle.

Use the Update Seen field to determine when the update should be visible to the user.

For example, Bubble uses a bright gift box icon that becomes visible whenever there’s a new fea-
ture available.

You can achieve the same by adding a condition to your icon or text: When current user’s “Update
Seen” is “no” > This element is visible.

Some apps always have their release


notes available as part of their normal
menus - the appearance only changes
when something new is added.

Regardless of how you introduce updates


to your users, try to let them make the de-
cision to learn about it on their own (rather
than forcing them) by allowing them to click
on an icon, element, etc.

Next, create a workflow that presents the


"Update Icon" Example, Source: Steven Fabre
list of Updates to the user once they’ve
via Dribbble
clicked on the conditional text or icon.
This can be via a popup or even by taking
them to a new page.

119
In the workflow, include: Make a change to current user: Update Seen = yes. This resets the us-
er’s status so the notification condition will no longer apply to them.

However you present the updates, keep them neat and organized. A repeating group displaying all
the fields mentioned above will do the trick.

Open-ended Type Field


An open-ended data type field is useful for organizing your updates by categories like:

• Announcement
• New Feature
• Bug Fix

You can have different icons or coloring for each Type so users know exactly what kind of an
update each one is.

Pro Tip

If you’re worried about the amount of processing power it will take to mass up-
date all your users’ yes/no field, make sure you only update users whose Seen
field is still “yes.”

The principals outlined here can be also applied to messaging features, general notification sys-
tems, or activity feeds.

For reference, here’s an example of Bubble’s changelog/release notes page: https://bubble.is/re-


lease_notes

120
31. How To: Make Your Searchbox Element
Funtion in 2 Ways
The SearchBox Element
Bubble’s SearchBox element gives your users an auto-complete function.

Here’s an example:

This element is the perfect feature to help your


users fill out forms.

Let’s say your app has a list of Companies, and


new users need to join a Company in order to
sign up. The signup form would contain this
SearchBox element to let the user find the
Company they want. Easy, right?

Now we’ll take it a step further. Let’s say the


user’s chosen company doesn’t exist in your
app’s database yet.

You can use the same element to let your


User create a new Company from whatever
they type into the SearchBox.

Take a look at the screenshot to the right. By


checking Allow entries not in list, you can en-
able this SearchBox element to act as a regular
input, so if there’s no Company value, you can
save the typed text as a Company name.
121
Follow these steps to create a two-way signup flow in the
workflows:
Create two separate events for the Sign Up Button click.

Each event will have a different condition: Only when SearchBox value is not empty and Only
when SearchBox typed text is not empty.

You can also do this under one event with the conditions on actions, but for this example, we’ll split
the actions under separate events to keep things crystal clear.

If the SearchBox has a value, meaning if a Company has


been selected from the auto-complete list:
1. Sign up the user
2. Set the Company field (type Company) to equal the value of the SearchBox.

122
If the Searchbox’s typed text is not empty, meaning there is
no Company value:

1. Create a new Company


2. Set the Name field (type text)
to equal the typed text of the
SearchBox

3. Sign up the user

4. Set the Company field to equal the result of step 1 (the newly created Company)

And that’s it! This is a great technique for giving a single element dual-use, which means cleaner
workflows for you, and a cleaner experience for your users. You might require additional actions to
make this process fit with your app’s flow, but this should get you going in the right direction. Also,
make sure to let your users know this kind of functionality is in place so they’re not lost...they’ll ap-
preciate it!

123
32. How To: Search Using “AND” “OR”
Constraints
Do you ever find yourself having to tinker with search constraints to figure out how to pull the right
results from your database?

Along with tons of other Bubblers, I know I’ve been there (with a slowly rising level of frustration)...

While it might seem tedious to pull up the right search results, though, there’s a simple way to think
about it.

Take “AND” constraints versus “OR” constraints, for


example:
• A search with AND constraints requires results to meet all constraints.
• A search with OR constraints requires results to meet only one of them.

Whenever you add multiple constraints to a search, you’re creating AND constraints. Meaning,
constraint 1 must be true AND constraint 2 must be true AND constraint 3 must be true. See the
screenshot below:

This example is:

Search for Users

Constraint 1: City = Paris

Constraint 2: Age > 18

Constraint 3: Profile Picture


is not empty

This search will return all


users who are in Paris AND
over 18 years old AND
have a profile picture.

124
If you want to search for Users who meet any one of these constraints (“OR” constraints), you’ll
need to merge multiple searches, each with a different constraint.

Simply select the “merged with” operator after each search. This will combine the results of each
search into one list. (See the screenshot below)

The example to the left shows:

Search for Users merged with Search for Us-


ers merged with Search for Users

Search 1’s only constraint: City = Paris

Search 2’s only constraint: Age > 18

Search3’s only constraint: Profile Picture is not


empty

This search will return users who are in Paris


OR over 18 years old OR have a profile pic-
ture. The user does not need meet all three
constraints.

Important note: With merged searches,


database entries still need to meet at least
one of the constraints to come up as a
result. Merged searches don’t make the
constraints optional as a whole.

125
33. How To: Create a Breadcrumb Navigation
Feature
What are breadcrumbs?
They’re quick navigation tools to help users orient themselves. You’ve undoubtedly seen and used
them on countless sites and apps.

Things like:

Home > Blog > Post

Category > Product

Search > Result

In this lesson, we’re going to go over exactly how to create a breadcrumb feature on your app, us-
ing only a single text element.

To give you a working example, we’ll walk through the steps of creating a Blog Post hierarchy
breadcrumb. In this example, if a user were to land on a specific Post’s page, they’d see the bread-
crumb for Home > Blog > Post.

1. Let’s start out by typing how you want the breadcrumb text
to look.
In this example, we have
Home > Blog > Post Title

126
2. For the Home
text, we’ll link to the
Index page.
1. Open up the rich text editor
2. Highlight “Home” and click
on the Insert Link icon.
3. Enter in your app’s home
page URL

You’ll see that the BBCode for a link is inserted into your text element, exposing how to structure
the link with label.

3. We’ll do the same for the “Blog” text. The Blog page also
happens to be a static page.

127
4. The “Post Title” text, however, needs to be dynamic so it
displays the title for the Current Page Post (this is assuming
this text element is on a page with Content Type set to a Post
data type).
The easiest way to do dynamic values is to type in the URL BBcode and replace the part of the
URL and label that needs to be dynamic.

Now each Post page will display a different title at the end of the breadcrumbs. Depending on your
preferences, you might not even want the page you’re on to be linked. Having the last link just be
dynamic text works, too!

Pro Tip

Your app’s test and live pages have different URLs, so I suggest adding a con-
dition to this text element so you can use test URLs in development mode and
live URLs in production. Since the text element in this example is defaulting to
the live links, we would need to add a condition for when Is Development Ver-
sion is “yes” and change the URLs to include “version-test”:

See Below

128
Make sure your text element is wide enough so dynamic values don’t force text to be cut off or
moved to a second line. By having all this in one text element, you won’t have to worry about dy-
namic values in the middle of the breadcrumbs creating spacing issues.

129
For more tutorials, tips, and lessons,
head over to CoachingBubble.com

The information provided within this eBook is for general informational purposes only. While
Coaching Bubble tries to keep the information up-to-date and correct, there are no representa-
tions or warranties, express or implied, about the completeness, accuracy, reliability, suitability
or availability with respect to the information, products, services, or related graphics contained in
this eBook for any purpose. Any use of this information is at your own risk. The methods describe
within this eBook are the author’s personal thoughts. They are not intended to be a definitive set of
instructions for this project. You may discover there are other methods and materials to accomplish
the same end result.

130
131

You might also like