Skip to content

Set Visibility: Hidden, Collapsed, and hit testing

At a glance

Lives in: Blueprint API / Widget - Target: a Widget object reference - Input: one ESlateVisibility value - Returns: no value; execution continues - Can fail by: targeting None, changing a child whose parent still hides it, or choosing a state with the wrong layout or hit-test behavior - Official docs: Set Visibility and ESlateVisibility

The one-minute version

  • Set Visibility changes one widget's Visibility property. It does not create, remove, disable, focus, animate, or destroy the widget.
  • Visible paints the widget and lets it and its visible children participate in pointer hit testing.
  • Hidden does not paint or receive pointer hits, but keeps its layout space.
  • Collapsed does not paint or receive pointer hits and takes no layout space, so surrounding layout can move.
  • Hit Test Invisible paints the widget but prevents pointer hits on both it and its descendants.
  • Self Hit Test Invisible paints the widget and prevents hits on that widget itself while still allowing eligible descendants to receive hits.
  • A parent's state gates its subtree. Setting a child to Visible cannot overcome a hidden or collapsed parent.
  • Choose one owner for Visibility. A property binding and direct setter calls competing over the same property produce fragile UI.

What Set Visibility actually does

Every UMG Widget exposes a Visibility property using the ESlateVisibility enum. Set Visibility writes one enum value to that property and synchronizes the change to the underlying Slate widget.

The Blueprint node has execution input/output pins, a Widget target, and an In Visibility enum input. It has no Boolean or object return value. The C++ call shape is void UWidget::SetVisibility(ESlateVisibility): the Blueprint target is a UWidget*, the enum keeps the same ESlateVisibility name, and void means there is no data return.

pseudocode of the Blueprint contract - not engine source

function ShowInventoryPanel(InventoryPanel):
    if InventoryPanel is None:
        report an invalid reference
        return

    InventoryPanel.SetVisibility(Visible)
    execution continues

The widget object remains the same object. Its variables, children, dispatcher bindings, and parent relationship still exist. Visibility changes whether Slate includes it in painting, layout, and hit testing according to the chosen enum value.

The five states

Visibility Painted? Keeps layout space? Self receives pointer hits? Children can receive pointer hits?
Visible yes yes yes yes, if their own states allow it
Hidden no yes no no
Collapsed no no no no
Hit Test Invisible yes yes no no
Self Hit Test Invisible yes yes no yes, if their own states allow it

The editor may display the last two as Not Hit-Testable (Self & All Children) and Not Hit-Testable (Self Only). Those labels describe the same distinction as HitTestInvisible and SelfHitTestInvisible in the enum.

Visible

Use Visible when the widget should draw and be eligible for normal pointer interaction. It does not guarantee that an ancestor is visible, that the widget is enabled, that it supports keyboard focus, or that another widget is not covering it.

Hidden

Use Hidden when the widget should disappear but its slot should still reserve the same space. This is useful when nearby controls must not shift:

Horizontal Box
-> Ammo Icon (Hidden when weapon has no ammo)
-> Ammo Count

The icon does not draw, but the count stays aligned.

Hidden is not "transparent but clickable." It is not painted or hit-testable.

Collapsed

Use Collapsed when the widget and its layout contribution should disappear. A Vertical Box, Horizontal Box, Wrap Box, or other layout panel can rearrange its remaining children as if the collapsed child took no space.

That is ideal for an optional settings row. It is a poor fit when hiding the row should not make the menu jump.

Hit Test Invisible

Use Hit Test Invisible for visible decoration that must not intercept pointer input, including all widgets beneath it in that subtree. A full-screen overlay with this state can display an effect without blocking controls elsewhere, but buttons nested inside that overlay cannot receive pointer hits either.

Self Hit Test Invisible

Use Self Hit Test Invisible for a visible container or decoration whose children still need pointer input. A Border can ignore hits on itself while a Button nested inside remains eligible.

This state does not force every child to be interactive. A child that is Hidden, Collapsed, disabled, or hit-test-invisible still follows its own state.

Parent and child visibility compose

A child does not escape its ancestors. Think of the effective result as every state along the path from the root to that child:

PauseMenuRoot = Collapsed
└── ResumeButton = Visible

effective result: ResumeButton is not drawn, laid out, or hit-testable

Changing ResumeButton to Visible stores that child state, but the root still gates the subtree. When the root becomes Visible, the button can appear using the state it already had.

Hit testing composes the same way:

Overlay = Hit Test Invisible
└── ResumeButton = Visible

effective result: button is drawn but cannot receive pointer hits

Change the overlay to Self Hit Test Invisible when the overlay itself should ignore hits but its eligible children should remain interactive.

When debugging, walk upward in the UMG hierarchy. Printing only the button's own Get Visibility value misses an ancestor that suppresses it.

Visibility, enabled state, and focus are different

Set Is Enabled(false) leaves a widget visible and in layout while making it unavailable for normal interaction. Controls commonly draw a disabled style, which is useful for "crafting requires more materials" or an unavailable menu choice. Visibility does not communicate that same state to the player.

Intent Typical choice
Show and allow interaction Visible and enabled
Show an unavailable control Visible and disabled
Show decoration that should not catch the pointer Hit Test Invisible or Self Hit Test Invisible
Hide but reserve its slot Hidden
Hide and reflow layout Collapsed

Keyboard/gamepad focus is another system. Hiding or collapsing the currently focused control does not define which control should receive focus next, and changing visibility does not change the PlayerController's input mode.

For a menu transition, make the handoff explicit:

OpenConfirmPanel
-> MainPanel Set Visibility Collapsed
-> ConfirmPanel Set Visibility Visible
-> ConfirmButton Set User Focus (Owning Player)

CloseMenu
-> MenuRoot Set Visibility Collapsed
-> Set Input Mode Game Only
-> Set Show Mouse Cursor false

Do not rely on the focus system to guess a useful replacement after its old target disappears.

Visibility and animations

Widget animation and visibility are separate properties. An animation that drives render opacity to zero can leave the widget Visible and hit-testable. Conversely, setting a widget to Hidden or Collapsed prevents its visuals from being shown even if an animation is changing opacity or transforms.

A predictable fade pattern is:

Fade in:
    Set Visibility Visible
    Play Animation Forward

Fade out:
    Play Animation Reverse
    On Animation Finished -> Set Visibility Collapsed

The completion event, interruption rules, and desired final state belong to your widget. Set Visibility itself does not start, stop, reverse, or wait for an animation.

Avoid using only Set Render Opacity(0) to close an interactive panel. Unless you also change its input state, an invisible-looking control can still sit in the hit-test path.

Direct setters versus property bindings

A Designer binding on Visibility makes the binding function a value owner. A direct Set Visibility call makes the event path a value owner. Mixing both means one path can replace or override what the other just requested as the property synchronizes.

Prefer one of these:

event-driven owner:
    On InventoryOpened -> InventoryPanel Set Visibility Visible
    On InventoryClosed -> InventoryPanel Set Visibility Collapsed

bound owner:
    Visibility binding returns Visible or Collapsed from one authoritative state

The event-driven version is usually easier to trace and avoids repeatedly evaluating a binding. If you inherit a widget with a binding, remove that binding before making direct calls the new contract. Do not add delays or repeat setters to "win" against another owner.

Repeated layout changes

Collapsed changes whether the widget participates in layout. Toggling many collapsed descendants can invalidate and recalculate surrounding layout, and it can make controls visibly jump. That is correct behavior, but it should be intentional.

  • Use Hidden when space should remain stable.
  • Use Collapsed when the layout should close the gap.
  • Use a Widget Switcher when one of several same-area panels should be active.
  • Update visibility when the underlying state changes, not from an unrelated Tick or a binding on every child.
  • Toggle a common parent when an entire group shares one state instead of issuing redundant calls to every descendant.

Visibility calls with the same value should still not be your synchronization strategy. A named RefreshState function that derives the small number of panel states from current data is easier to test.

Remove From Parent is a lifetime/layout choice

Visibility keeps the widget attached to its current parent. Remove from Parent detaches it.

Set Visibility Collapsed
-> same widget remains in the hierarchy
-> same variables and parent slot remain
-> Set Visibility Visible can show it again

Remove from Parent
-> widget is detached
-> stored reference may still be valid
-> it must be added again before it can show

For panels inside a persistent HUD, visibility is usually simpler. For a temporary top-level popup, removing and later re-adding or recreating it may be the cleaner lifecycle. Neither choice restores input mode or focus by itself.

Local multiplayer and networking

Screen UI is local presentation. Each local player should change visibility on that player's widget instance, using that widget's owning player for focus and input operations.

In a network game, replicate the gameplay fact, not the enum state of a screen widget:

replicated ObjectiveActive changes
-> each owning client's HUD receives/reads the new fact
-> that local HUD sets ObjectivePanel visibility

The server normally has no useful viewport widget to manipulate, and one client's Set Visibility call does not update another client's UI. Split-screen also has several local widget instances in one process; avoid assuming player index 0 owns every menu.

When it fails (and what failure does)

The node has no success output. Diagnose the visible behavior and the target reference:

  1. Target is None. Blueprint can report Accessed None, and the intended widget does not change. Guard optional references with Is Valid.
  2. The wrong instance changed. A duplicate menu is visible while the stored reference points to another copy.
  3. An ancestor still suppresses the widget. The child says Visible, but a parent is Hidden, Collapsed, or blocks descendant hit testing.
  4. A binding or refresh path changes it back. Two systems own the same property.
  5. The panel is invisible but still blocks input. Opacity was changed, not visibility, or another transparent widget covers the controls.
  6. The panel appears but its buttons do nothing. An ancestor uses Hit Test Invisible, the controls are disabled, focus/input mode is wrong, or another widget is on top.
  7. The menu jumps when a row hides. Collapsed removed layout space where Hidden was intended.

There is no built-in retry and no Boolean to branch on. Use the Widget Reflector, runtime prints of the exact instance and visibility chain, and a breakpoint on every setter when another path appears to reverse the change.

What Set Visibility does not do

  • It does not add a widget to a parent, viewport, or player screen.
  • It does not remove or destroy the widget.
  • It does not clear variables, stop dispatchers, or reset widget state.
  • It does not enable/disable a control or choose keyboard/gamepad focus.
  • It does not change input mode, pause, cursor state, or mapping contexts.
  • It does not play a widget animation or make opacity zero.
  • It does not hide an Actor or Scene Component in the 3D world.
  • It does not replicate to another client.

Lookalikes - which one do I want?

Node / property Use when Watch out
Widget Set Visibility You want to control UMG painting, layout participation, and hit testing. Parent state and the selected enum affect descendants.
Set Is Enabled A visible control should be unavailable for interaction. It stays in layout and usually draws a disabled style.
Set Render Opacity You are fading or visually dimming a widget. Zero opacity is not a visibility or input policy.
Remove from Parent The widget should detach from its panel or viewport. Stored references and other state can remain alive.
Widget Switcher One child panel should occupy a shared region at a time. It selects an active child; it is not actor visibility.
Set Actor Hidden In Game You need to hide an Actor's rendered components in the world. It does not target UMG widget layout.
Scene Component Set Visibility You need to change a rendered component's world visibility. Its propagation options and collision/lifetime are separate.

Rule of thumb: choose visibility from the desired layout and input behavior, then handle focus, input mode, animation, and lifetime as separate policies.

Going deeper