docs

Building a Desktop App

Lay out multi-panel windows, connect native code to the page, and package the application.

On this page

In Writing Your First App, we created a native OS window that displays a single HTML page.

We'll now turn that window into a full desktop application by dividing the interface into multiple views, connecting native code to the page, and packaging the build for distribution.

Lay Out the Window

You can divide a window into multiple views by arranging panels inside layout containers.

C++
///
/// Split the window into a fixed-width "nav" pane and a fluid "detail"
/// pane, with a draggable divider between them.
///
RefPtr<Container> body =
    window_->layout()->AddRow({ .key = "body", .resizable = true });
RefPtr<Panel> nav =
    body->AddPanel({ .key = "nav", .size = "220px", .min_size = "140px" });
RefPtr<Panel> detail = body->AddPanel({ .key = "detail" });

nav->view()->LoadURL("file:///nav.html");
detail->view()->LoadURL("file:///detail.html");

Each panel hosts its own View and manages its position automatically within the layout tree.

Setting resizable to true on a container lets the user resize adjacent panels by dragging the divider between them.

To configure sizing rules and constraints, see Laying Out Panels. For overlays like toasts or modal dialogs that float above the layout tree, see Floating Panels.

Connect Native Code to the Page

Native code connects to the page through each panel's View, retrieved by calling Panel::view().

Ultralight provides three ways to connect native code and the page. You can mix them freely across panels or within the same View.

Approach Description
Data Bindings Bind native structs to the page so UI elements update automatically when data changes. Best for settings forms, lists, and status displays.
DOM API Find and modify page elements, update text and styles, and listen for events directly from C++.
JavaScript API Call JavaScript from C++ and vice-versa, with type-safe conversions. Best for logic that lives in the page.

Match Native Look and Feel

Ultralight includes APIs to help match the look and feel of the native OS.

Guide Description
Native Window Styling Window chrome, transparency, and background blur materials.
Popups and Dialogs Popup windows for HTML menus and dropdowns, and native modal message boxes.
Dark Mode and Color Schemes Detecting and responding to OS light and dark theme changes.
Native Look and Feel Walkthrough of the 10-native-look-and-feel sample covering backdrop materials, popup menus, themes, and message boxes.
Custom Window Chrome Walkthrough of drawing title bars and caption buttons in HTML while keeping native dragging, resizing, and snapping.

Configure Application Settings

Before shipping, configure the application identity in Settings and pass it to App::Create().

C++
Settings settings;
settings.developer_name = "Acme";
settings.app_name = "Starlight";

app_ = App::Create(settings);

Ultralight uses Settings::developer_name and Settings::app_name to create storage directories on the user's system— these directories store the log file and session data such as cookies and cache files.

To explore all configuration options and app events, see App Lifecycle and Settings.

Package the Application

An installed application requires several support files beside the executable:

For instructions on packaging these dependencies for Windows, macOS, and Linux, see Shipping Your App.

What's Next

From here, Native Look and Feel walks through the 10-native-look-and-feel sample to match the look and feel of the native OS.