Overview of Godot UI containers
This is a reference page
You don't have to retain any of the information here. Rather, treat this as a tool to come back to when you need to build interfaces.
All the containers
CenterContainer
The CenterContainer
ensures all control children are always in the center. If your control nodes do not have a minimum size, they'll be squished to zero!Good for: Modal windows and popups. AspectRatioContainer
AspectRatioContainer
does resize a single child to fit a certain proportion that you can set. A proportion of1
means the child will always be square, while1.7
is close to cinema/HD aspect ratio. Values lower than1
will look like portraits; approximately0.6
is similar to an average smartphone's screen.Good for: Images, videos, or any content that needs to keep a specific aspect ratio. HBoxContainer and VBoxContainer
BoxContainer
Allows for placing items horizontally or vertically one after the other. Probably the most used Container.VBoxContainer
places items vertically, whileHBoxContainer
places them horizontally. You can set the spacing between children.Good for: Any kind of column such as menu items, or row such as a header with buttons. GridContainer
GridContainer
allows for placing items in a grid. Each column and each row needs at least one child node to have a minimum size, or the row or column will be squished to zero. The number of default columns is 1, which means the default behavior is somewhat similar to aVBoxContainer
.To create inventory UIs, a common technique when using GridContainer
is to fill it in advance with a customSlot
control node (usually made of aPanel
root node and aTextureRect
child node for the item icon).Good for: Inventories, crafting UIs. ScrollContainer
ScrollContainer
is a container that allows scrolling its children. It can scroll horizontally, vertically, or both. The scrollbars on the x or y axis can be set to show up when needed, always, or never.Good for: Any UI that can overflow the screen, such as a long list of items or a chat window. It's often good to wrap important screens with a ScrollContainer
to ensure they work on all screen sizes.HFlowContainer and VFlowContainer
FlowContainer
nodes imitate the default behavior of webpages: they wrap children when they reach the end of the container. You can set the spacing between children. TheHFlowContainer
flows children horizontally, while theVFlowContainer
flows them vertically.Good for: Complicated UIs that need to adapt to different screen sizes; tags. SplitContainer
SplitContainer
takes two children and adds a splitter between them. The user can drag the splitter to resize the children. Each side will not resize below the minimum size of the children. Acollapsed
property allows you to hide the first child (and disables the splitter).Good for: Side panels, such as the Godot editor's inspector. PanelContainer
PanelContainer
is a simple container that draws a rectangle around its unique child. It's useful to give a background to controls. To give it internal margins, edit theStyleBox
property.Good for: Windows, dialogs, and popups. TabContainer
TabContainer
will automatically create tabs from children. Each tab uses the corresponding child's name as a title. It's possible to override the title with theset_tab_title()
method from code, but not from the editor.Note that another control node, TabBar
, can be used for more complicated setups; but it isn't automated and requires you to create tabs yourself in code.Good for: Options screens, settings screens, or any screen that needs tabs. SubViewportContainer
SubViewportContainer
can host aViewport
node. Viewports can display a different scene than the one currently running. To use it, you need to add aViewport
node under it, and then a 2D or 3D scene under that. TheViewport
node needs a size set.Good for: mini-maps, mixing 2D and 3D, or to set up couch co-op split-screen
Common challenges of creating UI Scenes
How do I ensure my UI does not move when the camera moves?
CanvasLayer
node as a parent of your UI. This will ensure the UI is always drawn on top of the game world.CanvasLayer
creates a new rendering context, so it's not affected by the camera. Like the name implies, anything within a CanvasLayer
is in a different layer of its own (In 3D, adding a Controls automatically creates a new context, so it's not necessary to use CanvasLayer
).Should I always add a CanvasLayer as the root of my UI scene?
CanvasLayer
as the root.Control
node as the root of your UI as you normally would, and add a CanvasLayer
in the main scene where you want to display the UI.Should I fragment my UI in many scenes?
How can I preview a UI scene embedded in another in the editor?
@tool
annotation, but this is a little tricky._ready()
, the editor is more complicated. There are various cases in the editor where the children are not available, so if you try to change children nodes from a @tool
script, you will get errors.
To prevent most of those errors, await for the ready
signal anytime you need to access a child. Here's an example: This is a hypothetical ChatLine
scene with a title that you can edit in the editor:@tool
extends HBoxContainer
@onready var _label: Label = %Label
## Title of the item. Gets assigned to the underlying label
@export var title := "": set = set_title
## [param title] property setter.
func set_title(new_title: String) -> void:
title = new_title
if not is_inside_tree():
await ready
_label.text = title
is_inside_tree()
returns false
if the scene is not ready yet. If the parent is not ready, we await
the ready
signal. This delays the rest of the method until ready
is emitted.Which container to use when?
A settings menu screen
The core of a settings menu is a VBoxContainer
with rows. Each row is aHBoxContainer
with two children each: a label on the left, and some other control on the right. Don't forget to set both children asexpand
so they take the full width of the row.For more refinement, the entire thing can be put inside a ScrollContainer
to ensure all options fit no matter what.See the example scene: res lessons_reference UI_samples settings_screen.tscn . There's also a version with tabs: res lessons_reference UI_samples settings_screen_with_tabs.tscn A HUD
You could use an HBoxContainer
to place elements next to each other; for example, a character's portrait, and the bars to the right.To manage manually placing the bars in a pleasing way, you could break out of containers by inserting a plain Control
node between the bars and the parent, and using anchors to place the health and mana bars.An intermediary plain Control
is a good trick to escape the constraints of containers and place elements freely.See the example scene: res lessons_reference UI_samples hud.tscn A title screen
A simple VBoxContainer
with buttons might be enough, but in this example, I want to stagger the buttons. To do this, you could useHBoxContainer
nodes as a parent of eachButton
; and then, in each row, add first a plainControl
node, then the button. By setting both nodes toexpand
, you can control the spacing between each button and the left border by adjusting theControl
node's size or stretch ratio. A higher stretch ratio means more space between buttons and the left border.See the example scene: res lessons_reference UI_samples title_screen.tscn Mobile side drawer
The drawer's contents should in a ScrollContainer
to ensure all its contents are accessible if there are many. Inside theScrollContainer
, anHBoxContainer
can be used to place the buttons.Because you cannot animate children of a container, you'll need to use a Control
node as the parent of the drawer. Place the drawer in theControl
node, and move it outside the screen so it can slide in. You can now useTween
to animate the drawer.See the example scene: res lessons_reference UI_samples mobile_animated_menu.tscn . This example is a bit elaborate, with a button that has toggle_mode
on, used to open/close the menu.
Bonus 1: Animating container children
Control
node as the parent of the children you want to animate. The Control
will be constrained by the parent container, but the children inside can use anchors to move freely.Bonus 2: Custom containers
Container
class and create any layout you want.- Create a function that places all children depending on some custom logic
- Connect this function to the
NOTIFICATION_SORT_CHILDREN
event, which is dispatched anytime the container resizes or children are added or removed.
The Container node: important methods
_notification()
fit_child_in_rect()
queue_sort()
_notification()
is called by Godot on all nodes for a large number of events. It receives the event code as an argument, which allows us to react to it. In our case, what interests us is the NOTIFICATION_SORT_CHILDREN
event. This event is sent when the children of the container need to be sorted, and is called any time a child is added, removed, moved, or the container is resized.func _notification(what: int) -> void:
if what == NOTIFICATION_SORT_CHILDREN:
# do something with the children here
fit_child_in_rect()
is called by Godot to ensure a Control child is constrained to the size you specify. Here's a very simple (and pretty useless) custom container that stacks all children on top of each other:@tool
extends Container
func _notification(what: int) -> void:
if what == NOTIFICATION_SORT_CHILDREN:
for child in get_children():
if child is Control:
# We create a geometrical rectangle from the point (0,0)
# which is top left, down to the size of this container.
# this fills the entire container
var child_rectangle := Rect2(Vector2.ZERO, size)
fit_child_in_rect(child, child_rectangle)
_notification()
for all native events, it won't when your own custom properties change. If you need the container to recalculate children in reaction to your code, call queue_sort()
, which will dispatch the NOTIFICATION_SORT_CHILDREN
event. This is particularly useful in setters for properties that affect the layout. For example, you might have a container with a special max_child_width
property, and you want the children to be re-sorted and resized when it changes:func max_child_width := 250:
set(value):
max_child_width = value
queue_sort()
A simple container node example
CircleContainer
node in the project files. It places its children in a circle.
It also has two properties: radius
and angle_offset
, which can be used to define how far from the center children are, as well as where the circle begins.radius
value, and place the child there.Nathan
Founder and teacher at GDQuest
Lesson Q&A
Use this space for questions related to what you're learning. For any other type of support (website, learning platform, payments, etc...) please get in touch using the contact form.