Blueprint, C++ and web API reference fragments for an already configured custom app
The first rendered UI guide covers the prebuilt reference fixture. This page assumes an already configured custom app with its own frontend and native owner. The reference fixture is prebuilt; the examples below belong in a project-owned frontend and native integration. They are checked against source shared by baseline 933db517... and candidate e7ab5dcc..., but the new snippets have not been compiled or exercised together in Unreal. An independent source review confirmed the API names and payloads, but found three missing parts of an executable tutorial: app registration, a reproducible frontend build/manifest recipe, and an observable native action with lifecycle wiring. These fragments do not implement those parts and cannot be pasted into the reference host to create a working demo.
Keep the native and web contracts aligned
An app uses a UQuartzUIAppAsset and its Definition, with project-owned compiled output under Content/QuartzUI/Apps/<app-id>. The selected app owns its identity, contract/provider registrations and entry page. Native gameplay owns authoritative values. Browser code subscribes to those values and invokes explicitly registered requests.
The standalone host has no frontend package manifest: it tracks seven prebuilt reference resources. The plugin distributes source helpers under Resources/Client, not an installable npm package. Do not write npm install quartzui or assume a plugin Frontend/package.json exists.
Before using these fragments, your app must already have:
- A configured
UQuartzUIAppAsset, explicit identity, resource owner/root and entry page, registered in project settings and selected for the intended local player. Generating a contract does not create or register an asset. - A frontend build that compiles the selected app’s generated TypeScript contract, ships all imports and the entry document, and produces matching contract/localization fingerprints in
quartzui.build.manifest. - A native owner that invokes the C++ helper or Blueprint flow, binds a named action to an authoritative operation, and unregisters that action on teardown.
- A real score label and document owner that calls
connectScore, displays failures and disposes the client when the document ends.
This page does not supply the asset-authoring steps, build system, native handler or lifecycle fixture. The reference host includes only its prebuilt reference app, not an app named demo. Its build setup cannot be inferred from an unrelated game project. A standalone browser preview does not validate Unreal bridge integration.
App identity and selection prerequisites
The identifier demo below is illustrative. It is not an asset or app supplied by the reference host. For these exact fragments to apply, an existing custom app must satisfy the following source-defined configuration. If your app has a different ID, use its actual configuration and regenerate its contract; changing a JavaScript filename does not configure an Unreal app.
| Field or boundary | Required configuration for the illustrative demo app |
|---|---|
| Asset class | A real, loadable project UQuartzUIAppAsset; its asset path is chosen and authored by the consuming project. No DA_Demo asset is supplied here. |
Definition.AppId | demo, a valid lowercase DNS label; the resource origin is https://demo.quartzui.test/. |
Definition.ResourceOwner | EQuartzUIAppResourceOwner::Project; the default PluginFixture value does not configure a production app. |
Definition.PackagedRoot | Exactly Apps/demo, below project Content/QuartzUI; physical compiled output is Content/QuartzUI/Apps/demo. |
Definition.EntryPage | index.html for these assumptions, present with that exact case beneath the compiled root. |
| Canonical shell selection | UQuartzUIProjectSettings.DefaultApp references that asset. StartShell loads DefaultApp; adding an app only to RegisteredApps does not select it for this facade. |
| Startup and player | An explicit StartShell call works with StartupPolicy=Manual; Automatic uses the host startup path. Supply the intended local player/controller, a valid configured StartupLayer, and a usable viewport/input policy. Settings select the app; the player argument selects that player's shell. |
| Contract selection | -App=demo searches DefaultApp, RegisteredApps and LoadingApp, using the asset's canonical AppId. The selected asset must be loadable and unambiguous. An absent ID produces quartzui.contract.configured-app-not-found. |
| App contract closure | The asset's RegisteredProjectContracts, RegisteredProviders and RegisteredProviderSets define its generated contract. Empty registrations retain the core contract. Use the actual app's closure; do not copy a reference or another app's fingerprint. |
StartShell reuses an existing retained shell. Changing settings or generating a contract does not switch an already running document to another app. Selection and resource admission must be established in the consuming app before these fragments are used. This table describes prerequisites; asset authoring, selection and execution for a custom demo remain unverified.
Blueprint: publish one value
Use a Blueprint owned by the intended local player, such as its PlayerController, in a project with an app selected in QuartzUI Project Settings.
- Call Start UI Shell and pass that exact local Player Controller. Automatic startup already calls the canonical shell path; an explicit repeat reuses that player's shell.
- Check the returned shell. On startup failure inspect Get UI Failure. Observe Is UI Ready for the contract handshake; creating a shell is not readiness.
- Call Set UI Integer with the same controller, State Name
demo.score, Value42. Check the Boolean return. - In your web app, subscribe to
demo.scoreas shown below. Its object payload is{ value: 42 }; the frontend decides where to display it.
Do not expect the frozen reference menu to contain a score label. The intended result, after supplying and validating that missing integration, is a label reading Score: 42; it has not been observed for these snippets. State submitted after shell startup and before readiness uses the retained/coalesced bridge path. Passing no controller is valid only when resolution finds exactly one local player; it does not choose an arbitrary split-screen player.
Other actual nodes include Set UI Boolean, Set UI Float, Set UI Text, Set UI Name, Publish UI Struct, Show UI Notification, Send Typed UI Event, Register UI Action and Unregister UI Action. ExecuteJavaScript is a native advanced API, not an ordinary reflected gameplay Blueprint node.
C++: the same exact-player operation
For a consuming client module, add QuartzUIRuntime to its Build.cs dependencies (private if only implementation files use it; public if exported headers expose its types). The standalone host's empty module currently depends only on Core; it is not already an example integration module.
// A helper in your client module. The caller supplies its exact local player.
#include "QuartzUIBlueprintLibrary.h"
bool StartScoreUI(ULocalPlayer* LocalPlayer)
{
if (!LocalPlayer || !FQuartzUI::StartShell(LocalPlayer))
{
return false;
}
return FQuartzUI::SetIntegerValue(LocalPlayer, FName(TEXT("demo.score")), 42);
}
Call from your local-player game flow on the game thread after the selected app/settings are available. A true value publication result does not certify that the browser is ready; FQuartzUI::IsReady(LocalPlayer) is the readiness query. The subsystem owns the retained shell: do not create a browser per label or keep unmanaged pointers after player teardown. FQuartzUI::StopShell is for an intentional close.
Web: subscribe using shipped exports
Bundle Resources/Client/quartzui.mjs into your own app. Generate the selected app's TypeScript contract, compile it with your frontend, and import QUARTZUI_CONTRACT_HASH from that generated output. This example assumes your bundle puts quartzui.mjs and generated/app.contract.js beside the module below; those are explicit app build destinations, not pre-existing plugin files.
For an already registered app named demo, the source-defined generation command is:
& "$engineRoot/Engine/Binaries/Win64/UnrealEditor-Cmd.exe" $projectFile -run=QuartzUIContract -App=demo -Output=QuartzUI/Generated/app.contract.ts -unattended -NullRHI -nop4
if ($LASTEXITCODE -ne 0) { throw 'App contract generation failed.' }
Set $projectFile to the full path of your own .uproject and $engineRoot to the installed engine root that contains the Engine directory. This command does not create/register the demo asset. It fails if the selected app is not configured. -Check checks freshness instead of generating. Do not copy the reference fingerprint or hand-edit the generated hash.
The command emits TypeScript only. It does not compile JavaScript, copy client helpers, create HTML, build the app or generate quartzui.build.manifest. The consuming app must already supply and validate this mapping:
| Input | Required browser/build result |
|---|---|
QuartzUI/Generated/app.contract.ts | Compile/bundle the generated module. For the imports below, emit generated/app.contract.js relative to the importing module, exporting QUARTZUI_CONTRACT_HASH. The .js path is an app build choice, not generator output. |
Plugin Resources/Client/quartzui.mjs | Copy/bundle the inspected helper; resolve ./quartzui.mjs from the importing module. |
| App's HTML and UI module | Supply the configured index.html, load the built module, invoke connectScore, and provide its real rendering/error callbacks. These documents/call sites are not supplied here. |
Optional Resources/Client/react.mjs | Include its relative dependency react-store.mjs and resolve its bare react import with the consuming app's build. Copying react.mjs alone is insufficient. |
| App build metadata | Write quartzui.build.manifest at the compiled app root using the current native contract and localization identities. |
The metadata parser requires exactly six fields: numeric schemaVersion: 1, appId: "demo", resourceOwner: "project", entryPage: "index.html", contractFingerprint and localizationFingerprint. The last two must be the actual current identities, not placeholders. The audit also requires the contract fingerprint in compiled text outside the metadata, the exact entry document, and admissible compiled resources. TypeScript/build-tool inputs, source maps and development URLs are rejected by the production audit. The localization fingerprint represents the native canonical catalog (including configured string table, cultures and generated files); a hash copied from an empty reference catalog is not valid evidence for an app with different localization.
The audit's generated staging manifest (ManifestOutput) is distinct from the frontend-produced quartzui.build.manifest. An audit checks build evidence; it does not build a missing frontend. No generic frontend build command or custom manifest-generation procedure has been validated here. The reference host has no frontend package/build recipe, and a different game's app-specific build script is not a substitute. A passing source or syntax check cannot establish import resolution, admitted resources or readiness.
import { QuartzUIClient } from './quartzui.mjs';
import { QUARTZUI_CONTRACT_HASH } from './generated/app.contract.js';
// Called once by the document owner; renderScore belongs to your UI framework.
export async function connectScore(renderScore, reportError) {
const client = new QuartzUIClient({
contractHash: QUARTZUI_CONTRACT_HASH,
onError: reportError
});
const unsubscribe = client.subscribeState('demo.score', payload => {
if (Object.keys(payload).length !== 1 || !Number.isSafeInteger(payload.value)) {
throw new Error('Invalid demo.score payload');
}
renderScore(payload.value);
});
try {
await client.ping();
} catch (error) {
unsubscribe();
client.dispose();
throw error;
}
return {
client,
dispose() { unsubscribe(); client.dispose(); }
};
}
Wire renderScore to your label/component so it displays Score: 42; supply a reportError(error) callback for receive/subscriber failures, and catch rejected connectScore/request Promises in the UI's failure state. The client catches subscription callback errors and forwards them to onError; they are not a rejected ping() result. Keep exactly one client per browser document. Subscribe before ping() so initial retained state reaches the page. Subscription callbacks validate their own domain data; transport validation does not establish that value means a score. Subscriptions return cleanup functions; dispose the client when its document ends, not on an ordinary retained screen transition.
For React, Resources/Client/react.mjs exports useQuartzUIState(client, name) and createStateStore. It also imports Resources/Client/react-store.mjs; include that module in your bundle. The consuming app supplies React. Create the document client outside component render/Strict Mode effect replay and use the hook to read retained state. It does not generate native endpoints, provider bindings or gamepad navigation for you.
A web-to-native named action
Register Register UI Action for demo.confirm on the same controller with a bound handler, and unregister when that owner stops accepting it. The handler must validate current game context and call an authoritative native operation. With the connected client above:
// Run inside your click/action handler and handle a rejected Promise.
await client.request('quartzui.action', { action: 'demo.confirm' });
The exact payload key is action. The native boundary rejects extra/invalid fields or unregistered names. The shipped QuartzUIClient has no sendAction() export; helpers with that name in older reference docs are not part of this standalone transport. An acknowledgement means the handler was dispatched; show the resulting authoritative state rather than fabricating success. Timeout or local disposal does not undo an operation already sent to Unreal.
The consuming native owner must establish these obligations before treating a button as a working integration:
- Start or obtain the intended player's selected shell, bind a live handler and check the Boolean registration result. Blueprint uses
Register UI ActionwithFQuartzUINamedActionHandler(FName ActionName); C++ usesFQuartzUI::RegisterNativeActionwithFQuartzUINativeNamedActionHandler(void(FName)). This page supplies neither a handler class nor its invocation/lifecycle wiring. Registering the same name replaces the previous handler for that shell; use an explicit owner and avoid name collisions. - In the native handler, validate the current game context, player eligibility and permission to perform the operation, then call the game's existing authoritative operation. Registration validates naming and handler presence; it does not grant gameplay/server authority. The wire payload carries only the action name, not arbitrary gameplay arguments. A
SetActionsdisplay catalog does not register a named handler. - Observe the real outcome through authoritative state/event publication or a native diagnostic. For example, a project's existing confirm operation could publish its actual completion state; this is an obligation for that project's implementation, not a supplied
demo.confirmbehavior. Do not turn the dispatch acknowledgement into a fabricated success label. - Unregister
demo.confirmon that same player's shell before its owner stops accepting input, is destroyed or relinquishes ownership. C++ usesFQuartzUI::UnregisterAction; Blueprint usesUnregister UI Action. Check cleanup and avoid capturing an unowned pointer in a native delegate. Retained shell transitions can preserve registrations; do not rely on a screen change to unregister them. The view also exposesClearNamedActions, but this API does not wire a custom owner's teardown for it. - Keep the browser document alive while checking post-unregister rejection: a later request should reject with
quartzui.action.not-registeredif the shell/gateway remains live and no replacement handler owns that name. Once the document/gateway is gone, other transport errors can apply. Handle errors without reporting game success. - At document teardown, invoke the returned
dispose()to unsubscribe and dispose the one document client. If teardown races pendingconnectScore, the document owner must dispose the eventual returned connection too. Ordinary retained screen transitions should only release screen subscriptions. A request already dispatched may still affect native state after timeout or browser disposal.
These are source-derived ownership and outcome requirements, not an implemented or executed fixture. No Score: 42 label, native confirm outcome or teardown/rejection result has been observed for these fragments.
For typed gameplay integration, register UQuartzUIProvider classes/provider sets and explicit project contracts on the app asset, regenerate its exact contract and consume the resulting wire names. Do not reuse guessed provider names from the reference fixture. Publish UI Struct is a reflected wildcard node; use FQuartzUI::PublishStruct's template from C++ rather than treating its Blueprint thunk parameter as an ordinary integer API.
Execution remains unverified
Rebuild your host-owned frontend with all imports/assets, regenerate matching contract/build evidence, audit resources, compile native changes and test in actual PIE. Verify Score: 42, a registered action and native outcome, failure display, player ownership and cleanup on teardown. The exact app fingerprint and localization fingerprint must match quartzui.build.manifest. No custom-app authoring-to-cook walkthrough has been accepted yet; packaging remains an explicit gap.
Additional configuration/build/lifecycle anchors in the plugin: Source/QuartzUIRuntime/Public/QuartzUIApp.h:31, Source/QuartzUIRuntime/Public/QuartzUIProjectSettings.h:64, Source/QuartzUIRuntime/Private/QuartzUIProjectValidation.cpp:626, Source/QuartzUIRuntime/Private/QuartzUIProjectContract.cpp:376, Source/QuartzUIRuntime/Private/QuartzUISubsystem.cpp:94, Source/QuartzUIRuntime/Private/QuartzUIContractSchema.cpp:1079, Source/QuartzUIEditor/Private/QuartzUIProjectAudit.cpp:303, Source/QuartzUIEditor/Private/QuartzUIProjectAudit.cpp:911, Source/QuartzUIRuntime/Private/QuartzUIView.cpp:275, Resources/Client/quartzui.mjs:147.
Source anchors in the plugin: Source/QuartzUIRuntime/Public/QuartzUIBlueprintLibrary.h:20, Source/QuartzUIRuntime/Public/QuartzUIBlueprintLibrary.h:118, Source/QuartzUIRuntime/Private/QuartzUIBlueprintLibrary.cpp:219, Source/QuartzUIRuntime/Private/QuartzUIActions.cpp:51, Source/QuartzUIRuntime/Private/QuartzUIView.cpp:452, Resources/Client/quartzui.mjs:27, Resources/Client/react.mjs:1, Source/QuartzUIEditor/Private/QuartzUIContractCommandlet.cpp:21. These are source claims, not new Editor execution results.