| name | godot-ui-containers |
| description | Expert blueprint for responsive UI layouts using Container nodes (HBoxContainer, VBoxContainer, GridContainer, MarginContainer, ScrollContainer). Covers size flags, anchors, split containers, and dynamic layouts. Use when building adaptive interfaces OR implementing responsive menus. Keywords Container, HBoxContainer, VBoxContainer, GridContainer, size_flags, EXPAND_FILL, anchors, responsive. |
UI Containers
Container auto-layout, size flags, anchors, and split ratios define responsive UI systems.
Available Scripts
Expert container builder with breakpoint-based responsive layouts.
Auto-adjusting GridContainer that changes column count based on available width.
Expert logic for dynamic Grid columns based on available width and item minimum size.
Safe ScrollContainer management. Handles the common "one-frame delay" bug when adding logs or chat.
High-performance 3D-in-UI setup. Uses stretch_shrink and transparent_bg for character previews.
Pattern for dynamic tab spawning, custom titles, and tab closing logic.
Wrapping item lists using HFlowContainer, essential for tag clouds and responsive menus.
Optimization architecture. Replaces deep container nesting with lightweight Anchor and Offset logic.
Expert custom container logic implementing a radial/circle layout via NOTIFICATION_SORT_CHILDREN.
Dynamic sibling reordering and animation logic for interactive UI lists.
Enforcing strict aspect ratios (e.g. 1:1, 16:9) across fluid window resizes using AspectRatioContainer.
Advanced sizing logic using SIZE_EXPAND_FILL and stretch_ratio for weighted layouts.
NEVER Do in UI Containers
- NEVER ignore
mouse_filter properties; strictly set to PASS or IGNORE on overlay containers to prevent them from blocking clicks to underlying buttons.
- NEVER instantiate thousands of nodes in a
ScrollContainer; strictly use Virtual List Pooling with a VScrollBar hook and a single spacer child to simulate list height for O(1) rendering performance.
- NEVER manually calculate card dimensions for responsive grids; strictly use an
AspectRatioContainer to lock proportions (e.g., 2:3 ratio) while allowing parent containers to handle scaling.
- NEVER manually set child
position or size in a Container — Containers override child transforms during queue_sort(). Use custom_minimum_size or size_flags instead [1].
- NEVER forget
size_flags for expansion — Default is SIZE_SHRINK_BEGIN. Children will stay tiny unless you set SIZE_EXPAND_FILL for responsive containers.
- NEVER use
GridContainer without setting columns — Default is 1, creating a simple vertical list. For responsive wrapping, use HFlowContainer instead [8].
- NEVER nest containers too deeply (10+ levels) — Heavy nesting causes layout recalculation spikes. Replace intermediate containers with Anchor Layouts for static padding [16].
- NEVER skip separation overrides — Default theme separation is often too tight. Use
add_theme_constant_override("separation", value) for professional breathing room.
- NEVER use
ScrollContainer without a minimum size — Without it, the container may collapse to zero or expand infinitely, breaking the scroll mechanism.
- NEVER scroll to a new child on the same frame it was added — The layout hasn't updated yet. You MUST
await get_tree().process_frame before setting scroll_vertical [5].
- NEVER scale a
SubViewportContainer to change its size — This distorts the rendered contents. Adjust margins or use stretch and stretch_shrink properties instead [2].
- NEVER leave
mouse_filter on default for layered Viewports — Input events might not reach children. Use MOUSE_FILTER_PASS or STOP to ensure events drill down [6].
- NEVER use
GridContainer for responsive wrapping — Use HFlowContainer if you want items to wrap based on width. GridContainer enforces a strict column count [7].
- NEVER animate
position directly inside a container — Use Tween on custom_minimum_size to smoothly "push" siblings during transitions [1].
# VBoxContainer example
# Automatically stacks children vertically
# Children:
# Button ("Play")
# Button ("Settings")
# Button ("Quit")
# Set separation between items
$VBoxContainer.add_theme_constant_override("separation", 10)
Responsive Layout
# Use anchors and size flags
func _ready() -> void:
# Expand to fill parent
$MarginContainer.set_anchors_preset(Control.PRESET_FULL_RECT)
# Add margins
$MarginContainer.add_theme_constant_override("margin_left", 20)
$MarginContainer.add_theme_constant_override("margin_right", 20)
Expert Layout Patterns
1. Split-Screen-Container (Dynamic)
Standard pattern for local multiplayer or comparisons using HSplitContainer.
# split_screen.gd
func setup_split(v1: SubViewport, v2: SubViewport):
var hsplit = HSplitContainer.new()
var c1 = SubViewportContainer.new()
c1.stretch = true # Resize viewport to match container
c1.add_child(v1)
hsplit.add_child(c1)
# repeat for c2/v2...
2. Virtual List ScrollContainer (Pooling)
High-performance list for thousands of items by recycling a small node pool and using a spacer.
# virtual_list.gd
func _on_scroll():
var scroll_y = get_v_scroll_bar().value
var start_idx = int(scroll_y / item_height)
for i in range(node_pool.size()):
var node = node_pool[i]
# Move node down the list
node.position.y = (start_idx + i) * item_height
# Inject data from the massive array
node.update_data(massive_data_array[start_idx + i])
3. Aspect-Ratio-Locked Cards
Responsive cards that maintain proportions (e.g., 2:3) in any grid or flow container.
# card_grid.gd
func add_card(texture: Texture2D):
var arc = AspectRatioContainer.new()
arc.ratio = 0.66 # 2:3 proportions
arc.stretch_mode = AspectRatioContainer.STRETCH_FIT
var tr = TextureRect.new()
tr.texture = texture
tr.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
tr.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
arc.add_child(tr)
grid_container.add_child(arc)
SizeFlags
# Control how children expand in containers
button.size_flags_horizontal = Control.SIZE_EXPAND_FILL
button.size_flags_vertical = Control.SIZE_SHRINK_CENTER
Reference
Related