Install and show your first UI
0.57.0-dev · beta source documentation. Native, Blueprint, Editor and rendering examples are source-reviewed, not executed in Unreal during this scan. See validation and limits.
Use a C++ host project compatible with Unreal 5.8 on Win64. The plugin repository has no .uproject, build target, or bundled host game. Install the appropriate Unreal C++ toolchain for the host. The descriptor contains client runtime, optional GAS, editor, and editor-only test modules; dedicated servers must not depend unconditionally on client modules.
Install the source
Obtain the authorized source repository URL through your existing access. Substitute it below; these instructions do not grant access. From the consuming project's root:
git submodule add <AUTHORIZED_QUARTZUI_REPOSITORY_URL> Plugins/QuartzUI
git submodule update --init --recursive
git -C Plugins/QuartzUI checkout fe5b709ec900e282b50e58b819a25041945005f9
Merge a {"Name":"QuartzUI","Enabled":true} entry into the existing .uproject Plugins array. Preserve other entries. Regenerate project files and build the host Editor target. Source uses Epic's WebBrowser module; the separate WebBrowserWidget runtime plugin is not required by the descriptor. See the discrepancy in gaps.
Native modules that expose QuartzUI types in public headers add QuartzUIRuntime to PublicDependencyModuleNames; otherwise use private dependencies. Keep QuartzUIEditor dependencies in editor modules. Include individual headers such as QuartzUIBlueprintLibrary.h, not private implementation files.
Create the project app
Create a Data Asset of class QuartzUI App Asset (use the generic Data Asset picker if no dedicated entry appears). Name it DA_GameUI. Set its Definition:
| Field | Value for this tutorial |
|---|---|
| App Id | gameui |
| Resource Owner | Project |
| Packaged Root | Apps/gameui |
| Entry Page | index.html |
These files belong at <Project>/Content/QuartzUI/Apps/gameui. mvp is a reserved compatibility fixture ID. A native app asset starts with fixture defaults; explicitly change ownership and ID for production.
Under Project Settings > Plugins > QuartzUI, choose DA_GameUI as Default App. Keep Startup Policy Manual for this walkthrough. Other settings supply frame rate, transparency, input, localization, fallback, resource roots, and budgets. App-specific presentation overrides apply only when their corresponding override switches are enabled. Save the asset and project settings.
Prepare the frontend identity
Create a project-owned frontend source directory. Copy Resources/Client/quartzui.mjs into it (or use a build-time alias), retaining its source license. Generate the selected app's TypeScript:
& '<UE_ROOT>/Engine/Binaries/Win64/UnrealEditor-Cmd.exe' `
'<HOST_PROJECT>.uproject' -run=QuartzUIContract -App=gameui `
'-Output=<PROJECT>/QuartzUI/Generated/gameui.contract.ts' -unattended -nop4
Compile that generated module with the frontend. Its exported QUARTZUI_CONTRACT_HASH and QUARTZUI_CONTRACT_VERSION identify exactly the app's registered contracts and providers. Do not copy a demo hash once the app has its own schema.
This small framework-neutral smoke page deliberately needs no npm installation. For a React project, keep the same client singleton and use the React pattern.
<!doctype html>
<html lang="en">
<head><meta charset="utf-8"><title>Game UI</title></head>
<body>
<p id="status">Connecting to Unreal...</p>
<p>Ammo: <output id="ammo">waiting</output></p>
<script type="module" src="./app.mjs"></script>
</body>
</html>
// app.mjs: generated module must be compiled into this app, at this path.
import { QuartzUIClient } from './quartzui.mjs';
import { QUARTZUI_CONTRACT_HASH, QUARTZUI_CONTRACT_VERSION }
from './generated/gameui.contract.js';
const status = document.querySelector('#status');
const ammo = document.querySelector('#ammo');
const client = new QuartzUIClient({
contractHash: QUARTZUI_CONTRACT_HASH,
contractVersion: QUARTZUI_CONTRACT_VERSION,
onError: error => { status.textContent = error.code ?? error.message; },
});
const unsubscribe = client.subscribeState('hud.ammo', value => {
// The scalar native facade wraps the value in an object.
if (Number.isInteger(value.value)) ammo.textContent = String(value.value);
});
try {
await client.ping();
status.textContent = 'Connected to Unreal';
} catch (error) {
status.textContent = error.code ?? error.message;
}
window.addEventListener('pagehide', () => { unsubscribe(); client.dispose(); }, { once: true });
Build/copy the HTML, compiled JS, and transport to the app root. All runtime imports must resolve within compiled output. A plain desktop browser has no Unreal gateway and should report client.unavailable; that is not a plugin test failure.
The frontend build must also emit quartzui.build.manifest with exactly six fields: schemaVersion: 1, appId: "gameui", resourceOwner: "project", entryPage: "index.html", contractFingerprint, and localizationFingerprint. The fingerprints come from the selected generated contract and configured localization authority. Packaging explains how to compute them, including the minimal no-localization case. Do not name this file .json.
Start and verify
In the local PlayerController Blueprint BeginPlay, call Start UI Shell, passing that controller, then Set UI Integer, name hud.ammo, value 30, same controller. The equivalent C++ fragment is:
#include "QuartzUIBlueprintLibrary.h"
// In a local APlayerController method; inspect false/null in production code.
if (ULocalPlayer* Player = GetLocalPlayer())
{
if (FQuartzUI::StartShell(Player))
{
const bool bAccepted = FQuartzUI::SetIntegerValue(Player, TEXT("hud.ammo"), 30);
ensure(bAccepted);
}
}
Press Play. Expected: the page displays “Connected to Unreal” and ammo 30. Ready requires both document load and a matching contract handshake. Get UI Failure reports why startup or loading failed. Retry UI retries only the supported bounded failure path; it cannot repair a stale frontend contract.
Alternative ownership: set GameMode HUD Class to AQuartzUIHUD, add UQuartzUIShellComponent to an existing HUD/controller, or enable Automatic startup. All target the same canonical shell. Do not make a separate UMG browser for this tutorial.
Lifetime and mistakes
The GameInstance subsystem owns the shell, so HUD recreation or map travel does not intentionally close it. Call Stop UI Shell for an intentional close; player removal also cleans it up. Pass exact local players in split screen. An omitted controller works only when exactly one local player can be resolved.
Most blank-page failures come from an incorrect packaged path, a missing compiled import, stale fingerprints, a missing Default App, or a failed native mount. Diagnose those before adding delays or recreating the shell every frame. Run Tools > Quartz UI > Advanced > Audit QuartzUI Project Apps before packaging.
This walkthrough is source-reviewed, not PIE-executed in this scan. Pinned installation source (source: Documentation/Installation.md), app definition (source: Source/QuartzUIRuntime/Public/QuartzUIApp.h), client (source: Resources/Client/quartzui.mjs).