dioxus-use-js
A macro that generates Rust bindings to JavaScript or TypeScript functions, with compile time checks. For use with Dioxus. No need to use eval directly anymore!
Works across all eval supported platforms (Web, Desktop, Mobile, and liveview) — no wasm-bindgen required.
JavaScript Usage
You can write plain JavaScript and bind exported functions directly.
// assets/example.js
export
Bind it in Rust:
use use_js;
use_js!;
Generated Rust signature:
async ;
Use it like:
let val: String = greeting.await?;
TypeScript Usage
If you use TypeScript, the macro will parse types to produce more accurate Rust bindings. See the Type Mapping section for details on how TypeScript types are mapped to Rust types.
// js-utils/example.ts
export function greeting(from: string, to: string): string {
return `Hello ${to}, this is ${from} speaking from JavaScript!`;
}
Build with:
Bind with:
use_js!;
Generated Rust signature:
async ;
Macro Syntax
Js
use_js!;
use_js!;
use_js!;
Ts
use_js!;
use_js!;
use_js!;
Type Mapping
Built-in TypeScript Types
| TypeScript | Rust Input | Rust Output |
|---|---|---|
string |
&str |
String |
number |
f64 |
f64 |
boolean |
bool |
bool |
T | null |
Option<&T> |
Option<T> |
T[] |
&[T] |
Vec<T> |
Map<T, TT> |
&HashMap<T, TT> |
HashMap<T, TT> |
Set<T> |
&HashSet<T> |
HashSet<T> |
void, undefined, never, null |
- |
() |
any, unknown, object, -, * |
impl serde::Serialize |
T: serde::de::DeserializeOwned |
Promise<T> |
&T |
T |
Special Types
| TypeScript | Rust Input | Rust Output |
|---|---|---|
Json |
&serde_json::Value |
serde_json::Value |
JsValue<T>, JsValue |
&JsValue |
JsValue |
RustCallback<T,TT> |
dioxus::core::Callback<T, impl Future<Output = Result<TT, serde_json::Value>> + 'static> |
- |
Drop |
- |
- |
Special Types
Special types are types not included in the regular Typescript type system, but are understood by the use_js! macro and may augment the generated binding code.
Json
Json is a simple type that represents valid json. This type can best nested.
type Json = string | number | boolean | null | { [key: string]: Json } | Json[];
Example Usage
TypeScript:
type Json = string | number | boolean | null | { [key: string]: Json } | Json[];
export function json(): Json[] {
return [
{"key": "value"},
{"key": "value"},
];
}
Generated Rust signature:
pub async ;
JsValue: Javascript References
This special TypeScript type signals to the macro to bypass serialization and pass native JS values as opaque references between Rust and JavaScript. The macro generates the glue code required. The JS value is automatically disposed when all references on the Rust side go out of scope. Only the following are valid representations:
| Valid Ts Uses | Input | Output |
|---|---|---|
JsValue<T>, JsValue |
&JsValue |
JsValue |
Promise<JsValue<T>>, Promise<JsValue> |
- |
JsValue |
JsValue<T> | null JsValue | null |
Option<&JsValue> |
Option<JsValue> |
Promise<JsValue<T> | null>, Promise<JsValue | null> |
- |
Option<JsValue> |
type JsValue<T = any> = T;
Example Usage
TypeScript:
type JsValue<T = any> = T;
type MyObject = {
name: string;
method: (value: number) => number;
};
export function createJsObject(): JsValue<MyObject> {
return {
name: "example",
method: function (value) {
return value + 25;
},
};
}
export function useJsObject(value: JsValue<MyObject>): number {
let result = value.method(2);
return result;
}
Generated Rust signature:
pub async ;
pub async ;
Usage:
let js_value_example: = use_resource;
RustCallback: Passing Closures from Rust to JavaScript
This special TypeScript type signals to the macro that a Rust async callback will be passed into the JavaScript function. The macro generates the glue code required. This enables advanced interop patterns, such as calling Rust logic from within JS — all while preserving type safety. This type cannot be nested.
type RustCallback<A, R> = (arg: A) => Promise<R>;
A and R can only be:
stringnumberbooleanT | nullT[]Map<T, TT>Set<T>voidJson
Multiple invocations of a RustCallback can be inflight at the same time e.g.
let results = await Promise.;
Depending on the rust logic (e.g. different network requests), some invocations may finish before others. They do not debounce, wait for finish, or cancel previous requests. If this is desired, one would have to implement this logic themselves. Therefore it may be best to wait for the previous response to finish e.g.
let result1 = await ;
let result2 = await ;
The lifecycle of a RustCallback is tied to the lifecycle of the component the function was called in. i.e when the component drops all ongoing requests will be canceled on the rust side and awaiting promises on the js side will throw notifying that the component has been dropped. Any additional calls to the callback on the js side will also throw.
Since the lifecycle of RustCallback is not tied to the function invocation. The function can return and all RustCallback's will function until the component is dropped.
On the rust side, if the callback returns an Err then the js Promise will be rejected with that serialized value.
Example Usage
TypeScript:
type RustCallback<A, R> = (arg: A) => Promise<R>;
export async function useCallback(
startingValue: number,
doubleIt: RustCallback<number, number>
): Promise<number> {
let doubledValue = await doubleIt(startingValue); // Calls back into Rust
return doubledValue;
}
Generated Rust signature:
pub async ;
Usage:
// Rust async closure that will be called by JS
let cb = use_callback;
let callback_example: = use_resource;
Drop: Hook Into The Component Drop LifeCycle
Drop is used to hook into the component drop lifecycle on the js side.
type Drop = Promise<void>;
When the promise completes, the component the function was invoked from has been dropped. As such, all RustCallback parameters will now throw if invoked. Therefore, Drop can be used to remove any handlers (e.g. drop.then(() => document.removeEventListener('click', handler))) or abort early from a function invocation.
Drop is different from all the other special types in that it does not rely on any external context provided by user of the function containing it. Therefore, no user facing rust code will be generated for it.
Drop is also available in plain js. Any function parameter named drop without a type will be treated as Drop.
Example Usage
TypeScript:
type Drop = Promise<void>;
export async function dropExample(
value: number,
drop: Drop
): Promise<number>;
Generated Rust signature:
pub async ;