How to send data from JavaScript to Bubble with Toolbox Plugin and localStorage
This tutorial will guide you step-by-step on how to leverage the Javascript to Bubble element from the Bubble Toolbox plugin to send data from your client-side JavaScript code into your Bubble workflows and element logic. This technique is particularly useful for managing client-side state (like an "add to compare" list in localStorage) and synchronizing it with your Bubble application's backend or UI.
Scenario: We'll use the example of an "Add to Compare" feature where a list of product slugs is managed in the user's browser using localStorage and then sent to Bubble for persistence and display on a comparison page.
Setup (Install plugin and add the element)
The first step is to ensure you have the necessary plugin and element set up in your Bubble application.
Step 1: Install the Toolbox Plugin
The Javascript to Bubble element is part of the free Toolbox plugin.
- Open your Bubble application in the editor.
- Navigate to the Plugins tab in the left-hand sidebar.
- Click the + Add plugins button.
- In the search bar, type "Toolbox".
- Locate the "Toolbox" plugin by Bubble and click the Install button.
- Once installed, click Done.
Step 2: Add the "Javascript to Bubble" element to your page
You need to place an instance of the Javascript to Bubble element on the page where your JavaScript code will be executed (in our "Add to Compare" example, this would be the product detail page).
-
Go to the Design tab and open the page where your "Add to Compare" button and associated JavaScript will live (e.g., your product detail page).
-
In the element palette (usually on the left-hand side), find the "JavascriptToBubble" element. It's typically found under Visual elements or sometimes grouped with other Toolbox elements.
-
Drag and drop an instance of the "JavascriptToBubble" element onto your page canvas.
- Note: This element is invisible on the live page. Its purpose is purely functional (receiving data from JS and triggering Bubble events). You can place it anywhere convenient on the page, even stacked with other invisible elements.
Step 3: Configure the "Javascript to Bubble" element
Now, you need to configure the element so your JavaScript knows how to target it and so it triggers a workflow when data is sent.
-
With the newly added JavascriptToBubble element selected on the page, open its Property Editor.
-
Name the Element: Give the element a descriptive name. This name is crucial as it determines the name of the JavaScript function you'll call later. Let's name it jsToBubble_CompareList to match the example in your script.
-
Enable Event Triggering: Check the box labeled "Trigger event". This tells the element to fire a Bubble workflow event whenever it receives data from JavaScript.
-
Publish Value Type: Look at the "Publish value as" option. You can specify if the element should publish the incoming data as text, a number, a list, etc. For a comma-separated string of slugs like 'slug1,slug2,slug3', the default 'text' is perfectly fine. Bubble will treat the incoming string as a single text value.
-
Understand the Function Name: The most critical configuration is how your JavaScript will reference this specific element. Bubble automatically generates a JavaScript function name based on the element's name or a specific "Function name" setting (depending on the Toolbox version).
- Common Convention: If your element is named YourElementName, the corresponding JavaScript function will usually be bubble_fn_YourElementName.
- Matching Your Script: Since your provided JavaScript uses the call bubble_fn_jsToBubble_CompareList(finalCompareList);, ensure your element's name (or the dedicated "Function name" field if present) results in bubble_fn_jsToBubble_CompareList. By naming the element jsToBubble_CompareList in Step 2, Bubble will automatically create the function bubble_fn_jsToBubble_CompareList.
Sending data from JavaScript (on the main page)
Now that the receiving element is set up, you need to execute JavaScript code on your product page that gathers the data and sends it to the JavascriptToBubble element.
Step 1: Locate/write JavaScript logic
Your existing JavaScript code for managing the localStorage list is the core of this phase. It calculates the final list of slugs you want to send to Bubble.
// Example JavaScript code (based on your provided snippet)
// This code typically runs when the "Add to Compare" button is clicked
// --- Logic to calculate the list of slugs ---
// This part needs to be dynamic in Bubble Run Javascript action
// let newVal = 'Current page\'s Tool\'s Slug';
// const current = localStorage.getItem('items_to_compare');
// if (current) {
// newVal = `${current},${newVal}`;
// }
// newVal = newVal.split(',');
// // Filter out empty strings and duplicates
// newVal = newVal.filter(tool => tool && typeof tool === 'string').filter((tool, index, self) => self.indexOf(tool) === index);
// const finalCompareList = newVal.join(',');
// localStorage.setItem('items_to_compare', finalCompareList);
// --- End of logic ---
// THIS IS THE KEY LINE TO SEND DATA TO BUBBLE:
// Make sure 'jsToBubble_CompareList' matches the name of your Javascript to Bubble element.
bubble_fn_jsToBubble_CompareList(finalCompareList);
Step 2: Place the JavaScript code in a Bubble workflow action
The best place to execute this client-side JavaScript in Bubble, typically triggered by a user action like clicking a button, is the "Run javascript" workflow action provided by the Toolbox plugin.
- Go to the Workflow tab.
- Create a new workflow event that will trigger this process. For the "Add to Compare" feature, the event would be "An Element is clicked" -> Select your "Add to Compare" button element.
- Add a new action to this workflow.
- Search for the "Run javascript" action (also from the Toolbox plugin).
- Click on the "Run javascript" action to configure it. You'll see a large code input area.
- Paste your entire JavaScript snippet (the logic for managing localStorage and the bubble_fn_... call) into this code area.
Step 3: Make dynamic data work within the JavaScript
Your JavaScript needs to know the slug of the current product being viewed. You cannot directly use Bubble's dynamic expressions inside the JavaScript string literals like 'Current page\'s Tool\'s Slug'. You need to use Bubble's dynamic data feature around or to provide the string within the "Run javascript" action.
-
In the "Run javascript" action's code area, find the line where you define newVal.
-
Replace the placeholder slug string with a Bubble dynamic expression for the current product's slug.
-
Important Formatting: Bubble's dynamic expressions are inserted outside the literal JavaScript string context. You need to make sure the resulting JavaScript code is valid. The easiest way is often to build the string dynamically.
-
Click on the spot where the slug should be. Bubble's dynamic data inserter will appear.
-
Select the appropriate dynamic data source for the current product's slug (e.g., Current page Product's Slug, RepeatingGroup YourProducts's current cell's Product's Slug, etc.).
-
Crucially: The dynamic expression should resolve to a JavaScript string literal. This means it needs to be wrapped in quotes within the final JavaScript code.
- If your line is let newVal = 'placeholder';
- You would replace 'placeholder' with the dynamic expression including the quotes around it. Bubble handles inserting the dynamic value at that point.
- Example: let newVal = "Current page Product's Slug"; (Here, "Current page Product's Slug" is a Bubble dynamic expression. Bubble will replace this whole expression, including the quotes, with the actual slug wrapped in quotes, e.g., "my-awesome-product-slug").
// Modified JavaScript within the "Run javascript" action: let newVal = "Current page Product's Slug"; // <--- Replace this whole expression with Bubble dynamic data const current = localStorage.getItem('items_to_compare'); if (current) { newVal = `${current},${newVal}`; } newVal = newVal.split(','); // Filter out empty strings and duplicates newVal = newVal.filter(tool => tool && typeof tool === 'string').filter((tool, index, self) => self.indexOf(tool) === index); const finalCompareList = newVal.join(','); localStorage.setItem('items_to_compare', finalCompareList); // Call the Bubble function with the calculated list string bubble_fn_jsToBubble_CompareList(finalCompareList);
-
-
The workflow for the "Add to Compare" button now looks like this:
- When Button "Add to Compare" is clicked
- Action: Run javascript (Contains your script, which updates localStorage and calls bubble_fn_jsToBubble_CompareList with the comma-separated list string).
Receive and Process data in Bubble (main page logic)
When your JavaScript successfully executes bubble_fn_jsToBubble_CompareList(finalCompareList);, it triggers an event on the jsToBubble_CompareList element because you checked "Trigger event" during its setup. You'll now create a Bubble workflow to catch this event and process the data.
Step 1: Create a new workflow for the JavascriptToBubble event
- Go to the Workflow tab.
- Click "Click here to add an event...".
- Select Elements from the list of event types.
- Choose "A JavascriptToBubble event is triggered".
- A dropdown will appear asking you to select the element. Choose your jsToBubble_CompareList element from the list.
Step 2: Access the data sent from JavaScript
Inside the actions of this newly created workflow (When jsToBubble_CompareList event is triggered), you can access the data that was passed as an argument to the bubble_fn_... function in your JavaScript.
- The data is available via the expression This JavascriptToBubble's value.
- Since we configured the element to publish as 'text' and passed a comma-separated string, This JavascriptToBubble's value will contain that string (e.g., "slug1,slug2,slug3").
Step 3: Process and store the data in Bubble
The comma-separated string is useful, but Bubble often works better with lists. You'll want to convert this string into a Bubble list of texts and then store it.
-
Conversion: Use the :split by (,) operator in Bubble to convert the string "slug1,slug2,slug3" into a Bubble list of texts ["slug1", "slug2", "slug3"]. The expression will be This JavascriptToBubble's value :split by (,).
-
Storage Options: You have two main places to store this list:
-
Option A: Update a custom state (for immediate page feedback)
-
Purpose: Useful for quickly updating UI elements on the current page based on the new list (e.g., changing the button text, showing a count). Data stored in a custom state is client-side and doesn't persist across pages or user sessions (unless you manage persistence manually).
-
Setup:
- Select the page or a reusable element on the page (like a header) in the Design tab.
- Click the info icon (i) next to the element's name in the Property Editor.
- Scroll down to Custom states.
- Click "+ Add a new custom state".
- Give it a Name (e.g., compare_list_slugs).
- Set the State type to text.
- Check the "This state is a list (multiple entries)" box.
-
Workflow action:
- In the When jsToBubble_CompareList event is triggered workflow, add a new action.
- Choose Element Actions -> Set state.
- Select the element where you created the custom state (e.g., Index page).
- Select the custom state (compare_list_slugs).
- For the Value, enter the expression: This JavascriptToBubble's value :split by (,).
-
Now you can reference [Page/Element Name]'s compare_list_slugs anywhere on that page for UI logic.
-
-
Option B: Store in the database (for persistence and comparison page display)
-
Purpose: Essential if you want the list to follow the user across different pages and be available on the dedicated comparison page. Storing on the User data type is standard for logged-in users.
-
Setup:
- Go to the Data tab -> Data types.
- Select the built-in User data type (or your custom user type).
- Click "Create a new field".
- Give it a Field name (e.g., comparison_slugs).
- Set the Field type to text.
- Check the "This field is a list (multiple entries)" box.
-
Workflow action:
- In the When jsToBubble_CompareList event is triggered workflow, add a new action.
- Choose Data (Things) -> Make changes to current user... (Requires the user to be logged in).
- Click "Change another field".
- Select the field you just created (comparison_slugs).
- For the Value, enter the expression: This JavascriptToBubble's value :split by (,).
-
Note on anonymous users: If you need to support comparison for users who are not logged in, storing on the Current User field won't work reliably long-term or across different browsers/devices. You might need a more advanced strategy like:
- Creating a temporary Comparison Session data type and linking it to the browser's session using a cookie/parameter.
- Relying purely on localStorage until the user logs in or signs up, and then syncing the localStorage list to the database. The localStorage approach handled by your JavaScript already partially addresses this client-side. The Bubble workflow triggered by JS could potentially check if the user is logged in and either update the User record or update a temporary session object.
-
-
Display compared items on the comparison page
Now that the list of slugs is stored in Bubble (preferably in the User's database record), you can easily retrieve and display the actual product details on a separate comparison page.
Step 1: Retrieve the list of slugs
On your comparison page, you need to access the stored list of slugs. If you followed Option B in Phase 3, this list is stored on the Current User.
- The list is available as Current User's comparison_slugs. This is a Bubble list of texts.
Step 2: Display products in a Repeating Group
Use a Repeating Group to fetch and display the products whose slugs are in the user's comparison list.
-
Go to your comparison page in the Design tab.
-
Add a Repeating Group element to the page.
-
Configure the Repeating Group in its Property Editor:
-
Type of content: Set this to the data type of your products (e.g., Product).
-
Data source: Click here to define where the RG gets its data.
-
Choose "Search for...".
-
Select your product data type (e.g., Product).
-
Add a Constraint to the search. This is how you filter the products to match the user's list.
-
Click "Add a new constraint".
-
Select the field on your Product data type that stores the unique slug (e.g., Slug).
-
Choose the constraint operator "is in".
-
In the input box next to "is in", enter the dynamic expression for the user's list of slugs: Current User's comparison_slugs.
- This constraint tells Bubble to find all Product items where their Slug field's value is present in the list of slugs stored on the Current User.
-
-
Inside the Repeating Group's first cell, place elements (Text, Image) to display the product details (e.g., Current cell's Product's image, Current cell's Product's name, etc.).
This Repeating Group will automatically display all the products that correspond to the slugs saved in the logged-in user's comparison_slugs field.
Summary flow
To recap the entire process:
-
User Clicks "Add to Compare" on main page:
-
Triggers a Bubble workflow.
-
Run javascript action executes.
-
JavaScript code:
- Retrieves current list from localStorage.
- Adds the current product's dynamic slug.
- Removes duplicates/empty entries.
- Saves the updated comma-separated string back to localStorage.
- Calls bubble_fn_jsToBubble_CompareList(finalCompareList);
-
-
jsToBubble_CompareList Element Event Triggered:
- The bubble_fn_... call triggers the "A JavascriptToBubble event is triggered" workflow associated with the jsToBubble_CompareList element.
-
Workflow Actions Process/Store Data:
- Accesses the data via This JavascriptToBubble's value (the comma-separated string).
- Uses :split by (,) to convert it into a Bubble list of texts.
- Sets a Custom State (e.g., compare_list_slugs) for immediate UI updates on the current page.
- Makes changes to the Current User's comparison_slugs list field in the database for persistence.
-
Comparison Page Display:
- A Repeating Group's data source is set to Search for Products with the constraint Slug is in Current User's comparison_slugs.
- The RG displays the found products.
This setup allows you to leverage the speed and client-side capabilities of JavaScript and localStorage while seamlessly integrating and synchronizing that data with Bubble's powerful backend and workflow engine for persistence, multi-page access, and database operations.
ABOUT ME
I'm Juliet Edjere, a no-code professional focused on automation, product development, and building scalable solutions with no coding knowledge.
I document all things MVP validation and how designs, data, and market trends connect.
Click. Build. Launch.
Visit my website → built with Carrd and designed in Figma