Dynamic Verse UI Button Grid Generation
What you'll learn
You'll learn the known-good UEFN pattern for generating a grid of UI buttons from a data list. We'll cover the three correctness traps that break most attempts: GetPlayerUI[] is a failable (<decides>) call and must live in a failure context; widgets are laid out by adding slots to stack_box rows, not by hand-positioning; and a click is awaited with Button.OnClick().Await().
How it works
- Define the data: a flat
[]stringof labels drives how many buttons exist. - Build the buttons: for each label we create a
button_loud, store it in a[]button_loud. - Lay out the grid: we add each button to a horizontal row
stack_box; when a row fills up (GridWidthcolumns) we start a new row, then wrap all rows in a verticalstack_boxplaced in acanvas. - Show it:
if (PlayerUI := GetPlayerUI[Player])succeeds, we callPlayerUI.AddWidget(Canvas, ...)so the grid appears on screen and consumes input. - React: a coroutine loops on
Button.OnClick().Await()and prints which label was clicked, using.Playerfrom the returned message.
Let's build it
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/UI }
using { /UnrealEngine.com/Temporary/SpatialMath }
using { /Fortnite.com/UI }
# Dynamically builds a grid of clickable buttons and shows it to every player.
ui_grid_device := class<concrete>(creative_device):
@editable
GridWidth : int = 3 # columns per row; grid reshapes automatically
# The data list that drives the grid. Add labels -> more buttons appear.
ButtonLabels : []string = array{"Alpha","Beta","Gamma","Delta","Epsilon","Zeta","Eta","Theta","Iota"}
OnBegin<override>()<suspends>: void =
# Vertical container that will hold each row of buttons.
Column := stack_box{ Orientation := orientation.Vertical }
# Track buttons + the row currently being filled.
var Buttons : []button_loud = array{}
var Row : stack_box = stack_box{ Orientation := orientation.Horizontal }
var ColCount : int = 0
for (Label : ButtonLabels):
Button := button_loud{ DefaultText := StringToMessage(Label) }
set Buttons += array{ Button }
# Add this button as a slot in the current horizontal row.
Row.AddWidget(stack_box_slot{ Widget := Button })
set ColCount += 1
# Row is full -> commit it to the column and start a fresh row.
if (ColCount >= GridWidth):
Column.AddWidget(stack_box_slot{ Widget := Row })
set Row = stack_box{ Orientation := orientation.Horizontal }
set ColCount = 0
# Commit any trailing partial row.
if (ColCount > 0):
Column.AddWidget(stack_box_slot{ Widget := Row })
# Place the whole column on a canvas, centered on screen.
Canvas := canvas{}
Canvas.AddWidget(canvas_slot{
Widget := Column,
Anchors := anchors{ Minimum := vector2{X:=0.5,Y:=0.5}, Maximum := vector2{X:=0.5,Y:=0.5} },
Alignment := vector2{X:=0.5,Y:=0.5}
})
# Add the canvas to every player's UI (GetPlayerUI is failable!).
for (Player : GetPlayspace().GetPlayers()):
if (PlayerUI := GetPlayerUI[Player]):
PlayerUI.AddWidget(Canvas, player_ui_slot{ InputMode := ui_input_mode.All })
# Listen for clicks on each generated button.
for (Index -> Button : Buttons):
spawn { HandleClicks(Button, Index) }
# Helper: convert a plain string into a localizable message for widgets.
StringToMessage<localizes>(Value : string) : message = "{Value}"
# Awaits clicks forever and reports which label fired.
HandleClicks(Button:button_loud, Index:int)<suspends>: void =
loop:
Msg := Button.OnClick().Await() # widget_message carries .Player
if (Label := ButtonLabels[Index]):
Print("Clicked button {Index}: {Label}")```
## Try it yourself
1. Drag the `ui_grid_device` into your level and press play — a 3x3 grid appears.
2. Set `GridWidth` to 4 and add a few more strings to `ButtonLabels`; the rows reflow automatically.
3. Click buttons and watch the Output Log print the index and label.
4. Try `GridWidth := 1` to get a vertical menu, or a large width for a single wide row.
## Recap
You now know how to: generate `button_loud` widgets from a data list; arrange them into rows/columns by adding `stack_box_slot`s to nested `stack_box`es; place that inside a `canvas` and push it to the screen via the failable `GetPlayerUI[]` + `AddWidget`; and handle clicks with `Button.OnClick().Await()`. Because the layout is driven by the list, editing the data reshapes the entire grid — no editor widget tree required.
Check your understanding
Test yourself with an interactive quiz and track your progress + earn XP — free for members.
Turn this into a guided course
Add Dynamic UI Grid Generation via Verse List Comprehension to your free study plan — we'll suggest related pages and stitch the lot into one compile-checked, self-guided lesson with worked examples and quizzes.
Original tutorial generated by Verse Island from the Verse/UEFN knowledge base, with references to the Epic Games sources above. Code is validated against the knowledge base.