> For the complete documentation index, see [llms.txt](https://manual.bubble.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://manual.bubble.io/help-guides/logic/workflows/events/frontend-events.md).

# Frontend events

Frontend events start a workflow when something happens on a page. Learn how element and general events work, with examples you can build yourself.

Frontend events are the triggers that happen on a **page**, or on a view[^1] in a native mobile app. A user often sets them off, by clicking a button or changing an input, but they can also fire on their own, like when a page finishes loading. They only trigger while the page is open.

This article walks through the two main groups of frontend events, element events and general events, with examples you can build yourself. For an overview of how frontend events differ from backend events, see [Events](/help-guides/logic/workflows/events.md).

***

## Element events

The most basic triggers come from users interacting with an element[^2]. For example, a user clicks a *Save* button, which starts a set of actions that saves what they've typed to the database.

Element events include:

| Event                                        | When it triggers                                                                                        |
| -------------------------------------------- | ------------------------------------------------------------------------------------------------------- |
| *An element is clicked*                      | The user clicks an element, such as a button or an icon.                                                |
| *An input's value is changed*                | The value of an [input form](#user-content-fn-3)[^3] is updated.                                        |
| *An element has an error running a workflow* | A workflow started by that element runs into an error. See [catching errors](#example-catching-errors). |

Some elements, including elements from plugins, add their own events to this list.

**Reference:** [Element events](/core-resources/events/element-events.md)

### Example: button click

Let's start with one of the most common events an app can have: responding to a button click.

{% hint style="info" %}
You can press **Cmd + K** on a Mac or **Ctrl + K** on Windows to quickly add a workflow to an element.
{% endhint %}

{% stepper %}
{% step %}

#### Add a button

Add a **Button** element to the page. In the example below, we've labeled it *Click me*.
{% endstep %}

{% step %}

#### Start the workflow

Right-click the button to open the context menu, and select *Start/Edit workflow*.

<figure><img src="https://34394582-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M5sbzwG7CljeZdkntrL%2Fuploads%2FAjKbJkuBXO1VUynbFyQc%2Fstart-edit-workflow-from-element.png?alt=media&amp;token=e643da4d-d20a-4cbc-8878-741f3a35631f" alt="A button labeled Click me on the canvas, with its right-click menu open and the Start/Edit workflow option highlighted."><figcaption><p>The right-click menu is the quickest way to create a workflow for an element.</p></figcaption></figure>
{% endstep %}

{% step %}

#### Add actions

You'll be taken to the workflow canvas and the new workflow. Click the *+* icon to start adding actions.

<figure><img src="https://34394582-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M5sbzwG7CljeZdkntrL%2Fuploads%2FnA0MpJ1wEOqrzh9XtMEC%2Fadd-event-from-element.png?alt=media&amp;token=f8dbbad7-5c13-4fa8-819b-ae315029ad84" alt="The workflow canvas with the Button Click me is clicked event and the action menu open below it, next to the workflow property editor showing the event&#x27;s properties."><figcaption><p>The event is already set up for you, so you can go straight to choosing an action.</p></figcaption></figure>
{% endstep %}
{% endstepper %}

### Example: input value changed

Next, let's set up a workflow that runs whenever the user changes the value in an input form. Bubble registers the change in one of two ways:

| Input type                                         | When the change registers                        |
| -------------------------------------------------- | ------------------------------------------------ |
| Text-based elements                                | When the element loses focus                     |
| Click-based elements, like dropdowns and calendars | When the user confirms their choice with a click |

{% stepper %}
{% step %}

#### Add an input

Add one of the elements from the input forms category. We're using a regular text input.
{% endstep %}

{% step %}

#### Start the workflow

Right-click the element and select *Start/Edit workflow*. Bubble recognizes the element type and sets the event to respond to the element's value changing.

<figure><img src="https://34394582-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M5sbzwG7CljeZdkntrL%2Fuploads%2FQprUbBE3MoCvBrMU1FLc%2FCleanShot%202026-10-13%20at%2013.43.04.png?alt=media&amp;token=399dcd71-f6b5-4c67-add1-1dfc67d47371" alt="Right-clicking an input element and clicking the Start/Edit workflow option."><figcaption><p>This is the same right-click menu you use for a button.</p></figcaption></figure>
{% endstep %}

{% step %}

#### Add actions

You'll be taken to the workflow canvas, where the new workflow is labeled after its event. Add the actions that should run when the value changes.

<figure><img src="https://34394582-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M5sbzwG7CljeZdkntrL%2Fuploads%2F02wiQ0Ldbo7wA7bFoLn0%2FCleanShot%202026-10-13%20at%2013.44.25.png?alt=media&amp;token=43467cf3-63dd-4c5b-ac19-8507bf18953d" alt="The workflow canvas showing an input&#x27;s value is changed event with no actions."><figcaption><p>Bubble names the workflow after the event.</p></figcaption></figure>
{% endstep %}
{% endstepper %}

{% hint style="info" %}
To save a value to the database whenever an input changes, you can also use auto-binding, which doesn't need a workflow.

**Article section:** [Auto-binding](/help-guides/data/the-database/creating-saving-and-deleting-data.md#auto-binding)
{% endhint %}

### Example: catching errors

Sometimes a workflow runs into an error. For example, a user might try to log in with the wrong credentials. By default, Bubble tells them so in a standard browser message:

<figure><img src="https://34394582-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M5sbzwG7CljeZdkntrL%2Fuploads%2FCrTMa85VGyk1tuNhiEmj%2FCleanShot%202026-10-13%20at%2013.47.52.png?alt=media&amp;token=470a1bd5-5141-4bec-98fb-2aa2a1973095" alt="The browser&#x27;s system dialog showing a login error."><figcaption><p>System errors will result in a standard error message like this in Chrome, but you can override that message by setting up an event to catch it.</p></figcaption></figure>

The *An element has an error running a workflow* event lets you replace that message with your own. It's connected to the element that started the workflow. If a user enters the wrong password, the event belongs to the *Log in* button, not to the input, since the button is what started the workflow.

Let's use it to show an alert we can style however we want:

{% stepper %}
{% step %}

#### Add an alert

Add an **Alert** element[^4] to the page and set it to appear at the top.

<figure><img src="https://34394582-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M5sbzwG7CljeZdkntrL%2Fuploads%2F80vyYte409BjtzTPpK16%2Falert-properties.png?alt=media&amp;token=a26b1e9a-952b-4a37-ab5d-27e71ee2329d" alt="An alert element at the top of the page, with its property editor open and the option to position the alert at the top checked."><figcaption><p>Enabling <em>Position the alert at the top</em> lets us stick the alert to the top of the screen regardless of scrolling position.</p></figcaption></figure>
{% endstep %}

{% step %}

#### Add the event

In the **Workflow** tab, [add a new workflow](/help-guides/logic/workflows/events.md#adding-an-event-to-a-workflow) and pick the *An element has an error running a workflow* event. Then select the *Log in* button as the element.

<figure><img src="https://34394582-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M5sbzwG7CljeZdkntrL%2Fuploads%2Fs9HNakqKuVXKSj614orD%2FCleanShot%202026-10-13%20at%2013.54.55.png?alt=media&amp;token=793bb3ba-bb08-4faa-89d5-e762f2d6f791" alt="The event menu in the workflow canvas with the Elements category expanded and An element has an error running a workflow highlighted."><figcaption><p>The <em>An element has an error running a workflow</em> event triggers whenever a workflow triggered by an element (such as a button) fails to complete.</p></figcaption></figure>
{% endstep %}

{% step %}

#### Show the alert

Add the **Show message in an alert box** action, and use Bubble's original error message as the text. The event now triggers every time a workflow started by the *Log in* button leads to an error.

<figure><img src="https://34394582-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M5sbzwG7CljeZdkntrL%2Fuploads%2F1xU76M47UqHgdCHfTwmz%2FCleanShot%202026-10-13%20at%2013.56.10.png?alt=media&amp;token=99f608f6-97bd-49c2-9b3a-9224beca6450" alt="Bubble&#x27;s workflow editor with the action menu open, the Element Actions category expanded, and Show message in an alert box highlighted."><figcaption><p>The <em>Show message in an alert box</em> action displays the alert element with a customizable message.</p></figcaption></figure>
{% endstep %}
{% endstepper %}

<details>

<summary>Identifying and changing error messages</summary>

In the example above, the alert shows the system-generated message from the `Current workflow error's message` data source.

The *message* operator returns the message Bubble would otherwise have displayed. The *code* operator identifies the error. If you want an action to run only on a certain error, you can use its *Only when* field to check the error code.

All of Bubble's built-in messages are stored in [app texts](/help-guides/data/static-data/app-texts-translations.md), which you'll find in the **Languages** section of the **Settings** tab.

The code of each error message is shown in gray, all-caps text under its description, and you can edit the message itself on the right. Your edit replaces the message whether you show it with a workflow or in the default browser message.

</details>

***

## General events

General events are triggered by something happening on the page that isn't directly tied to an element. They can be the result of something a user does, but they don't have to be.

**Reference:** [General events](/core-resources/events/general-events.md)

### When the page is loaded or URL refreshed

The *Page is loaded* event triggers when the page loads, and when the URL is refreshed with the [*Go to page*](#user-content-fn-5)[^5] action, such as when you add URL parameters. It's useful for anything that should happen as soon as a user arrives.

Since *Go to page* triggers the event even if the page doesn't technically reload, it can run more often than you intended. You can stop that with a condition. For example, you can save a value in a [custom state](#user-content-fn-6)[^6] the first time the page loads, and check that value before running the event again.

### The user is logged in or out

The *User is logged in* and *User is logged out* events trigger when a user's login status changes, which is useful for sending logged-out users to another page.

They also trigger on page load, based on the user's status. So if you send logged-out users to another page, that workflow runs as soon as a logged-out user tries to open the page.

The *Log out user's other sessions* action started from another device may not trigger the event right away. It triggers once the user takes another action on the page.

### An unhandled error occurs

This event works like *An element has an error running a workflow*, but it applies more broadly. It catches errors anywhere on the page, regardless of which element caused them, if any. That's useful when you want to handle every error in one place, such as by showing the same message for all of them.

If both events could run for the same error, only *An element has an error running a workflow* runs.

{% hint style="warning" %}
If you set the event's *Catch* property to *Any workflow error*, Bubble stops displaying workflow error messages altogether. That can also hide messages that you and your users need to see.
{% endhint %}

### When a condition is true

The *Do when condition is true* event uses a [condition](/help-guides/logic/conditions.md) to check in real time whether something is true. It runs a workflow as soon as the expression returns *yes*.

Its *Run this* property decides how often that happens:

<table><thead><tr><th width="160.23828125">Option</th><th>What it does</th></tr></thead><tbody><tr><td><em>Only once</em></td><td>Triggers once per page load. The <em>Go to page</em> action <strong>doesn't</strong> reset this if the user stays on the same page.</td></tr><tr><td><em>Every time</em></td><td>Triggers every time the condition is true.</td></tr></tbody></table>

This event can cause performance issues if it triggers too often, so be mindful of the condition you use and the actions it runs.

### At a set interval

The *Do every 5 seconds* event runs a workflow over and over while the page is open, and you can change the interval to suit your needs.

**Article:** [Recurring workflows](/help-guides/logic/workflows/events/frontend-events/recurring-workflows.md)

### Example: conditional expression

Let's use *Do when condition is true* to make something happen. When the user has entered both an email and a password, we'll animate the *Sign up* button to encourage them to click it.

Start by adding two inputs and a button. Set the *Content format* of the inputs to *Email* and *Password*. We don't just want the inputs to be filled in. We want them to be *valid*.

Then [add a new workflow](/help-guides/logic/workflows/events.md#adding-an-event-to-a-workflow) and pick *Do when condition is true* from the *General* category, and set up the event and its action:

<figure><img src="https://34394582-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M5sbzwG7CljeZdkntrL%2Fuploads%2FluS4DPCtdAENTaTVqxXI%2FCleanShot%202026-10-13%20at%2014.01.26.png?alt=media&amp;token=aeb4ced5-3f1b-4026-b86d-4dc63e5e807a" alt="A condition event in the workflow canvas with numbered callouts on its Run this value and its Only when condition, and a third callout on the Animate Button Log in action below it."><figcaption><p>The numbers match the table below.</p></figcaption></figure>

<table><thead><tr><th width="125.25390625">Number</th><th>What's set up</th></tr></thead><tbody><tr><td>1</td><td>We only need the animation to appear once, so we leave the <em>Run this</em> dropdown set to <em>Only once</em>.</td></tr><tr><td>2</td><td>The expression checks both inputs and uses the <em>and</em> operator, so both have to be valid. With <em>or</em>, one would be enough.</td></tr><tr><td>3</td><td>The <strong>Animate an element</strong> action animates the button. We picked the subtle <em>Callout pulse</em> animation.</td></tr></tbody></table>

The expression needs to check four things:

| Check                                 | What it means                                                                                    |
| ------------------------------------- | ------------------------------------------------------------------------------------------------ |
| `Input Email's value is not empty`    | The input has to contain text.                                                                   |
| `Input Email's value is valid`        | The input has to contain a valid email address.                                                  |
| `Input Password's value is not empty` | The input has to contain text.                                                                   |
| `Input Password's value is valid`     | Technically, all passwords are valid, but you can choose to require a certain password strength. |

<figure><img src="https://34394582-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M5sbzwG7CljeZdkntrL%2Fuploads%2FdJpyQdM9dghuAHt8cQpK%2FCleanShot%202023-03-16%20at%2013.44.04.gif?alt=media&amp;token=be6747a1-bec4-40ec-84bd-261007e06889" alt="A login form with an email input, a password input, and a Log in button."><figcaption></figcaption></figure>

***

## Custom events

Custom events are the third kind of frontend event. They only run when another workflow triggers them, which saves you from repeating the same actions in several places.

**Article:** [Custom events](/help-guides/logic/workflows/events/frontend-events/custom-events.md)

***

## Build frontend events with Bubble AI

You can ask the [Bubble AI Agent](/help-guides/ai/bubble-ai-agent.md) to create a workflow for most of the events on this page. Select the element you're working with, describe what should happen, and then open the **Workflow** tab to see what it built.

The Agent supports a [set list of frontend events](/help-guides/ai/bubble-ai-agent/edit-apps-with-ai.md#generate-and-modify-frontend-workflows): *User is logged in*, *User is logged out*, *Page is loaded*, *Do every five seconds*, *Do when condition is true*, *An unhandled error occurs*, and the events for an element being pressed, an input changing, and an element hitting an error. Custom events aren't on that list.

Here are a few prompts to try:

{% code overflow="wrap" %}

```
Create a workflow that runs when the user clicks the Log In button. It should log the user in using the values from the Email address and Password inputs on this page.
```

{% endcode %}

{% code overflow="wrap" %}

```
Create a workflow that shows an alert when the Log In button has an error running a workflow.
```

{% endcode %}

{% code overflow="wrap" %}

```
Create a workflow that runs when the user is logged out. It should send them to the login page.
```

{% endcode %}

{% code overflow="wrap" %}

```
Create a workflow that animates the Sign up button once when the Email and Password inputs are both valid.
```

{% endcode %}

```
Explain what this workflow does.
```

**Article:** [Edit apps with AI](/help-guides/ai/bubble-ai-agent/edit-apps-with-ai.md)

***

## FAQ: Frontend events

<details>

<summary>Why does my User is logged out event run when the page loads?</summary>

The *User is logged in* and *User is logged out* events also trigger on page load, based on the user's status. That's why a redirect for logged-out users can run the moment they open the page.

</details>

<details>

<summary>What's the difference between An element has an error running a workflow and An unhandled error occurs?</summary>

The first catches errors from workflows started by one specific element. The second catches errors anywhere on the page. If both could run for the same error, only *An element has an error running a workflow* runs.

</details>

<details>

<summary>Can I show my own error message instead of the browser's?</summary>

Yes. Catch the error with an event and show your own message, for example in an alert. See [Example: catching errors](#example-catching-errors).

</details>

<details>

<summary>How do I make Do when condition is true run only once?</summary>

Set its *Run this* property to *Only once*. It then triggers once per page load.

</details>

<details>

<summary>Can frontend events slow down my app?</summary>

They can. Events that trigger often, like *Do when condition is true* and *Do every 5 seconds*, can affect performance and workload, depending on what they check and which actions they run.

**Article section:** [Do when condition is true and conditions](/help-guides/workload/optimizing-workload/optimization-checklist/workflows-and-actions.md#do-when-condition-is-true-and-conditions)

</details>

<details>

<summary>Are frontend events secure?</summary>

Frontend workflows run at least partly on the user's device, so don't rely on them alone to protect anything. Base sensitive conditions on something the server can check, and protect your data with [privacy rules](/help-guides/data/the-database/protecting-data-with-privacy-rules.md).

**Article section:** [Using conditions for security](/help-guides/logic/conditions.md#using-conditions-for-security)

</details>

[^1]: A view is the top-level element in a native mobile app, serving the same role that a page does in a web app.\
    **Article:** [The view element (mobile)](/help-guides/design/elements/ios-and-android-app/the-view.md)

[^2]: *Elements* are the objects that you place on the page as you design your app. There are many different types of elements, like texts, buttons, input fields, and calendars.\
    **Article series:** [Elements](/help-guides/design/elements.md)

[^3]: *Input forms* are elements that accept information from the user, such as a text field, a checkbox, a date picker, or a dropdown.\
    **Article:** [Input forms](/help-guides/design/elements/web-app/input-forms.md)

[^4]: The *Alert* element is used to quickly flash a text message on the screen.\
    **Reference:** [Alert](/core-resources/elements/visual-elements.md#alert)

[^5]: The *Go to page* action is used to send the user to a page in your app. It's also used to change the *Page thing* or update URL parameters.\
    **Reference:** [Go to page](https://manual.bubble.io/help-guides/logic/workflows/events/pages/-MTujrgFLiHe7MfwSBOF#go-to-page-...)

[^6]: Custom states are variables that you can save on any element on the page, including the page itself. They let you store data temporarily that is reset when the page is reloaded.\
    **Article:** [Custom states](/help-guides/data/temporary-data/custom-states.md)
