Repository navigation
Pass objects through the bridge
A binding and an event carry text by default. With a codec, they carry records and other objects: the page passes a JavaScript object, and Java receives it as the type that it asks for, and back. In this guide, a task list keeps its tasks in Java as records.

With the lwjwae Gradle plugin, one line in build.gradle.kts adds a codec at the version that the
plugin knows:
lwjwae {
jackson() // or gson(), or jsonb()
}The application finds the codec on the classpath by itself. Without the plugin, add
dev.ivchenko.lwjwae.codec:lwjwae-codec-jackson to runtimeOnly, or pass a codec to
ApplicationParameters.builder().codec(...).
-
Describe what crosses the bridge as a record:
public record Task(String title, boolean done) {}
-
Bind a function that takes a
Taskand returns the list, and listen to an event that carries a number:public class TypedBridge { private static final List<Task> TASKS = new CopyOnWriteArrayList<>(); public static void main(String[] args) { Application.launch( WindowParameters.of("Tasks", 560, 440), window -> { window.bind("addTask", Task.class, TypedBridge::add); window.listen("toggle", Integer.class, index -> TypedBridge.toggle(window, index)); window.loadResource("typed/index.html"); }); } private static List<Task> add(Task task) { TASKS.add(task); return TASKS; } private static void toggle(Window window, int index) { Task task = TASKS.get(index); TASKS.set(index, new Task(task.title(), !task.done())); window.emit("tasks", TASKS); } }
bind(name, Class, handler)decodes the argument into the class, and encodes what the handler returns.listen(name, Class, listener)decodes the payload of an event.emit(name, Object)encodes an object into the payload. -
On the page, pass objects and receive them:
const list = document.querySelector("#tasks"); function show(tasks) { list.replaceChildren(...tasks.map((task, index) => { const item = document.createElement("li"); const box = Object.assign(document.createElement("input"), { type: "checkbox", checked: task.done }); box.addEventListener("change", () => lwjwae.emit("toggle", index)); const title = Object.assign(document.createElement("span"), { textContent: task.title }); title.classList.toggle("done", task.done); item.append(box, title); return item; })); } document.querySelector("#add").addEventListener("submit", async (event) => { event.preventDefault(); const title = document.querySelector("#title"); show(await window.addTask({ title: title.value, done: false })); title.value = ""; }); lwjwae.listen("tasks", (event) => show(event.payload));
window.addTaskresolves to the list as an array of objects, andevent.payloadof an event that Java emitted with an object is the object, not its text.
A codec reads and creates the records through reflection, which a native image allows only for
the classes that its metadata names. Mark the outermost type that crosses the bridge with
@BridgeType:
@BridgeType
public record Task(String title, boolean done) {}The lwjwae-processor annotation processor writes the metadata for it and for every type that it
holds: the components of a record, the fields of a class, the type arguments of a List<Task>.
The Gradle plugin adds the processor from lwjwae 0.8.0, so there is nothing else
to do.
Without the plugin, or with an older lwjwae, add the processor to annotationProcessor yourself, or
list every type that crosses the bridge, and the types that they hold, in
src/main/resources/META-INF/native-image/GROUP/NAME/reachability-metadata.json:
{
"reflection": [
{
"type": "com.example.guides.Task",
"allDeclaredConstructors": true,
"allDeclaredMethods": true,
"allDeclaredFields": true
}
]
}- A handler runs on a virtual thread, so it may block, as
addwould to write a file. - A binding on the
Applicationinstead of aWindowreaches every window, and its handler can take the window that called; see Open several windows. - Without a codec, the typed methods fail with a message that says so, and the text methods keep working.
Tutorial
- Set up the project
- Open a window
- Call Java from the page
- Send events from Java to the page
- Draw your own title bar
- Export notes through a native dialog
- Keep the application in the tray
- Show notifications
- Give the application an icon and a name
- Build a native executable
- Package the application
Guides
- Pass objects through the bridge
- Stream bytes between the page and Java
- Open several windows
- Place and control a window
- Make a transparent window
- Add menus
- Register global shortcuts
- Show progress in the taskbar
- Use the clipboard
- Handle dropped files
- Zoom the page
- Set the window icon
- Follow the theme of the desktop
- Use cookies
- Store data in SQLite
- Keep secrets
- Show dialogs
- Run a single instance
- Open your own links and files
- Update the application
- Handle page permissions
- Handle links and page loads
Reference