Bubble Card Module Editor schema documentation
June 16, 2026 · View on GitHub
This documentation covers all available options for creating editor schemas in Bubble Card modules. These schemas define the user interface presented to users when configuring your module.
Table of contents
- Basic structure
- Accessing configuration values in your module code
- Field properties
- Field types
- Selector-based fields
- Text input selector
- Number selector
- Boolean selector
- Select selector
- Color selector
- Icon selector
- Condition selector
- Entity selector
- Device selector
- Area selector
- Theme selector
- Action selector
- Time selector
- Date selector
- Datetime selector
- Media selector
- Attribute selector
- State selector
- Target selector
- Config entry selector
- Addon selector
- Location selector
- Object selector
- Backup selector
- Assistance selector
- Label selector
- Language selector
- Schedule selector
- Template selector
- File selector
- QR code selector
- Conversation agent selector
- Duration selector
- Dashboard selector
- Floor selector
- Legacy type-based fields
- Selector-based fields
- Advanced structure
- Best practices
- Example: Complete module editor schema
- References
Basic structure
A module editor schema is an array of objects, where each object represents a form field:
editor:
- name: color
label: "Color"
selector:
select:
options:
- label: "Red"
value: "red"
- label: "Blue"
value: "blue"
- name: icon_size
label: "Icon Size"
selector:
number:
min: 20
max: 50
unit_of_measurement: "px"
Accessing configuration values in your module code
When creating a Bubble Card module, you'll need to access the values configured by users through the editor. These values are available in your module's code through the this.config object.
Accessing configuration in JavaScript templates
In your module's code section, you can access the configured values using JavaScript template literals. The configuration values follow a specific structure:
this.config.module_id?.field_name
Where:
module_idis the ID of your module as defined in your module definitionfield_namecorresponds directly to thenameproperty of the field in your editor schema
Example
If you have this editor schema:
editor:
- name: color # This becomes "color" in your code
label: "Background Color"
selector:
ui_color: {}
- name: size # This becomes "size" in your code
label: "Icon Size"
selector:
number:
min: 10
max: 50
unit_of_measurement: "px"
- name: show_icon # This becomes "show_icon" in your code
label: "Show Icon"
selector:
boolean: {}
Then in your module's code section, you would access these values like this:
.bubble-icon-container {
/* Access the "color" field */
background: var(--${this.config.module_id?.color}-color) !important;
/* Access the "size" field with a default value if undefined */
--mdc-icon-size: ${this.config.module_id?.size || 24}px;
/* Access the "show_icon" boolean field */
display: ${this.config.module_id?.show_icon ? 'flex' : 'none'};
}
Remember to replace module_id with your actual module ID in your code.
Tips for working with configuration values
-
Use optional chaining: Always use the optional chaining operator (
?.) when accessing nested configuration properties to prevent errors if the configuration is missing. -
Provide default values if possible: In some cases, use the OR operator (
||) to supply default values in case the configuration value is undefined.
Example: Complete module with editor and code
Complete module example
Here's a complete example of a module definition showing both the editor schema and how to use the values in the code:
icon_container_color:
name: 'Example: Customize the icon container color'
version: v1.1
creator: Clooos
link: https://github.com/Clooos/Bubble-Card/discussions/1231
unsupported:
- horizontal-buttons-stack
- separator
description: |
A list of predefined colors to customize the icon container color.
Configure this module via the editor or in YAML, for example:
<br><br>
<code-block><pre>
icon_container_color:
color: light-blue
</pre></code-block>
code: |
.bubble-icon-container {
opacity: 1 !important;
background: var(--${this.config.icon_container_color?.color}-color) !important;
}
editor:
- name: color
label: Color
selector:
ui_color:
include_none: true
Field properties
Every field in your editor schema can have these common properties:
| Property | Type | Description |
|---|---|---|
name | string | Required. The key used to store the value in the module configuration |
label | string | The displayed name for the field |
required | boolean | Whether the field is required |
disabled | boolean | Whether the field is disabled |
default | any | Default value if no value is provided |
Field types
You can define fields using either the legacy type syntax or the modern selector syntax.
Important
Not all selector types have been tested with Bubble Card modules. Some selectors might not work correctly or might not be fully compatible. If you encounter an issue, please report it here.
Selector-based fields
Selector-based fields provide rich UI controls. Simply use the selector property with one of the following selector types:
Text input selectors
- name: title
label: "Title"
selector:
text: {}
Options
| Option | Type | Description |
|---|---|---|
multiline | boolean | Enable multiline text input |
type | string | HTML input type (e.g., "email", "url", "password") |
autocomplete | string | Browser autocomplete attribute |
prefix | string | Text to display before the input |
suffix | string | Text to display after the input |
Number selector
- name: opacity
label: "Opacity"
selector:
number:
min: 0
max: 100
step: 5
unit_of_measurement: "%"
Options
| Option | Type | Description |
|---|---|---|
min | number | Minimum value |
max | number | Maximum value |
step | number | Step value |
mode | string | Display mode: "box" or "slider" (default: "slider") |
unit_of_measurement | string | Unit label |
min_step | number | Minimum step value |
Boolean selector
- name: show_icon
label: "Show Icon"
selector:
boolean: {}
No additional option for this selector.
Select selector
- name: theme
label: "Theme"
selector:
select:
options:
- label: "Light"
value: "light"
- label: "Dark"
value: "dark"
- label: "Auto"
value: "auto"
multiple: false
custom_value: false
mode: "dropdown"
Options
| Option | Type | Description |
|---|---|---|
options | array | List of options with label/value pairs, or simple string arrays |
translation_key | string | Translation key for the options |
multiple | boolean | Allow multiple selection |
custom_value | boolean | Allow custom values |
mode | string | Display mode: "dropdown" or "list" |
Color selector
- name: background_color
label: "Background Color"
selector:
ui_color: {}
Options
| Option | Type | Description |
|---|---|---|
default_color | string | Default color to use if no color is selected |
include_none | boolean | Include an option to select no color |
include_state | boolean | Include a color based on the entity state |
Icon selector
- name: custom_icon
label: "Custom Icon"
selector:
icon: {}
No additional option for this selector.
Condition selector
- name: conditions
label: "Conditions"
selector:
condition: {}
The condition selector allows you to define complex conditions based on entity states, numeric values, time, and more. This is especially powerful for creating conditional UI elements or behaviors.
Using conditions in your module code
In your JavaScript code, you can use the checkConditionsMet function to evaluate conditions at runtime:
// Example of checking if conditions are met
if (!badgeConfig?.condition || (badgeConfig?.condition && checkConditionsMet([].concat(badgeConfig.condition), hass))) {
// The condition is met or there is no condition defined
// Show or activate your component here
}
Here's a simple example:
# Module configuration example
my_module:
element_to_show:
condition:
- condition: state
entity_id: light.living_room
state: 'on'
- condition: numeric_state
entity_id: sensor.temperature
above: 20
// In your module code
const elementConfig = this.config.my_module?.element_to_show;
if (!elementConfig?.condition || checkConditionsMet([].concat(elementConfig.condition), hass)) {
// Show element when living room light is ON and temperature is above 20
}
No additional selector options.
Entity selector
- name: target_entity
label: "Target Entity"
selector:
entity:
filter:
domain: light
Options
| Option | Type | Description |
|---|---|---|
filter.domain | string | string[] | Filter by entity domain(s) |
filter.device_class | string | string[] | Filter by device class(es) |
filter.integration | string | Filter by integration |
filter.supported_features | number | number[] | Filter by supported features flags |
include_entities | string[] | List of entities to include |
exclude_entities | string[] | List of entities to exclude |
multiple | boolean | Allow multiple selection |
Device selector
- name: device
label: "Device"
selector:
device:
filter:
integration: zwave
Options
| Option | Type | Description |
|---|---|---|
filter.integration | string | string[] | Filter by integration(s) |
filter.manufacturer | string | string[] | Filter by manufacturer(s) |
filter.model | string | string[] | Filter by model(s) |
entity.domain | string | string[] | Filter by entity domain(s) |
entity.device_class | string | string[] | Filter by entity device class(es) |
multiple | boolean | Allow multiple selection |
Area selector
- name: area
label: "Area"
selector:
area: {}
Options
| Option | Type | Description |
|---|---|---|
entity.domain | string | string[] | Filter by entities in area with domain(s) |
entity.device_class | string | string[] | Filter by entities in area with device class(es) |
device.integration | string | string[] | Filter by devices in area with integration(s) |
device.manufacturer | string | string[] | Filter by devices in area with manufacturer(s) |
device.model | string | string[] | Filter by devices in area with model(s) |
multiple | boolean | Allow multiple selection |
Theme selector
- name: card_theme
label: "Card Theme"
selector:
theme: {}
Options
| Option | Type | Description |
|---|---|---|
include_default | boolean | Include the default theme |
Action selector
- name: tap_action
label: "Tap Action"
selector:
action: {}
Options
| Option | Type | Description |
|---|---|---|
actions | string[] | List of allowed actions (e.g., ["more-info", "toggle", "call-service", "navigate", "url", "none"]) |
Time selector
- name: start_time
label: "Start Time"
selector:
time: {}
No additional option for this selector.
Date selector
- name: event_date
label: "Event Date"
selector:
date: {}
Options
| Option | Type | Description |
|---|---|---|
min | string | Minimum date in ISO format (YYYY-MM-DD) |
max | string | Maximum date in ISO format (YYYY-MM-DD) |
Datetime selector
- name: event_datetime
label: "Event Date and Time"
selector:
datetime: {}
Options
| Option | Type | Description |
|---|---|---|
min | string | Minimum datetime in ISO format |
max | string | Maximum datetime in ISO format |
Media selector
- name: media
label: "Media"
selector:
media: {}
Options
| Option | Type | Description |
|---|---|---|
filter_media_source | boolean | Filter media sources |
filter_local_media | boolean | Filter local media |
Attribute selector
This selector works only if combined to an entity selector at the same level. Inside an object selector item, if the entity field next to it is empty the attribute list falls back to the card's configured entity, matching the common runtime pattern where items inherit the card entity.
- name: attribute
label: "Attribute"
selector:
attribute: {}
Options
| Option | Type | Description |
|---|---|---|
entity_id | string | Required: Entity ID to select attribute from |
hide_attributes | string[] | List of attributes to exclude |
State selector
This selector works only if combined to an entity selector at the same level.
- name: target_state
label: "Target State"
selector:
state: {}
Options
| Option | Type | Description |
|---|---|---|
entity_id | string | Required: Entity ID to select state from |
attribute | string | Select from entity attribute rather than state |
Target selector
- name: target
label: "Target"
selector:
target:
entity:
domain: light
Options
| Option | Type | Description |
|---|---|---|
entity | object | Entity filters (same as entity selector) |
device | object | Device filters (same as device selector) |
area | object | Area filters (same as area selector) |
Config entry selector
- name: config_entry
label: "Integration"
selector:
config_entry:
domain: zwave_js
Options
| Option | Type | Description |
|---|---|---|
domain | string | Filter by domain |
Addon selector
- name: addon
label: "Add-on"
selector:
addon: {}
Options
| Option | Type | Description |
|---|---|---|
name | string | Filter by add-on name |
Location selector
- name: location
label: "Location"
selector:
location:
radius: true
icon: "mdi:home"
Options
| Option | Type | Description |
|---|---|---|
radius | boolean | Allow setting a radius around the location |
icon | string | Icon to show on the map |
Object selector
The object selector lets users enter structured objects defined by a set of sub-fields. Each sub-field uses its own selector (e.g., text, number, icon). It can capture a single object or a list when multiple is true. Use label_field and description_field to control the label and secondary text displayed for each item. The output is an object or a list of objects.
When multiple is true, each item row has a drag handle to reorder the list and a duplicate button that inserts a deep copy of the item right below it.
- name: main_item
label: "Main item"
selector:
object:
fields:
name:
label: "Name"
selector:
text: {}
icon:
label: "Icon"
selector:
icon: {}
label_field: name
description_field: icon
multiple: true
Fields that share a group are rendered together inside a collapsible section
(the group name becomes the section title, group_icon its optional icon).
The stored configuration stays flat — grouping only affects the editor UI, so
adding groups to an existing module is fully backward compatible. Fields
without a group render at the top level as usual:
- name: items
label: "Items"
selector:
object:
fields:
name:
label: "Name"
selector:
text: {}
background_color:
label: "Background color"
group: "Appearance"
group_icon: "mdi:palette"
selector:
ui_color: {}
text_color:
label: "Text color"
group: "Appearance"
selector:
ui_color: {}
multiple: true
Options
| Option | Type | Description |
|---|---|---|
fields | object | Map of field keys to field schemas. Each field supports a label, a description, a nested selector (any selector type), and an optional group. Keys starting with __ or bc_group_ are reserved for the editor's internal UI state. |
fields.*.group | string | Renders the field inside a collapsible section with this title. Fields sharing the same group end up in the same section. UI-only: the stored value stays flat. |
fields.*.group_icon | string | Optional icon (Material Design Icons) for the field's group section. |
fields.*.visible_if | string | JS expression evaluated against the item's current data as item, the live hass object and the card's config as card (e.g. item.target === 'card'). hass and card may be undefined (early renders, dialogs the card config can't be found from) — guard them (card && card.entity). The field only renders while the expression is truthy; sections whose fields are all hidden disappear. Re-evaluated live on every change. Broken expressions fail open (field stays visible). |
fields.*.warn_if | string | JS expression with the same item / hass / card arguments as visible_if, including the same need to guard hass and card. While truthy, the field shows warn_text — e.g. warn about a missing entity (item.entity && hass && !hass.states[item.entity]) or an option that has no effect without another one. Broken expressions fail silent (no warning). |
fields.*.warn_text | string | The warning message displayed while warn_if is truthy, rendered as an amber warning alert above the field. |
fields.*.default | any | For text-based sub-fields, the declared default is shown as the input's placeholder so users can see what applies when the field is left empty. Single-select dropdowns render the default as the selected value while the key is unset (display only — picking the default or clearing the dropdown stores nothing, so unset and default stay the same config). |
fields.*.variant_of | string | Marks this field as an alternative representation ("variant") of the named base field — e.g. a state→color map or a JS expression next to a static color. The form collapses the family into one mode dropdown (the base mode — labelled Static unless the base field sets its own variant — plus each variant's variant label) and only the active variant's input. The dropdown is UI-only: stored values keep their original keys, and opening an existing config selects whichever variant already has data. If an inactive variant also has data, a helper line warns that the module's priority rules decide which wins. The family renders inside a light visual cluster (thin left rail), so the mode dropdown and its value field read as one unit. |
fields.*.variant | string | Display label of this variant in the family's mode dropdown (e.g. "State map", "JS"). Defaults to the field key. On the base field it instead renames the base mode's dropdown label (default "Static") — e.g. variant: Single next to a "Multiple" variant. |
fields.*.cluster_of | string | Renders this field inside the named base field's visual cluster (the same thin-left-rail box variant families use) instead of as a standalone row — purely visual grouping for fields that form one logical unit, e.g. a mode select plus its parameters. Rule of thumb: variants swap which key is stored, clusters only group real fields visually. Nothing is synthetic or swapped: every member is a real stored field and keeps its own visible_if. A hidden base hides the whole cluster; while no member is visible the base renders flat (no rail). Composes with variants — members append after the variant rows. |
label_field | string | Property key used as the item label in the UI (useful when multiple is true). |
description_field | string | list | Property key used as an optional item description in the UI. A list of keys falls back to the first one with a value; list values render comma-separated. |
multiple | boolean | Allow entering a list of objects. If true, the resulting value is a list. |
Backup selector
- name: backup
label: "Backup"
selector:
backup:
integration: google_assistant
Options
| Option | Type | Description |
|---|---|---|
integration | string | Filter backups by integration |
Assistance selector
- name: assistance_pipeline
label: "Assistance Pipeline"
selector:
assistance: {}
No additional option for this selector.
Label selector
- name: labels
label: "Labels"
selector:
label:
multiple: true
Options
| Option | Type | Description |
|---|---|---|
multiple | boolean | Allow multiple selection |
Language selector
- name: language
label: "Language"
selector:
language: {}
No additional option for this selector.
Schedule selector
- name: schedule
label: "Schedule"
selector:
schedule: {}
No additional option for this selector.
Template selector
- name: template
label: "Template"
selector:
template: {}
No additional option for this selector.
File selector
- name: config_file
label: "Configuration File"
selector:
file:
accept: ".yaml,.json"
Options
| Option | Type | Description |
|---|---|---|
accept | string | Comma-separated list of acceptable file extensions |
QR code selector
- name: qr_data
label: "QR Code Data"
selector:
qr_code: {}
No additional option for this selector.
Conversation agent selector
- name: agent
label: "Conversation Agent"
selector:
conversation_agent: {}
No additional option for this selector.
Duration selector
- name: timeout
label: "Timeout"
selector:
duration:
enable_day: false
Options
| Option | Type | Description |
|---|---|---|
enable_day | boolean | Include days in the duration selector |
Dashboard selector
- name: dashboard
label: "Dashboard"
selector:
dashboard:
include_dashboards: ["lovelace"]
Options
| Option | Type | Description |
|---|---|---|
include_dashboards | string[] | List of dashboards to include |
Floor selector
- name: floor
label: "Floor"
selector:
floor: {}
No additional option for this selector.
Legacy type-based fields
While selector-based fields are recommended, you can also use the legacy type syntax:
String field
- name: title
label: "Title"
type: string
Integer field
- name: count
label: "Count"
type: integer
valueMin: 0
valueMax: 100
Float field
- name: opacity
label: "Opacity"
type: float
Boolean field
- name: enabled
label: "Enabled"
type: boolean
Select field
- name: mode
label: "Mode"
type: select
options:
- ["auto", "Automatic"]
- ["manual", "Manual"]
Multi-select field
- name: features
label: "Features"
type: multi_select
options:
- ["animations", "Animations"]
- ["colors", "Custom Colors"]
- ["icons", "Custom Icons"]
Advanced structure
Grid layout
You can organize fields in a grid layout:
- type: grid
name: appearance
schema:
- name: color
label: "Color"
selector:
select:
options:
- label: "Red"
value: "red"
- label: "Blue"
value: "blue"
- name: size
label: "Size"
selector:
number:
min: 10
max: 100
Options
| Option | Type | Description |
|---|---|---|
column_min_width | string | Minimum column width (CSS value) |
schema | array | Fields in the grid |
Expandable sections
You can create collapsible sections:
- type: expandable
name: advanced_settings
title: "Advanced Settings"
icon: "mdi:tune"
expanded: false
schema:
- name: animation_speed
label: "Animation Speed"
selector:
number:
min: 1
max: 10
Options
| Option | Type | Description |
|---|---|---|
title | string | Section title |
icon | string | Section icon (Material Design Icons) |
expanded | boolean | Initially expanded |
schema | array | Fields in the section |
Best practices
- Keep it simple: Only include fields that users actually need to configure
- Use clear labels: Make field labels descriptive but concise
- Provide defaults: Set sensible default values where possible
- Group related fields: Use grid or expandable sections to organize complex options
- Add descriptions: Use the description property to explain complex options
- Test your UI: Ensure your form is user-friendly before sharing
Example: Complete module editor schema
editor:
- name: color_mode
label: "Color Mode"
selector:
select:
options:
- label: "Custom Color"
value: "custom"
- label: "Theme Color"
value: "theme"
- name: custom_color
label: "Custom Color"
selector:
ui_color: {}
- type: expandable
title: "Advanced Settings"
expanded: false
schema:
- name: animation
label: "Animation"
selector:
boolean: {}
- name: animation_speed
label: "Animation Speed"
selector:
number:
min: 1
max: 10
step: 0.1
- name: opacity
label: "Opacity"
selector:
number:
min: 0
max: 100
unit_of_measurement: "%"
This schema creates a form with a select dropdown for color mode, a color picker, and an expandable section with animation controls. This is the example shown in the screenshot at the top of this documentation.
References
This documentation is based on Home Assistant's form schemas: