---
title: Name Resolution
description: Element names, pre-defined names, and lookup rules.
SC: true
---

import SC from '@slint/common-files/src/components/SC.astro';
import OnlyInSC from '@slint/common-files/src/components/OnlyInSC.astro';

Name resolution decides which declaration an identifier in an expression refers to.
Properties, callbacks, and functions share the same rules.

## Element Names

The `:=` syntax gives an element a name, also called its *id*:

```slint
export component MyApp inherits Window {
    hello := Text {
        text: "hello";
    }
}
```

An element name must be a valid [identifier](../lexical-structure/#sls.lex.identifier.classes).
As with all identifiers, `-` and `_` are interchangeable.

Element names must be unique within a component.
Two elements in the same component with the same id are a compile error, including elements inside `for` and `if` subtrees.
The names `self`, `parent`, and `root` are reserved and can't be used as an element name.

## Pre-Defined Names

Some elements are reachable under pre-defined names:

- `self` refers to the current element — the element in whose binding, callback, or function the name appears.
- `parent` refers to the element that encloses the current element.
  It is available only when the current element has an enclosing element within the same component.
- `root` refers to the outermost element of the current component.

`root` is the automatic id of every component's outermost element; an element named `root` explicitly is a reserved-id error.
`self`, `parent`, and `root` are element references and are used to qualify a member, as in `self.width` or `root.some-property`.

`true` and `false` are pre-defined names for the two boolean values and can't be redefined.

<SC>
## Lookup Rules

<OnlyInSC>
An unqualified identifier is resolved by trying, in order, `self`, `parent`, `true`, or `false`;
an element id; then a declared property, searching from the current element outward to the root.
The first match wins. \{#sls.name.lookup}

A qualified name refers to the named member of the element reached by the qualifier,
and does not fall back to ancestor elements. \{#sls.name.lookup.qualified}
</OnlyInSC>
</SC>

When an identifier is used without a qualifier, it is resolved by trying the following, in order, and taking the first match:

1. A local variable declared with `let` in an enclosing block, or a parameter of the enclosing callback or function handler.
2. `self`, `parent`, `true`, or `false`.
3. An element id.
   Ids are searched in the current component's element tree; the id of the current element and of any element reachable from `root` is visible, as is the id of an enclosing `for` element and of any element nested within it.
   `root` resolves here, as the id of the outermost element.
4. A declared property, callback, or function in scope.
   The current element (`self`) is tried first, then its enclosing element, and so on up to the root of the component.
   Only members introduced by a declaration in the source are found this way; a built-in property of the element or of an ancestor is *not* reachable unqualified and must be written `self.name` or through an id.
   Within a `for` element, the loop's index and model-data names resolve here.
5. A [global](../globals/) singleton or an enumeration, by name.
6. A built-in namespace: `Colors`, `Easing`, `Math`, `Key`, `FontWeight`, or `MouseCursor`.
7. A named value of the expected type: a named color where a `color` or `brush` is expected, a named easing curve where an `easing` is expected, or an enumeration value where that enumeration is expected.
   Writing an enumeration value without its type is therefore possible only in a position whose type is known to be that enumeration.
8. A built-in free function, such as the math and color functions and `debug`.

The first step that produces a match wins, so an earlier kind shadows a later one of the same name.
A local variable shadows a member in scope, a member on the current element shadows a member on an ancestor, and an id is chosen over a same-named property in scope.

### Qualified names

When an identifier is qualified with an element — an id, `self`, `parent`, or `root` — the member must be defined on that element or inherited from its base type.
Resolution does not fall back to ancestor elements in this case.
Calling a member without a qualifier is therefore *not* equivalent to calling it with `self`: the unqualified form searches the whole scope, while `self.name` looks only at the current element.

A member reached through a qualifier is subject to visibility: a `private` property, callback, or function of another component can't be accessed, and a special binding-only property can't be read.

A [global](../globals/)'s members are reached through its name, as in `Name.property`, `Name.callback(...)`, or `Name.function(...)`.

### Shadowing across elements

Because a declared member is looked up starting from the current element and moving outward, a member declared on an inner element shadows a same-named member declared on an enclosing element.
Multiple functions with the same name are allowed in one component as long as they are declared on different elements, and an inner one shadows an outer one for unqualified calls made within its subtree:

```slint
export component Example {
    property <int> secret_number: my-function();
    public pure function my-function() -> int {
        return 1;
    }

    VerticalLayout {
        public pure function my-function() -> int {
            return 2;
        }

        Text {
            text: "The secret number is " + my-function();
            public pure function my-function() -> int {
                return 3;
            }
        }

        Text {
            text: "The other secret number is " + my-function();
        }
    }
}
```

Here `secret_number` is set to 1, the first label reads "The secret number is 3", and the second reads "The other secret number is 2".
