The Problem
In the previous chapter, we accessed the count property through parent.parent.count. This works for two levels of nesting, but it's fragile. If you restructure the object tree — add a new wrapper, change the nesting order — parent.parent breaks. You need a way to reference any object in the tree directly, regardless of nesting.
The Naive Approach
You could store references in JavaScript variables:
property var boxRef: null
// ... later in some init code:
boxRef = boxBut this misses the point of QML's declarative nature. You shouldn't need imperative initialization code just to reference an object.
The Idea
QML provides IDs — a lightweight way to name any object so other objects can reference it directly. An ID is declared with id: someName on any object, and then you can use someName.property anywhere in the same file. No parent.parent, no lookup chains.
Think of IDs as a phone book for your QML file. Each object can have an entry in the book (its id), and any other object can look it up by name. You don't need to navigate the object tree — you just call the name.
Let's Build It
import QtQuick
import QtQuick.Window
Window {
width: 500
height: 300
visible: true
title: "IDs"
Rectangle {
id: leftBox
x: 20; y: 30
width: 200; height: 200
color: "#4ecdc4"
radius: 8
Text {
id: leftLabel
text: "I am leftBox"
anchors.centerIn: parent
color: "white"
font.pixelSize: 16
}
}
Rectangle {
id: rightBox
x: 260; y: 30
width: 200; height: 200
color: "#ff6b6b"
radius: 8
Text {
id: rightLabel
text: "I reference leftBox"
anchors.centerIn: parent
color: "white"
font.pixelSize: 14
wrapMode: Text.WordWrap
width: parent.width - 20
horizontalAlignment: Text.AlignHCenter
}
}
Rectangle {
id: bottomBox
x: 20; y: 250
width: 460; height: 30
color: leftBox.color // Direct ID reference
radius: 4
}
MouseArea {
anchors.fill: parent
onClicked: {
// Access objects by ID from JavaScript
leftBox.color = "#ffe66d"
leftLabel.text = "Clicked!"
rightLabel.text = "leftBox is now yellow"
bottomBox.color = rightBox.color
}
}
}The bottom box sets its color directly to leftBox.color — a binding across siblings, using the ID leftBox. When you click, JavaScript code changes properties on leftBox, leftLabel, rightLabel, and bottomBox using their IDs.
Key points about IDs:
- IDs are file-scoped. Any object in the
.qmlfile can reference any other object by ID. They're not global across files. - IDs start with a lowercase letter by convention, though this is not enforced.
- IDs are not properties. You can't access
idat runtime —object.idreturnsundefined. IDs are compile-time references, not runtime strings. - You can use IDs in both bindings and JavaScript.
color: leftBox.coloris a declarative binding.leftBox.color = "#ffe66d"in JavaScript is an imperative assignment.
Let's Improve It
IDs really shine when you have deeply nested objects. Compare:
// Without IDs — fragile parent chains
Text {
text: parent.parent.parent.parent.someProperty
}
// With IDs — direct and robust
Text {
text: someObject.someProperty
}There's a practical difference: the first version breaks if you wrap the Text in an extra container. The second version works regardless of nesting.
Using the same ID twice. IDs must be unique within a file. The QML engine will emit an error if you declare two objects with the same id.
Trying to set id dynamically. id is a compile-time attribute and cannot be changed at runtime. It must be declared inline in the object definition.
Using parent when you mean an ID. parent is a property that follows the object tree. If you restructure the tree, parent changes. An ID follows the object, not the tree.
Under the Hood
When the QML engine compiles a file, it builds a symbol table of IDs. Each ID maps to the C++ object pointer of the instantiated type. The mapping is established once at creation time — there's no lookup overhead at runtime. This is why color: leftBox.color is as efficient as color: parent.color.
The engine also tracks reverse dependencies: if bottomBox.color is bound to leftBox.color, the engine registers a dependency from bottomBox's binding on leftBox's colorChanged signal. When leftBox.color changes, it triggers re-evaluation of bottomBox.color's binding.
Exercises
- IDs provide direct, stable references to any object in a QML file
- IDs are file-scoped, unique, and created at compile time
- Use IDs instead of parent chains for robust object references
- IDs work in both declarative bindings and imperative JavaScript