Bubble to NodeGX dictionary
A Bubble app is stored as JSON: a tree of elements, each with a type and its properties, and workflows made of actions. A NodeGX app is JSON too: components made of nodes, each with a type and its parameters, joined by connections. This page puts the two side by side, one Bubble element at a time.
The examples are real. The Bubble side is cut from the export of a Bubble landing page; the only edits are a placeholder image URL, and a scroll target moved to an element that is kept. The NodeGX side is what the rules below make from it, regenerated with the page whenever the rules change.
For Bubble's operators one by one (:count, :formatted as, :filtered), see Coming from Bubble. This page covers everything around them: elements, layout, styles, workflows and data.
Elements, one example each
Each example shows the element as it appears in the Bubble export, then the NodeGX nodes it becomes. The NodeGX node keeps the Bubble id: cmMfz becomes el_cmMfz. Canvas positions are left out.
Page
One Bubble page becomes two nodes. The Page holds the title and the URL (the index page gets ""). The Group under it lays the page out: the page's container_layout, minimum height and background colour.
Bubble
{
"id": "bTIWz",
"name": "index",
"type": "Page",
"properties": {
"title": {
"entries": {
"0": "Page Title"
},
"type": "TextExpression"
},
"new_responsive": true,
"container_layout": "column",
"min_height_px": 400,
"backdrop_bgcolor": "rgba(255, 255, 255, 1)",
"width": 1200,
"height": 4166,
"preset_width": "full_width",
"default_width": 1200,
"responsive_version": 1,
"element_version": 5
},
"elements": "(the elements below)",
"workflows": "(the workflows below)"
}
[
{
"id": "page_bTIWz",
"type": "Page",
"label": "index",
"parameters": {
"title": "Page Title",
"urlPath": ""
},
"children": [
"root_bTIWz"
]
},
{
"id": "root_bTIWz",
"type": "Group",
"label": "Page index",
"parent": "page_bTIWz",
"parameters": {
"flexDirection": "column",
"width": {
"value": 100,
"unit": "%"
},
"sizeMode": "contentHeight",
"minHeight": {
"value": 400,
"unit": "px"
},
"backgroundColor": "rgba(255, 255, 255, 1)"
},
"children": [
"el_cmMsV",
"el_cmMfz",
"el_cmMfn",
"el_cmMwK",
"el_cmMpD",
"el_cmMwz"
]
}
]
Group
A row. container_layout: "row" becomes flexDirection: "row", fit_height becomes sizeMode: "contentHeight", and the margins carry over in px. Its two children are the next two examples.
Bubble
{
"properties": {
"height": 250,
"left": 24,
"top": 0,
"width": 400,
"zindex": 2,
"background_style": "none",
"order": 1,
"fit_height": true,
"margin_top": 32,
"margin_left": 24,
"margin_right": 24,
"single_width": false,
"min_width_css": "20px",
"single_height": false,
"min_height_css": "20px",
"horiz_alignment": "center",
"container_layout": "row",
"container_horiz_alignment": "flex-start"
},
"type": "Group",
"id": "cmMsV",
"default_name": "Group BZ",
"elements": {
"cmMsa": "(MaterialIcon E, below)",
"cmMsd": "(Text U, below)"
},
"order": 1
}
NodeGX: Group
{
"id": "el_cmMsV",
"type": "Group",
"label": "Group BZ",
"parent": "root_bTIWz",
"parameters": {
"flexDirection": "row",
"justifyContent": "flex-start",
"width": {
"value": 100,
"unit": "%"
},
"minWidth": {
"value": 20,
"unit": "px"
},
"minHeight": {
"value": 20,
"unit": "px"
},
"sizeMode": "contentHeight",
"alignX": "center",
"backgroundColor": "transparent",
"marginTop": {
"value": 32,
"unit": "px"
},
"marginLeft": {
"value": 24,
"unit": "px"
},
"marginRight": {
"value": 24,
"unit": "px"
}
},
"children": [
"el_cmMsY",
"el_cmMsb"
]
}
Material icon (plugin element)
A Group sized like the Bubble element, holding the icon itself. The icon names Material Icons' check glyph, and the project links the Material Icons font for it.
Bubble
{
"properties": {
"height": 24,
"left": 0,
"top": 0,
"width": 24,
"icon": "check",
"icon_color": "rgba(11, 159, 111, 1)",
"order": 1,
"fixed_width": true,
"vert_alignment": "center"
},
"type": "materialicons-Materialicon",
"id": "cmMsY",
"default_name": "MaterialIcon E",
"order": 1
}
[
{
"id": "el_cmMsY",
"type": "Group",
"label": "MaterialIcon E",
"parent": "el_cmMsV",
"parameters": {
"alignY": "center",
"width": {
"value": 24,
"unit": "px"
},
"height": {
"value": 24,
"unit": "px"
},
"sizeMode": "explicit"
},
"children": [
"icon_cmMsY"
]
},
{
"id": "icon_cmMsY",
"type": "net.noodl.visual.icon",
"label": "MaterialIcon E glyph",
"parent": "el_cmMsY",
"parameters": {
"iconIconSource": {
"class": "material-icons",
"code": "check",
"codeAsClass": false
},
"iconSize": {
"value": 24,
"unit": "px"
},
"iconColor": "rgba(11, 159, 111, 1)"
}
}
]
Text
The text comes out of its TextExpression. font_face: "Inter:::regular" becomes fontFamily and fontWeight, and the project links Inter from Google Fonts.
Bubble
{
"properties": {
"text": {
"entries": {
"0": "Feature numero uno"
},
"type": "TextExpression"
},
"bold": false,
"font_face": "Inter:::regular",
"height": 22,
"left": 0,
"top": 0,
"width": 228,
"font_color": "rgba(107, 114, 128, 1)",
"font_size": 16,
"line_height": 1.2,
"order": 2,
"fit_width": true,
"fixed_width": false,
"font_family": "Inter",
"font_weight": "400",
"margin_left": 8,
"padding_top": 0,
"padding_left": 0,
"padding_right": 0,
"padding_bottom": 0,
"vert_alignment": "center"
},
"type": "Text",
"id": "cmMsb",
"default_name": "Text U",
"order": 1
}
NodeGX: Text
{
"id": "el_cmMsb",
"type": "Text",
"label": "Text U",
"parent": "el_cmMsV",
"parameters": {
"alignY": "center",
"sizeMode": "contentSize",
"color": "rgba(107, 114, 128, 1)",
"fontSize": {
"value": 16,
"unit": "px"
},
"fontWeight": "400",
"fontFamily": "Inter",
"lineHeight": 1.2,
"marginLeft": {
"value": 8,
"unit": "px"
},
"text": "Feature numero uno"
}
}
Fonts and icon fonts
NodeGX does not load a web font by itself, and Bubble does. So the project's head code gets one Google Fonts link carrying every family and weight the page uses, and Material Icons when an icon needs it. This is the project setting made for this sample.
NodeGX (nodegx.project.json)
{
"settings": {
"headCode": "<link rel=\"preconnect\" href=\"https://fonts.googleapis.com\">\n<link rel=\"preconnect\" href=\"https://fonts.gstatic.com\" crossorigin>\n<link href=\"https://fonts.googleapis.com/css2?family=Inter:wght@400;600&family=Material+Icons&display=swap\" rel=\"stylesheet\">"
}
}
Button
The This element is hovered condition becomes the button's hover state. Bubble's 200 ms fade on that change is the element's own transitions, which are not carried yet.
Bubble
{
"properties": {
"text": {
"entries": {
"0": "Benefit"
},
"type": "TextExpression"
},
"bold": false,
"font_face": "Inter:::600",
"height": 40,
"left": 0,
"top": 0,
"width": 60,
"font_alignment": "center",
"font_color": "rgba(107, 114, 128, 1)",
"font_size": 16,
"line_height": 1.4,
"background_style": "none",
"border_style": "none",
"order": 2,
"fixed_width": true,
"font_family": "Inter",
"font_weight": "600",
"max_width_css": "90px",
"min_width_css": "60px",
"min_height_css": "40px",
"vert_alignment": "center",
"responsive_alignment": "left"
},
"states": {
"bTGVB": {
"condition": {
"next": {
"type": "Message",
"name": "is_hovered"
},
"properties": {
"element_id": "bTGMw"
},
"type": "ThisElement"
},
"properties": {
"font_color": "rgba(22, 30, 46, 1)"
},
"type": "State"
}
},
"type": "Button",
"id": "cmMfz",
"default_name": "Button B",
"order": 2,
"transitions": {
"font_color": {
"fn": "ease-in-out",
"duration": 200
}
}
}
NodeGX: Button
{
"id": "el_cmMfz",
"type": "net.noodl.controls.button",
"label": "Button B",
"parent": "root_bTIWz",
"parameters": {
"alignY": "center",
"width": {
"value": 100,
"unit": "%"
},
"minWidth": {
"value": 60,
"unit": "px"
},
"maxWidth": {
"value": 90,
"unit": "px"
},
"minHeight": {
"value": 40,
"unit": "px"
},
"sizeMode": "contentHeight",
"backgroundColor": "transparent",
"color": "rgba(107, 114, 128, 1)",
"fontSize": {
"value": 16,
"unit": "px"
},
"fontWeight": "600",
"fontFamily": "Inter",
"lineHeight": 1.4,
"textAlignX": "center",
"label": "Benefit",
"useLabel": true
},
"stateParameters": {
"hover": {
"color": "rgba(22, 30, 46, 1)"
}
}
}
Image
The protocol-relative src gains https:. The fixed width (50) and the 10:11 aspect ratio give the height (55). The element wears the style it wore in Bubble, as a variant.
Bubble
{
"properties": {
"height": 55,
"left": 0,
"top": 0,
"width": 50,
"zindex": 2,
"src": {
"entries": {
"0": "//example.com/logo.png"
},
"type": "TextExpression"
},
"order": 3,
"fit_height": false,
"margin_left": 0,
"single_width": true,
"min_width_css": "50px",
"single_height": true,
"min_height_css": "55px",
"vert_alignment": "center",
"use_aspect_ratio": true,
"aspect_ratio_width": 10,
"aspect_ratio_height": 11
},
"type": "Image",
"id": "cmMfn",
"default_name": "Image A",
"style": "Image_simple_image_",
"order": 3
}
NodeGX: Image
{
"id": "el_cmMfn",
"type": "Image",
"label": "Image A",
"parent": "root_bTIWz",
"variant": "Image_simple_image_",
"parameters": {
"alignY": "center",
"width": {
"value": 50,
"unit": "px"
},
"minWidth": {
"value": 50,
"unit": "px"
},
"height": {
"value": 55,
"unit": "px"
},
"sizeMode": "explicit",
"marginLeft": {
"value": 0,
"unit": "px"
},
"src": "https://example.com/logo.png",
"objectFit": "contain"
}
}
The style Image_simple_image_, as a variant in nodegx.styles.json:
{
"name": "Image_simple_image_",
"typename": "Image",
"parameters": {},
"stateParameters": {},
"stateTransitions": {}
}
Link
A Text with the link's words and its hover colour. This Link goes to a URL, which is not wired yet; a Link to a page also gets a Router Navigate on its click.
Bubble
{
"properties": {
"text": {
"entries": {
"0": "FAQ"
},
"type": "TextExpression"
},
"bold": false,
"font_face": "Inter:::regular",
"height": 45,
"left": 0,
"top": 0,
"width": 200,
"font_color": "rgba(107, 114, 128, 1)",
"font_size": 16,
"line_height": 1.2,
"vertical_centering": true,
"order": 4,
"fit_width": true,
"fit_height": true,
"font_family": "Inter",
"font_weight": "400",
"padding_top": 0,
"padding_left": 0,
"single_width": false,
"min_width_css": "0px",
"padding_right": 0,
"single_height": false,
"min_height_css": "40px",
"padding_bottom": 0,
"horiz_alignment": "flex-start"
},
"states": {
"0": {
"condition": {
"next": {
"type": "Message",
"name": "is_hovered"
},
"properties": {
"element_id": "bTGMw"
},
"type": "ThisElement"
},
"properties": {
"font_color": "rgba(22, 30, 46, 1)"
},
"type": "State"
}
},
"type": "Link",
"id": "cmMwK",
"default_name": "Link E",
"order": 4,
"transitions": {
"font_color": {
"fn": "ease",
"duration": 200
}
}
}
NodeGX: Text
{
"id": "el_cmMwK",
"type": "Text",
"label": "Link E",
"parent": "root_bTIWz",
"parameters": {
"minHeight": {
"value": 40,
"unit": "px"
},
"sizeMode": "contentSize",
"alignX": "left",
"color": "rgba(107, 114, 128, 1)",
"fontSize": {
"value": 16,
"unit": "px"
},
"fontWeight": "400",
"fontFamily": "Inter",
"lineHeight": 1.2,
"text": "FAQ"
},
"stateParameters": {
"hover": {
"color": "rgba(22, 30, 46, 1)"
}
}
}
Shape
A Group with the shape's colour, size and rounded corners.
Bubble
{
"properties": {
"height": 80,
"left": 0,
"top": 0,
"width": 160,
"zindex": 2,
"border_roundness": 5,
"bgcolor": "rgba(60, 13, 153, 1)",
"order": 5,
"fit_height": false,
"margin_left": 41,
"single_width": true,
"min_width_css": "160px",
"single_height": true,
"min_height_css": "80px",
"nonant_alignment": "aa"
},
"type": "Shape",
"id": "cmMpD",
"default_name": "Shape A",
"order": 5
}
NodeGX: Group
{
"id": "el_cmMpD",
"type": "Group",
"label": "Shape A",
"parent": "root_bTIWz",
"parameters": {
"width": {
"value": 160,
"unit": "px"
},
"minWidth": {
"value": 160,
"unit": "px"
},
"height": {
"value": 80,
"unit": "px"
},
"sizeMode": "explicit",
"backgroundColor": "rgba(60, 13, 153, 1)",
"borderRadius": {
"value": 5,
"unit": "px"
},
"marginLeft": {
"value": 41,
"unit": "px"
}
}
}
Floating group
A Group with the same shadow and corners, unmounted (mounted: false) because it starts hidden. It does not float yet: Bubble pins a Floating Group to the window, and this Group scrolls with the page. Its children are left out of this sample.
Bubble
{
"properties": {
"height": 287,
"left": 0,
"top": 0,
"width": 1190,
"zindex": 2,
"boxshadow_horizontal": 0,
"border_roundness": 5,
"boxshadow_style": "outset",
"is_visible": false,
"background_style": "bgcolor",
"bgcolor": "rgba(255, 255, 255, 1)",
"background_image": {
"entries": {
"0": ""
},
"type": "TextExpression"
},
"boxshadow_color": "rgba(170,170,170,0.5)",
"boxshadow_spread": 1,
"fit_height": false,
"single_width": false,
"min_width_css": "98%",
"single_height": false,
"use_max_width": true,
"min_height_css": "100%",
"horiz_alignment": "flex-start",
"container_layout": "column",
"background_size_cover": true,
"background_color_if_empty_image": "rgba(255,255,255,1)",
"order": 6
},
"type": "FloatingGroup",
"id": "cmMwz",
"default_name": "FloatingGroup A",
"name": "Floating Mobile Nav Menu",
"order": 6
}
NodeGX: Group
{
"id": "el_cmMwz",
"type": "Group",
"label": "Floating Mobile Nav Menu",
"parent": "root_bTIWz",
"parameters": {
"flexDirection": "column",
"width": {
"value": 100,
"unit": "%"
},
"minWidth": {
"value": 98,
"unit": "%"
},
"mounted": false,
"minHeight": {
"value": 100,
"unit": "%"
},
"sizeMode": "contentHeight",
"alignX": "left",
"backgroundColor": "rgba(255, 255, 255, 1)",
"borderRadius": {
"value": 5,
"unit": "px"
},
"boxShadowEnabled": true,
"boxShadowOffsetX": {
"value": 0,
"unit": "px"
},
"boxShadowOffsetY": {
"value": 0,
"unit": "px"
},
"boxShadowBlurRadius": {
"value": 0,
"unit": "px"
},
"boxShadowSpreadRadius": {
"value": 1,
"unit": "px"
},
"boxShadowColor": "rgba(170,170,170,0.5)"
}
}
A workflow
Two Bubble workflows from the same page. Clicking Button B triggers the custom event Scroll To with the text "benefit"; the event scrolls to Group BZ only when its text is "benefit".
Bubble
[
{
"properties": {
"element_id": "cmMfz"
},
"type": "ButtonClicked",
"id": "cmMwl",
"actions": {
"0": {
"properties": {
"arguments": {
"0": {
"param_id": "_LEGACY_",
"arg_value": "benefit"
}
},
"custom_event": "cmMvZ"
},
"type": "TriggerCustomEvent",
"id": "cmMwo"
}
}
},
{
"properties": {
"event_name": "Scroll To",
"parameters": {
"0": {
"btype_id": "text",
"param_id": "_LEGACY_",
"param_name": "Workflow's text"
}
},
"event_color": "orange"
},
"type": "CustomEvent",
"id": "cmMvZ",
"actions": {
"0": {
"properties": {
"condition": {
"next": {
"args": "benefit",
"type": "Message",
"name": "equals"
},
"type": "CurrentWorkflowItem"
},
"element_id": "cmMsV",
"offset": -80
},
"type": "ScrollToElement",
"id": "cmMvf"
}
}
}
]
NodeGX: Send Event, Receive Event, Condition, Expression
[
{
"id": "act_cmMwo",
"type": "Event Sender",
"label": "Trigger cmMvZ",
"parameters": {
"channelName": "cmMvZ",
"propagation": "global"
}
},
{
"id": "ev_cmMvZ",
"type": "Event Receiver",
"label": "Custom event Scroll To",
"parameters": {
"channelName": "Scroll To"
}
},
{
"id": "ac_1",
"type": "Condition",
"label": "Only when CurrentWorkflowItem's equals \"benefit\"",
"parameters": {}
},
{
"id": "ex_2",
"type": "Expression",
"label": "Only when CurrentWorkflowItem's equals \"benefit\"",
"parameters": {
"expression": "(param_payload == \"benefit\")",
"evaluateAtLoad": true
}
},
{
"id": "act_cmMvf",
"type": "Expression",
"label": "ScrollToElement Group BZ (small action)",
"parameters": {
"expression": "/* ScrollToElement Group BZ */ true"
}
}
]
And the connections that make it run, in order:
| From | Output | To | Input |
|---|---|---|---|
el_cmMfz (Button B) | onClick | act_cmMwo (Trigger cmMvZ) | sendEvent |
act_cmMvf (ScrollToElement Group BZ (small action)) | done | el_cmMsV (Group BZ) | scrollIntoView |
ev_cmMvZ (Custom event Scroll To) | payload | ex_2 (Only when CurrentWorkflowItem's equals "benefit") | param_payload |
ex_2 (Only when CurrentWorkflowItem's equals "benefit") | result | ac_1 (Only when CurrentWorkflowItem's equals "benefit") | condition |
ev_cmMvZ (Custom event Scroll To) | eventReceived | ac_1 (Only when CurrentWorkflowItem's equals "benefit") | eval |
ac_1 (Only when CurrentWorkflowItem's equals "benefit") | ontrue | act_cmMvf (ScrollToElement Group BZ (small action)) | run |
The rules
Everything the dictionary knows, including what the example page does not use. A row marked not in the dictionary yet is a Bubble feature with no NodeGX answer so far. It is listed so you know.
Elements
| Bubble | NodeGX | Notes |
|---|---|---|
a page (type: "Page") | a /Pages/<name> component: a Page (title, urlPath; the index page gets "") over a root Group | The page's own layout, gaps, padding and background go on the root Group. |
Group, Shape, Popup, FloatingGroup, GroupFocus, Alert | Group | A Popup or GroupFocus starts with mounted: false. A Floating Group does not float yet: it scrolls with the page. A Shape keeps its box, but its shape type (ellipse, triangle) is not read yet. |
Text | Text | BBCode such as [b] and [sup] is stripped. A Text with a background or a border becomes a Group carrying the box around a Text, because a NodeGX Text has no box. |
Link | Text, plus a Navigate wired from its onClick when it links to a page | A Link to an external URL is not wired yet. An icon-only Link gets text: "". |
Button | Button | text becomes label, with useLabel: true. |
Input, MultiLineInput | Text Input | placeholder carries over. content_format password, email, number, phone or url sets type. A MultiLineInput gets type: "textArea". |
Checkbox | Checkbox | text becomes label. |
Dropdown, RadioButtons, AutocompleteDropdown | Dropdown | The data source becomes an Expression wired to items. |
Image | Image | src gains https: when Bubble wrote it protocol-relative (//s3…). objectFit is contain, which is how Bubble shows an image. A fixed-width image with an aspect ratio takes its height from the ratio. |
Icon, and the Material Icons plugin element | a Group holding an Icon | A Material icon becomes iconIconSource: { class: "material-icons", code: "<name>" }, and the project links the Material Icons font in its head code. |
RepeatingGroup | a Group holding a Repeater whose template is a row component /Rows/<page> <name> | The cell's elements go into the row component. A Component Inputs node gets one port per field the cell reads, and the data source becomes an Expression wired to items. |
| a reusable element | a /Reusable/<name> component, placed where the element was used | The instance keeps only its position and size. |
| a custom state | a Variable named <element>.<state> | Its default value carries over. |
HTML, Video, GoogleMap, SliderInput, DateInput, FileInput, PictureInput, any other plugin element | a labelled Group placeholder | Not in the dictionary yet. The page still opens, and the placeholder says what stood there. |
Layout
Bubble's responsive engine is flexbox underneath, so most of it maps one property to one parameter.
| Bubble | NodeGX | Notes |
|---|---|---|
container_layout row or column, row_gap / column_gap, container_horiz_alignment / container_vert_alignment | flexDirection, rowGap / columnGap, alignItems / justifyContent | |
single_width / single_height (fixed), fit_width / fit_height, min_width_css / max_width_css, horiz_alignment | width / height in px, sizeMode, minWidth / maxWidth, alignX | Stretch becomes width: 100%. fixed_width is ignored on a new-engine page, where single_width is the flag. |
vert_alignment of a child in a row | alignY | |
container_layout: "relative" (Align to parent) with nonant_alignment | the biggest child stays in flow and gives the container its height; the others are position: absolute with alignX / alignY from the nonant | The nonant's letter order is a guess: every example so far reads the same either way. |
a fixed container, or a page with no container_layout (the pre-2021 engine) | children position: absolute, placed by marginLeft / marginTop from left / top, inside a position: relative parent | It renders the same and edits badly. Re-flowing it into rows and columns is a judgement for a person or an AI. |
rotation_angle | transformRotation | |
| how a row shares its width among children that are not fixed | equal shares | Not in the dictionary yet: Bubble's rule is not known, so columns with different minimum widths come out a different size. |
Style
| Bubble | NodeGX | Notes |
|---|---|---|
bgcolor, background_style (gradient or none), border_*, border_roundness, boxshadow_*, opacity | backgroundColor, backgroundGradient, borderStyle / borderWidth / borderColor, borderRadius, boxShadow*, opacity | |
font_color, font_size, font_family / font_face: "Inter:::600", line_height, letter_spacing, font_alignment | color, fontSize, fontFamily + fontWeight, lineHeight, letterSpacing, textAlignX | Every family that is not built into the browser is added to one Google Fonts link in the project's head code, with the weights the page uses. |
padding_*, margin_* | padding*, margin* | |
settings.client_safe.color_tokens / font_tokens | custom design tokens with Bubble's own names (--color_primary_default), plus an _rgb triple for each colour | Bubble's rgba(var(--color_primary_default_rgb), 0.8) keeps working unchanged. |
a style (styles.<id>) | a variant { name, typename, parameters, stateParameters } in nodegx.styles.json, which the element wears as variant | The element drops every parameter the variant already sets. |
| a condition This element is hovered / focused / pressed | stateParameters.hover / focused / pressed | A style's transitions become the variant's stateTransitions. An element's own transitions are not carried yet, so its hover change is instant. |
| any other condition | one Expression per changed property: (<condition>) ? <state value> : <base value> | is_visible drives mounted, or visible when the element keeps its space while hidden. This input is (not) valid has no NodeGX state yet. |
Workflows
| Bubble | NodeGX | Notes |
|---|---|---|
ButtonClicked, ElementClicked | the element's onClick | |
PageLoaded | the Page's didMount | |
InputValueChanged | the input's onTextChanged | |
CustomEvent | Receive Event on a channel named after the event | |
| the action chain | one node per action: the event triggers the first, and each action's done triggers the next | An action's Only when becomes a Condition between them, fed by an Expression. |
TriggerCustomEvent | Send Event | |
NewThing, MakeChanges, DeleteThing | Create Record, Update Record, Delete Record | Each value becomes a prop-<field> input: a direct wire when it is one input's value, an Expression otherwise. |
ChangePage | Navigate to /Pages/<name> | Page parameters are not carried yet. |
GoPrevious | Pop Component Stack | |
OpenURL | External Link | |
LogIn, SignUp, LogOut, ResetPassword | Log In, Sign Up, Log Out, Reset Password | Reset Password takes its token from the URL. |
SetCustomState, DisplayGroupData | Set Variable | |
ShowElement, HideElement | a Set Variable on <element>.mounted, which the element's mounted reads | |
ScrollToElement, SetFocusToElement | an Expression that passes the signal on to the element's scrollIntoView or focus | Other one-signal actions (ResetInputs, AnimateElement, …) keep their place in the chain with nothing on the end yet. |
ScheduleAPIWorkflow, SendEmail, plugin actions, any other action | an Expression placeholder carrying the action's settings in a comment | Not in the dictionary yet. The chain stays whole, so the workflow still runs past it. |
Dynamic expressions
A Bubble expression (Input A's value :count > 3) becomes one Expression node. Each value it reads is an input wired from the node that holds it; each operator is a line of JavaScript. For the operators one by one, see Coming from Bubble.
| Bubble | NodeGX | Notes |
|---|---|---|
| Input A's value, Checkbox B's value | a wire from that element's value output | |
| Current User, Current User is logged in | a User node's id, email or authenticated | |
| Get data from page URL | Page Inputs, one pm-<name> output per parameter | |
| Current page width / height | Screen Resolution | |
| Get data from an external API (API Connector) | one HTTP Request node per call, fetched when the page mounts | The call's parsed types give the field paths. Auth headers and keys are not carried. |
| Parent group's thing inside a repeating group | the row component's Component Inputs | |
| an operator not in the table | __bubble("<name>", …) in the Expression | Not in the dictionary yet. The page opens and the expression says what it was. |
Data
| Bubble | NodeGX | Notes |
|---|---|---|
user_types (data types) | backend collections in the project (metadata.dbCollections) | text, number, yes/no, date, list and another type become String, Number, Boolean, Date, Array and Pointer. |
| option sets | an /Option sets/<name> component whose items output lists the options | One option in an expression becomes { value, display }. |
| privacy rules | not converted yet | They use the same expression language, so they will be a table row. Until then a person writes the NodeGX security rules. |