Menu

Godot @onready: Get Child Nodes Before _ready()

@onready delays a variable's value until the node is in the scene tree, which is when $Child paths work. See when it runs, the null instance error it prevents, and how it compares with @export.

This page includes runnable editors. Edit, run, and see output instantly.

Child nodes are not available when a script's object is created. @onready waits until they are:

extends CharacterBody2D

@onready var sprite: Sprite2D = $Sprite2D
@onready var health_bar: ProgressBar = $UI/HealthBar
@onready var jump_sound := $JumpSound as AudioStreamPlayer2D

func _ready():
	health_bar.value = 100

func jump():
	jump_sound.play()

@onready var sprite = $Sprite2D is the same as declaring var sprite and assigning sprite = $Sprite2D at the start of _ready(). It is shorter, and it keeps the declaration next to its value.

When Each Value Is Set

A plain member variable gets its value when the object is created, before it is in the tree. An @onready variable gets its value right before _ready():

plain var set, in tree: false
_init
_enter_tree
@onready var set, in tree: true
_ready, parent is root
  1. Plain var values, then _init(), when the object is created.
  2. _enter_tree(), when the node is added to the tree.
  3. @onready values, once the node's children are ready.
  4. _ready().

The Error @onready Prevents

Without @onready, a path is looked up while the node has no parent and no children:

var parent_name = get_parent().name   # runs at creation: there is no parent yet
SCRIPT ERROR: Invalid access to property or key 'name' on a base object of type 'null instance'.

The same message appears when a $Path has a typo. If you see null instance, check the node name and spelling in the Scene dock first, then check whether the code runs before _ready().

Typing @onready Variables

Add a type so the editor can autocomplete the node's methods:

@onready var timer: Timer = $Cooldown
@onready var label := $Score as Label

With : Timer, Godot checks the node when the variable is set, and reports an error if $Cooldown is a different type. With as Label, a node of the wrong type gives null instead.

Unique Names: %Node

A $ path breaks when you move the node to another parent. Mark the node as a unique name (right click it in the Scene dock, then Access as Unique Name) and use %:

@onready var score_label: Label = %ScoreLabel

%ScoreLabel finds the node anywhere in the same scene, so you can rearrange the scene freely.

@onready or @export?

@onready var bar = $UI/Bar@export var bar: ProgressBar
Set bythe path in the codepicking the node in the Inspector
Moving or renaming the nodebreaks the pathkeeps working
Visible in the Inspectornoyes

Use @onready for a node's own children that rarely move. Use @export for references a designer sets, or for nodes in other parts of the scene.

Do Not Combine Them

@export @onready var bar = $Bar looks handy but does not do what you expect: the @onready value replaces the one set in the Inspector, so the export is useless. Godot warns with ONREADY_WITH_EXPORT. Pick one of the two.

Frequently Asked Questions

What does @onready do in Godot?

It delays a member variable's starting value until just before _ready() runs. At that point the node and its children are in the scene tree, so @onready var label = $Label finds the child. Without @onready, the value would be computed when the object is created, before it has any children.

What is the difference between @onready and _ready?

None in timing: @onready var x = $X is the same as declaring var x and setting x = $X as the first line of _ready(). @onready is shorter and keeps the declaration and its value together.

Should I use @onready or @export for nodes?

Both work. @onready var bar = $UI/HealthBar depends on the path, so renaming or moving the node breaks it. @export var bar: ProgressBar, set in the Inspector, survives moves. Many projects use %UniqueName with @onready as a middle ground.

Why do I get "Invalid access ... on a base object of type 'null instance'"?

Code used a node that was not there. Either the path in $ or get_node() does not match the scene, or the variable was read before _ready(), for example from _init() or from another node's _ready() that ran first.

Coddy programming languages illustration

Learn to code with Coddy

Get started