MARKETPLACE
PLUGINS
TOOLBOX - CUSTOM JS RUNNER
Toolbox - Custom JS runner logo

Toolbox - Custom JS runner

Published June 2026
   •    Updated this week

Plugin details

Execute your own JavaScript in a native mobile app and get real Bubble values back: numbers, dates, lists and objects, not just strings. Async/await support, automatic type conversion both ways, a configurable timeout for async code, and rock-solid error handling that never crashes your app. The native alternative to web Toolbox.
Your code runs in the app's own JavaScript engine, so there is no WebView and no slow load.

-> See it in action in our demo app: https://betternotcode-plugins.bubbleapps.io/version-test/api/1.1/mobile/preview?debug_mode=true&preview_view=custom_js_runner
-> See in editor: https://bubble.io/page?id=betternotcode-plugins&app_type=mobile&tab=Design&name=custom_js_runner&type=page&version=test&elements=bTSdM
-> User documentation: https://betternotcode.notion.site/Toolbox-Custom-JS-runner-native-mobile-for-Bubble-io-User-Documentation-3824240d2e28801e9511c6e80fe66632


How it works

Drop one invisible element on the page.
Call the Run JavaScript action with your code.
Return any value and the plugin detects its type for you. A number lands in result_number, a date in result_date as a real Bubble date, a list in the matching list state, an object in result_json. A result_type state tells you what came back, and result_text is always a readable version.



Coming from the web Toolbox?

The migration is mostly about dropping the workarounds you no longer need:

-> The action and the typed states now live in one element, instead of a Run javascript action paired with a JavascriptToBubble element.
-> You publish a result with return value, instead of bubble_fn_suffix(value).
-> The output type is detected for you, instead of declared by hand.
-> Numbers and dates pass through typed, so the "format as text" tricks and the date re-parsing are gone.
-> List inputs are real arrays, with no proxy gymnastics.

async/await also works better than on web: it is real promise support, not a fire-and-forget call.


Why there is no "write the whole function" field

You might expect a single field where you type a whole function and drop Bubble dynamic expressions straight into the code. We left that out on purpose, because inserting a dynamic value into a text field turns it into a string, so your numbers and dates would lose their type. A stray quote or line break would break the code outright.

Plus, any value coming from an end user would be concatenated into your code, which is an open door to injection.

Typed parameters avoid all of this: a value is always data, never code. So you write your code once, reference the parameters by name, and bind your Bubble expressions to them.

If you need more parameters than the built-in ones, you can always put a JSON string in a text parameter and parse it in your code.


Elegant error handling

Every run is wrapped so nothing reaches your page uncaught. Syntax errors, runtime exceptions, rejected promises, timeouts and unsupported values all come back as a readable error_message with a stable code you can branch on (NO_CODE, SYNTAX_ERROR, RUNTIME_ERROR, PROMISE_REJECTED, TIMEOUT, etc.).

If two runs overlap, only the newest one publishes (button-mashing won't leave a stale result on screen).


Documentation and AI support

We built the user documentation we wish every plugin had.

The plugin also ships with a context document you can paste into any AI assistant. Once pasted, it knows every action, field, state and common pitfall, so it can write your Bubble expressions or diagnose an error for you. It is downloadable from the documentation link.


This plugin only works in native apps

It will not work for web apps or WebView-wrapped apps. BDK, Natively and similar wrappers do not use the React Native runtime the plugin relies on.


Try risk-free

The monthly subscription is prorated by the day. Try the plugin for a few days; if it is not the right fit, cancel and pay only for the days you used.

$29

One time  •  Or  $6/mo

stars   •   0 ratings
0 installs  
This plugin does not collect or track your personal data.

Platform

Native mobile

Contributor details

BetterNotCode logo
BetterNotCode
Joined 2020   •   8 Plugins
View contributor profile

Instructions

Quick Start
Step 1 — Place the "Toolbox - Custom JS Runner" element onto your mobile view. It is invisible at runtime (you'll see a small badge in the editor only). One element per view is enough.

Step 2 — In any workflow, add the action "Run JavaScript" (under your JS Runner element's actions) and write your code. For example, for the following code:

return number1 * (1 + number2 / 100);

Fill the number1 and number2 fields with static or dynamic values.

If you prefer, you may also use exportToBubble(value) to send the result back.

Step 3 — Read the result. Bind your UI to the element's states (in our example, Custom JS Runner's result_number).

-> if your returned value is of type "text", read it in "result_text"
-> if your returned value is of type "number", read it in "result_number" (NaN and Infinity raise a clear error instead)
-> if your returned value is of type "boolean", read it in "result_yes_no" (result_text shows yes/no)
-> if your returned value is a date, read it in "result_date" (a real Bubble date: format it, compare it, save it)
-> if your returned value is an array of numbers, read it in "result_list_numbers" (you may bind a Repeating Group directly)
-> if your returned value is an array of texts, read it in "result_list_texts"
-> if your returned value is an array of dates, read it in "result_list_dates"
-> if your returned value is a mixed array, read it in "result_list_texts" or "result_json" (items are stringified)
-> if your returned value is an empty array, every list state stays empty
-> if your returned value is a plain object, read it in "result_json" (clean JSON; dates inside become ISO strings)
-> if you return nothing or null, the run still succeeds with empty result states

Whatever the type: "result_text" always holds a readable rendering, and "result_json" always holds the JSON rendering. Every list also fills "result_list_texts".

If you want it event-driven, use the element's events: Custom JS Runner finished successfully, Custom JS Runner failed, and Custom JS Runner finished (success or error) (always fires last).


If you return something that cannot cross into Bubble (a function, undefined inside an object, NaN, a circular structure, a Map…), the run fails with a message that names the exact problem.

Types

This plugin can be found under the following types:
Element   •   Event   •   Action

Categories

This plugin can be found under the following categories:
Productivity   •   Technical   •   Analytics   •   Data (things)   •   Compliance   •   Visual Elements

Resources

Support contact
Documentation
Tutorial

Rating and reviews

No reviews yet

This plugin has not received any reviews.
Bubble