UReactWebBrowser
Source: SampleProject/Plugins/UnrealReactBridge/Source/UnrealReactBridge/Public/ReactWebBrowser.h
The React ↔ Unreal bridge as a single, self-contained widget. Drag React Web Browser from the Palette into any Widget Blueprint — no parent class to inherit, no child widget to name exactly right. All bridge lifecycle (BindUObject timing, event queue, init script) is handled internally.
- UCLASS:
BlueprintType - Display name: React Web Browser
- Parent class (C++):
UWidget— notUWebBrowser.UWebBrowserdeclares a non-virtualLoadURL(FString)that navigates without resetting bridge state, and UHT has no way to hide an inherited non-virtualUFUNCTIONon a derived class, so inheriting it would leave that trap sitting in the Palette forever.UReactWebBrowserowns itsSWebBrowserdirectly instead.
Multiple browsers on one HUD
Drop more than one ReactWebBrowser into the same Widget Blueprint and each must be a direct child of a CanvasPanel — see Multi-HUD at named anchors and Known Issues → LayerId collision for why.
Properties
| Property | Type | Category | Notes |
|---|---|---|---|
URL | FString (EditAnywhere, BlueprintReadWrite) | React Bridge | React app URL. Use http://localhost:5173 for a Vite dev server, or a file:// path for a packaged build. Falls back to Project Settings → Unreal React Bridge → Default Dev URL if empty. |
BridgeName | FString (EditAnywhere, BlueprintReadWrite) | React Bridge | Optional. Identifier used to address this browser from a Sender's TargetBridgeName. Empty (default) = receives broadcasts only. |
InitialState | TMap<FString, FString> (EditAnywhere, BlueprintReadWrite) | React Bridge | Initial State | Key/value pairs delivered to React on the ue5-bridge-ready event under detail.initialState. Each value should be a valid JSON string; non-JSON values are wrapped as JSON strings. See /guide/initial-state. |
bInteractive | bool (EditAnywhere, BlueprintReadWrite) | React Bridge | When true (default) the browser can take mouse & keyboard focus so React <input> fields work. Set false for a display-only overlay. |
Functions
| Function | Signature | Notes |
|---|---|---|
SendEvent | void SendEvent(const FString& EventName, const FString& JsonData) | Queues if React not yet ready; queue is flushed once on OnReactReadyEvent. |
LoadReactURL | void LoadReactURL(const FString& NewURL) | Resets bridge state and re-initializes on load. |
IsReady | bool IsReady() const (BlueprintPure) | Has React called window.ue.bridge.ready() (i.e. fired onreactready) yet? |
SetInteractive | void SetInteractive(bool bNewInteractive) | Toggle mouse/keyboard focus. Applies immediately to an already-mounted browser. |
SetVisible | void SetVisible(bool bVisible) | Show/hide without unmounting. The embedded browser stays alive across the toggle. |
SendEvent
void SendEvent(const FString& EventName, const FString& JsonData);| Param | Type | Required | Notes |
|---|---|---|---|
EventName | FString | yes | Case-sensitive |
JsonData | FString | yes | Valid JSON string; reaches React as event.detail |
Returns — void. Fires synchronously if ready; otherwise enqueued and flushed when OnReactReadyEvent is invoked.
LoadReactURL
void LoadReactURL(const FString& NewURL);| Param | Type | Required | Notes |
|---|---|---|---|
NewURL | FString | yes | New page URL (http://, https://, or file:///) |
Returns — void. Triggers the full lifecycle again: page load → bridge re-bind → OnReactReadyEvent re-fires. Any state held in React is lost; rehydrate via InitialState on the next ready.
Named LoadReactURL, not LoadURL, purely for naming consistency with the rest of the bridge API — this class does not inherit UWebBrowser (see above), so there is no competing inherited LoadURL(FString) to collide with. LoadReactURL is the only load entry point this widget exposes to Blueprint.
IsReady
bool IsReady() const;Returns — true once React has called window.ue.bridge.ready() and the queued events have been flushed.
SetInteractive
void SetInteractive(bool bNewInteractive);| Param | Type | Required | Notes |
|---|---|---|---|
bNewInteractive | bool | yes | true = can take mouse/keyboard focus; false = display-only, HitTestInvisible |
Returns — void. Reapplies focusability + hit-test visibility immediately, so it works on an already-mounted browser (e.g. a toggled-open chat panel).
SetVisible
void SetVisible(bool bVisible);| Param | Type | Required | Notes |
|---|---|---|---|
bVisible | bool | yes | true = show (the interactive-resolved visibility); false = Collapsed |
Returns — void. The embedded SWebBrowser is kept alive across a hide, so re-showing never reconstructs it.
macOS: why SetVisible exists
On macOS, a freshly-constructed native web view grabs Slate keyboard focus once, which drops held game input (WASD) and shows the OS cursor — see Known Issues. Toggling SetVisible never reconstructs the browser, so the grab never re-fires. Mount once (e.g. at BeginPlay), then use SetVisible for any runtime show/hide — never destroy and recreate the widget just to hide a panel.
Events
All events are BlueprintAssignable and live under category React Bridge.
| Event | Signature | When |
|---|---|---|
OnReactReadyEvent | FOnReactBrowserReady() | React signaled ready, bridge live, queued events flushed |
OnEventReceived | FOnReactBrowserEventReceived(const FString& EventName, const FString& JsonData) | A React-side event arrived. Receiver components handle most cases; this event is for ad-hoc handling directly on the widget |
OnBridgeError | FOnReactBrowserBridgeError(const FBridgeErrorInfo& ErrorInfo) | Any bridge error — see /reference/types#fbridgeerrorinfo |
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnReactBrowserReady);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnReactBrowserEventReceived,
const FString&, EventName,
const FString&, JsonData);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnReactBrowserBridgeError,
const FBridgeErrorInfo&, ErrorInfo);Lifecycle notes
- The widget self-registers with
UUnrealReactBridgeSubsysteminsideRebuildWidget()and unregisters inReleaseSlateResources()— a plainUWidgethas noNativeConstruct/NativeDestruct, so lifecycle is driven from those two instead. You don't call any registration API yourself. LoadReactURLrestarts the full lifecycle: page reload → bridge re-bind →OnReactReadyEventfires again.- At Widget Blueprint compile time,
ValidateCompiledDefaultschecks for the LayerId-collision constraint (see the tip above) and warns if this browser shares a non-CanvasPanelancestor with anotherReactWebBrowser.
Example
WBP_MyHUD
└── CanvasPanel (root)
└── ReactWebBrowser ← React Web Browser widget, dropped onto the canvas
Details
URL: http://localhost:5173
BridgeName: MainHUD
InitialState:
"player" → {"name":"Alice","hp":100}
"level" → 7Level Blueprint
Event BeginPlay
└─ Create Widget (Class: WBP_MyHUD) → HudRef
└─ Add to Viewport (Target: HudRef)// C++: drive the widget after creation
if (UReactWebBrowser* Browser = Cast<UReactWebBrowser>(
Hud->WidgetTree->FindWidget(TEXT("ReactWebBrowser"))))
{
Browser->InitialState.Add(TEXT("player"),
TEXT("{\"name\":\"Alice\",\"hp\":100}"));
Browser->OnReactReadyEvent.AddDynamic(this, &AGameMode::HandleReactReady);
Browser->OnEventReceived.AddDynamic(this, &AGameMode::HandleHudEvent);
}See also
/guide/first-hud— build your first React HUD/guide/multi-hud— multiple browsers on one canvas, and the LayerId constraint/guide/initial-state— howInitialStateserializes and lands on the JS side/guide/lifecycle— full load → ready → destruct timeline/reference/types—FBridgeErrorInfo,EBridgeErrorType