> 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/data/static-data/option-sets.md).

# Option sets

This section covers option sets, used to store a static list of options in a database-like structure

Option sets let you store a static list of options in a database-like structure, without using the database. They're useful for information like days of the week, marital status, colors, states, countries, and other data that you want to load quickly and that rarely changes.

<figure><img src="https://34394582-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M5sbzwG7CljeZdkntrL%2Fuploads%2F40r40My9jITeh2cusn4T%2Fdropdown-option-set.gif?alt=media&amp;token=5ac145c8-b5ec-4bb0-abae-f571d89408a8" alt=""><figcaption><p>Option sets can be used to store static options and use them around your app. In this example we have saved a list of colors as options in a set</p></figcaption></figure>

## What is an option set?

An option set becomes [part of your app's source code](#user-content-fn-1)[^1], which means it's downloaded as part of the JavaScript file that makes up your app. Because of that, it doesn't require a database lookup, and it's cached[^2] on the user's device until you deploy a new version, making it fast to load and lightweight.

It also means an option set can't be added to, edited, or deleted by your users, and you have to redeploy your app before new options become available in Live.

{% hint style="danger" %}
Unlike the database, option sets aren't encrypted, and they become part of your app's source code. **Option sets should never contain sensitive information.**
{% endhint %}

## How option sets are structured

An option set, as the name suggests, is a set of *options*. To each set you can add a list of *attributes*, which are similar to the fields on a data type. An attribute can be one of the following types:

* text
* number
* date
* date interval
* yes / no
* file
* image
* geographic address
* another option set

To summarize: each option set is a collection of options that share the same attributes, each of one of the types above.

Option sets are often used to define a fixed set of values for a field on a data type. For example, in a blog platform you could create a *Status* option set with the options Draft, Review, and Published, then add a *Status* field on the *Post* data type that uses it. Every post then draws from the same fixed list, and you can't end up with typos or stray values like "publised".

<figure><img src="https://34394582-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M5sbzwG7CljeZdkntrL%2Fuploads%2FNhJppZPxVTD6TI9BDwvA%2FCleanShot%202026-09-21%20at%2014.56.16.png?alt=media&amp;token=a9200da8-ab3d-4650-867e-598a818bd131" alt="The editor for a Status option set, showing the built-in Display attribute and three options: Draft, Review, and Published."><figcaption><p>A Status option set for a blog app, with the options Draft, Review, and Published stored in the built-in Display attribute.</p></figcaption></figure>

{% hint style="warning" %}
Bubble doesn't require *Display* values to be unique, but it's worth keeping them unique anyway. If you later filter options by their *Display* value and you have duplicates, you may not get the results you expect.
{% endhint %}

## Creating an option set

You create and manage option sets in the *Data* tab, in the *Option sets* section.

{% stepper %}
{% step %}

#### Create the set

In the *Option sets* section, type a name into the *New option set* field and click **Create**. Your new set appears in the list on the left.

<figure><img src="https://34394582-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M5sbzwG7CljeZdkntrL%2Fuploads%2FsBDLPcKWhIuZZqphYmfO%2Fnew-option-set.png?alt=media&amp;token=c7a435b5-c5bc-4596-a03c-9b780e801f26" alt=""><figcaption></figcaption></figure>
{% endstep %}

{% step %}

#### Add options

<figure><img src="https://34394582-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M5sbzwG7CljeZdkntrL%2Fuploads%2FNhJppZPxVTD6TI9BDwvA%2FCleanShot%202026-09-21%20at%2014.56.16.png?alt=media&amp;token=a9200da8-ab3d-4650-867e-598a818bd131" alt="The editor for a Status option set, showing the built-in Display attribute and three options: Draft, Review, and Published."><figcaption><p>Here, we have created the three options <em>Draft, Review</em> and <em>Published.</em></p></figcaption></figure>

With the set and its attributes in place, add the options themselves. For a *City* set, the options would be the cities you want to list, like *Boston* and *Washington*. Select the set, type a name into the *New option* field, and click **Create**. The name you enter is stored in the option's *Display* attribute.
{% endstep %}

{% step %}

#### Set up attributes

Add the attributes you need to describe each option. In many cases, the built-in *Display* attribute is enough to hold each option's main identifier, such as the statuses in this example.

<figure><img src="https://34394582-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M5sbzwG7CljeZdkntrL%2Fuploads%2FTZbFNp13nQSi0IoKWxJS%2Fattributes.png?alt=media&amp;token=bad841b9-54fb-45b8-8805-bace21f64c93" alt="The Status option set editor, showing a custom Can be edited by attribute of type Role added alongside the built-in Display attribute. The Review option has its Can be edited by value set to Editor."><figcaption><p>The Status option set with a Can be edited by attribute that links to a Role option set. Here, the Review status is set so it can be edited by the Editor role.</p></figcaption></figure>

Let's add one more attribute that determines what user roles (another option set) that edit a blog post.

1. Click *Create new attribute* and name it. We've called it *Can be edited by*.
2. The attribute will shop up in the list of attributes on this option set
3. We've set the *Can be edited by* attribute to *Editor* which is another option set that sets user roles.
   {% endstep %}

{% step %}

#### Editing the attributes

To edit an option set's attributes, click the pencil icon next to the set in the list. This link only appears once you've created at least one option.

<figure><img src="https://34394582-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M5sbzwG7CljeZdkntrL%2Fuploads%2FapfxdkYjjC3CJ4qG9v92%2Fediting-attributes.png?alt=media&amp;token=b6f2cfde-2168-4d18-88dd-182127e768b2" alt=""><figcaption></figcaption></figure>

{% endstep %}
{% endstepper %}

## Using option sets

### Using option sets in an element

Unlike data types, you don't *search* for option sets. They're all loaded on page load, and you reference them in elements and workflows as needed.

<figure><img src="https://34394582-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M5sbzwG7CljeZdkntrL%2Fuploads%2FJmhlZRygMyqQm2RPTTCt%2Fuse-option-set.png?alt=media&amp;token=9b107ee9-e327-4707-aadc-fe8f00685852" alt=""><figcaption></figcaption></figure>

{% stepper %}
{% step %}

#### Add a dropdown

Place a dropdown element on the page and give it a placeholder, such as *Choose an option*. This is the element your users will pick a status from.
{% endstep %}

{% step %}

#### Open the Choices property

In the element's property editor, find the *Choices* setting under *Content*.
{% endstep %}

{% step %}

#### Switch to Dynamic choices

In the *Choices* popup, select *Dynamic*. This lets you pull the options from an option set rather than typing them in by hand.
{% endstep %}

{% step %}

#### Set the type of choices

Select the *Status* in the *Option set* category.
{% endstep %}

{% step %}

#### Set the choices source

<figure><img src="https://34394582-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M5sbzwG7CljeZdkntrL%2Fuploads%2FlAN2QqznTNQWwzrgX1wp%2Fstatus-choices.png?alt=media&amp;token=5df9ad73-137f-490f-a429-9c9671b8bfe5" alt=""><figcaption></figcaption></figure>

In *Choices source*, enter `All options` to load every option in the set. This is the key difference from data types: you don't search for the options, you load them all. To narrow the list, add the `:filtered` operator, such as `All Status:filtered`.
{% endstep %}

{% step %}

#### Set the option caption

In *Option caption*, choose which attribute to show as each option's label. Here we use `Current option's Display`, so the dropdown shows each status's *Display* value (Draft, Review, Published).
{% endstep %}
{% endstepper %}

### Using option sets in an expression

You can also use an option set as a data source in an expression.

This example ties the linked option set together. The *Status* option set has a *Can be edited by* attribute pointing to a *Role* option set, and here we use it to save a post only when the current user's role matches the role allowed to edit that status.

{% stepper %}
{% step %}

#### Set up the event

Start a workflow from the user action that saves the post, such as *Save post is clicked*.
{% endstep %}

{% step %}

#### Add the Make changes action

Add a **Make changes to a thing** action, and set *Thing to change* to `Current page's Post`.
{% endstep %}

{% step %}

#### Add the condition

In the action's *Only when* field, build this condition:

<figure><img src="https://34394582-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M5sbzwG7CljeZdkntrL%2Fuploads%2F9yMXj3xHLatJge5Jce9D%2Foption-set-in-an-expreession.png?alt=media&amp;token=9bf8a0d3-7121-4daa-b40b-29deac4f6bbc" alt=""><figcaption></figcaption></figure>

`Current page's Post's Status's Can be edited by is Current User's Role`

The action now runs only when the current user's role matches the *Can be edited by* role on the post's current status. If the roles don't match, the save is skipped.
{% endstep %}
{% endstepper %}

## Option sets versus data types

Option sets and data types both let you store structured information, but they serve different purposes. Choosing between them comes down to whether the information is static and known in advance, or dynamic and tied to user actions. The two also have very different security profiles.

### Security

Option sets and data types are stored and accessed in fundamentally different ways, which has direct security implications. Choosing the right one for a given piece of information is as much a security decision as a structural one.

#### Option sets are part of your app's source code

Option sets are included in the JavaScript files Bubble sends to the client when your app loads. Every option set, including all its options and their attributes, is visible in your app's source code. Anyone who inspects the page in their browser's developer tools can see the full list.

This is by design. Option sets are meant for static, non-sensitive information the app needs instantly, like categories, statuses, or fixed configuration values. The trade-off for that speed is visibility. For this reason, never use an option set to store:

* API keys, tokens, or other credentials
* Internal business logic that shouldn't be exposed
* Information about features you don't want users to discover
* Any value that needs to be hidden from the client

#### Data types are stored in an encrypted database on the server

Data types live in the database, on the server. The client only receives data type records when it explicitly requests them, and only the fields it's allowed to see. That makes data types the right choice for anything sensitive, dynamic, or tied to specific users.

The key security mechanism for data types is privacy rules. They let you control who can see which records and which fields, evaluated on the server before any data reaches the client. With privacy rules in place, sensitive fields can be hidden entirely from users who shouldn't have access, even when the rest of the record is visible.

### Comparison

| Feature                     | Option set                                                      | Data type                                          |
| --------------------------- | --------------------------------------------------------------- | -------------------------------------------------- |
| How is the data stored?     | In your app's source code                                       | In the database, server-side and encrypted         |
| How is data created?        | Defined in the editor at build time                             | Created at run time or in Bubble's database editor |
| When is it loaded?          | Whenever any page loads                                         | Only when requested                                |
| When is it updated?         | On the first page load after the app is deployed                | Instantly                                          |
| Who can modify the data?    | The app developer                                               | Users, workflows, and the app developer            |
| Performance                 | Loaded instantly with the app                                   | Fetched from the database when needed              |
| Privacy rules               | Not supported                                                   | Supported                                          |
| Suitable for sensitive data | No, since values are visible in the source code                 | Yes, when protected by privacy rules               |
| Editable in run mode        | No                                                              | Yes                                                |
| Typical examples            | Categories, statuses, country lists, fixed configuration values | Users, orders, blog posts, messages                |

### When to use which

Use an **option set** when:

* The values are known in advance and won't change based on user actions
* The list is relatively short and stable, like a set of statuses or categories
* You want the values available instantly, without a database lookup
* You don't need privacy rules to control who can see the values

Use a **data type** when:

* The data is created, updated, or deleted by users or workflows
* The number of records can grow over time
* You need privacy rules to control access
* The data is tied to specific users or other database records through relationships

In practice, most apps use both. Option sets handle the fixed structure of the app, while data types hold the dynamic content users interact with.

## Creating option sets with Bubble AI

You don't have to build option sets by hand. Bubble AI can create them for you from a plain-language description, including the options and their attributes, which is often the fastest way to set one up.

Ask the Bubble AI Agent for what you need, and it builds the option set in your app. You can then review it, adjust it, or keep prompting to refine it. A few examples of prompts you could give Bubble AI:

{% code overflow="wrap" %}

```
Create a Status option set with the options Draft, Review, and Published.
```

{% endcode %}

{% code overflow="wrap" %}

```
Create a Country option set with an attribute for the two-letter country code.
```

{% endcode %}

{% code overflow="wrap" %}

```
Create a Role option set, then add a Can be edited by attribute to my Status option set that links to it.
```

{% endcode %}

Because option sets are static, the Agent defines them in the editor the same way you would, so everything covered in this article still applies to what Bubble AI creates. You stay in full control: review what the Agent built, edit any option or attribute directly, and redeploy when you're ready for the changes to reach your live app.

{% hint style="info" %}
This is a quick look at using Bubble AI for option sets. To learn more about building with Bubble AI and the Bubble AI Agent, see the article below.

Article: [Bubble AI](/help-guides/ai/bubble-ai-agent.md)
{% endhint %}

## FAQ: Option sets

<details>

<summary>What is an option set?</summary>

An option set is a static list of predefined values stored in your app's source code. Option sets are used for information that doesn't change based on user actions, such as categories, statuses, country lists, or fixed configuration values.

</details>

<details>

<summary>How is an option set different from a data type?</summary>

Option sets are static and stored in your app's source code, while data types are dynamic and stored in the database. Option sets are defined at build time and don't change based on user actions, whereas data types can be created, updated, and deleted by users and workflows.

Read more in Option sets versus data types.

</details>

<details>

<summary>Can option sets be modified at run time?</summary>

No. Option sets are part of your app's structure and can only be modified in the editor. If you need values that change based on user actions, use a data type instead.

</details>

<details>

<summary>Are option sets visible to users?</summary>

Yes. Option sets are included in your app's source code and visible to anyone who inspects the page in their browser's developer tools. This includes all of an option set's data, not just its names. Don't use option sets to store sensitive information.

</details>

<details>

<summary>Can I add custom attributes to options?</summary>

Yes. Each option set can have its own attributes, similar to fields on a data type. Attributes can be text, numbers, dates, yes/no values, or references to other option sets. They're useful for storing related information alongside each option, such as a display label or a color code.

</details>

<details>

<summary>How many options can an option set have?</summary>

Option sets are designed for small, static lists. There's no strict limit, but very large option sets can slow down your app's load time, since the entire set is included in the source code. If you find yourself with a long or growing list, consider a data type instead.

</details>

<details>

<summary>Can I apply privacy rules to an option set?</summary>

No. Privacy rules only apply to data types. If you need to control who can see certain values, store them in a data type and use privacy rules to restrict access.

</details>

<details>

<summary>When should I use an option set instead of a data type?</summary>

Use an option set when the values are known in advance, won't change at run time, and don't need privacy controls. Use a data type when the values are dynamic, tied to users, or need access restrictions. Most apps use both, with option sets for fixed structure and data types for user-generated content.

See Option sets versus data types for more.

</details>

<details>

<summary>Can I convert an option set to a data type, or the reverse?</summary>

There's no built-in conversion tool. To switch, you'll need to recreate the structure manually in the other format and update any references in your app.

</details>

<details>

<summary>Can option sets reference each other?</summary>

Yes. An attribute on one option set can reference another option set. This is useful when one set of options is related to another, such as a list of regions tied to a list of countries.

See Linking option sets.

</details>

<details>

<summary>How do I display options to users?</summary>

Options can be displayed in dropdowns, radio buttons, and other input elements by setting the choices source to your option set. You can also reference specific options directly in dynamic expressions throughout your app. See Using option sets.

</details>

<details>

<summary>How do I search for or filter option sets?</summary>

You don't search option sets the way you search data types, since they're already on the user's device and aren't part of the database. Loading an option set always returns all its options. To narrow that list, add the `:filtered` operator to the option set data source in a dynamic expression, then set your constraints inside the filter. You can also reference a single option directly without searching.

</details>

<details>

<summary>I changed an option set. Why can't I see the change in my live app?</summary>

If you can't see your change, check the following:

* Changes appear in Development after you refresh the page, and in Live after you deploy and refresh.
* Confirm the *Saving* indicator next to the edit menu reads *Saved*, so you know the change synced to Bubble's server. If it doesn't, check your internet connection.

</details>

<details>

<summary>Does Bubble only download the option sets used on a page?</summary>

No. All option sets are downloaded on every page, or the cached file is used if it's already on the user's device. Two things follow from this:

* Avoid storing a lot of data in an option set, such as many long texts, since it can slow down page load and general performance.
* Never store sensitive information in an option set, since it's visible in your app's source code whether or not it's used on the page.

</details>

<details>

<summary>Can an option set reference a database thing?</summary>

No, an option set can't reference a database thing directly, since option sets are static and database records are dynamic. You can store a thing's unique ID in an option set, but this comes with risks:

* Any database record can be deleted, which breaks the link.
* A thing can have a different unique ID in your Live and Development databases, which can break the link when you deploy.
* Exposing a unique ID isn't a vulnerability on its own, but it's generally not recommended, especially for things holding sensitive data. A visible unique ID combined with misconfigured privacy rules can open up vulnerabilities that would otherwise be harder to reach.

</details>

## Other ways to learn

<details>

<summary><mark style="color:blue;">Core reference</mark></summary>

* Get an option (loading an option set)

</details>

<details>

<summary><mark style="color:blue;">Video lessons</mark></summary>

* [How to use option sets](https://www.youtube.com/watch?v=7FwLBBQzinM)

</details>

[^1]: While Bubble is a no-code platform, your app consists of code that Bubble generates for you. These files are downloaded to the user's device when they load the page.

    In the case of option sets, they're stored as JSON in a file called dynamic.js.

[^2]: Cached means the downloaded file is stored on the user's device the first time it's downloaded, so the page loads faster on later visits, since the cached files don't need to be downloaded again. Whenever you deploy changes, the file is re-downloaded so it stays up to date.
