Skip to content

Callbacks

A callback is a member that carries an invocation from one place to another. It’s declared with the callback keyword, invoked by calling it, and reacted to by a handler set with the => arrow.

export component Example inherits Window {
callback hello;
TouchArea {
clicked => { root.hello(); }
}
}
slint

A callback may be declared in a component, on any element within a component, or in a global.

A callback declaration adds a callback to the element in whose body it appears. It consists of the keyword callback, a name, and ;.

A callback name shall not collide with another member of the same element: a declaration whose name matches an existing property, function, or callback is an error.

Between the name and the ;, a declaration may also carry the callback’s parameter types and its return type:

export component Example {
callback plain; // no parameters, no return value
callback with-args(int, string); // two parameters
callback with-return(int, int) -> int;
}
slint

The following forms are allowed:

  • No parentheses declares a callback with no parameters, as in callback plain;.
  • Parentheses list the parameter types, as in callback with-args(int, string);. A trailing comma after the last parameter is allowed.
  • A parameter may carry a name, written name: type, as in callback c(foo: int, bar: string). Names are optional and may be mixed with unnamed parameters. The name carries no semantic value; a handler names its parameters independently.
  • A return type is written after -> and requires the parentheses to be present, even when there are no parameters: callback c() -> int. Writing -> type without parentheses is a compile error.

pure may precede callback to mark the callback as having no side effects (see Purity). A pure callback may be called from a pure context, and its handler must itself be pure. Calling an impure callback from a pure context is a compile error.

A duplicate callback declaration is a compile error.

A callback is invoked with call syntax: the callback, followed by parentheses holding the arguments.

Invoking a callback runs its handler. A callback without a handler does nothing when invoked.

export component Example inherits Window {
callback acknowledged;
TouchArea {
clicked => { root.acknowledged(); }
}
}
slint

The number of arguments must match the declaration, and a callback with a return type returns the default value of that type when it has no handler:

export component Example {
pure callback hello(int, int) -> int;
property <int> result: hello(1, 2);
}
slint

A handler reacts to a callback’s invocation. It’s written as the callback’s name, =>, and either a single expression or a code block.

A callback has at most one handler; setting a second handler for the same callback is an error.

A handler may be set on a callback of a child element, such as TouchArea’s clicked.

export component Example inherits Window {
callback first;
callback second;
TouchArea {
clicked => {
root.first();
root.second();
}
}
}
slint
  • The handler names its parameters in parentheses. These names are local to the handler body and are independent of any names in the declaration. Parentheses may be omitted when the callback has no parameters.
  • The body’s last expression is the return value; its type must match the callback’s return type.
export component Example {
callback sum(int, int) -> int;
sum(a, b) => { a + b }
}
slint

A handler may also be set on a callback declared in the same component.

init is a built-in callback present on every element. Its handler runs once when the element is instantiated. init handlers run from the outermost component inward, so a component’s own init runs after the init of the elements it contains. Calling init explicitly does nothing and is deprecated.

A global can neither declare nor handle an init callback; both are compile errors.

A callback alias forwards one callback to another, using the same <=> syntax as a two-way binding:

export component Example {
callback clicked <=> area.clicked;
area := TouchArea {}
}
slint
  • The alias must omit the parameter list; writing parentheses in an aliased declaration is a compile error.
  • Both callbacks must have the same signature.
  • Invoking either callback invokes the other, and a handler set on either responds to both.

An alias may target a global’s callback, written Global.callback. A global may alias another global’s callback; this is the supported way for one global to implement another’s callback. A non-global element cannot set a handler on an alias that targets a global’s callback, because the element may be instantiated many times while the global is a singleton; this is a compile error.

changed <property> => { ... } declares a handler that runs when a property’s value changes:

export component Example inherits Window {
in-out property <string> value;
changed value => { debug(self.value); }
}
slint
  • The target must be a property that exists on the element; naming a callback, a function, or a nonexistent member is a compile error.
  • A change callback cannot be set on a private property that belongs to another component; this is a compile error.
  • A property has at most one change callback per element; a duplicate is a compile error.
  • The handler body may have side effects; it is not required to be pure.

A change callback is not invoked immediately. It is queued and runs in a later iteration of the event loop. It runs only if the value actually differs from the value at the previous run: multiple changes within one cycle collapse to a single invocation, and a value that changes and reverts before the callback runs produces no invocation.

Changing a property from within a change callback in a way that feeds back into the same property is undefined:

export component Example {
in-out property <int> foo;
property <int> bar: foo + 1;
changed bar => { foo += 1; }
}
slint

Such a chain, where one change callback triggers another, does not run unbounded. The runtime runs the queued change handlers in a loop that stops after ten passes, so a self-perpetuating sequence terminates without invoking the remaining handlers.

A change callback forces the property to be evaluated eagerly rather than lazily, and its side effects make the surrounding bindings impure. A declarative binding tracks its dependencies automatically and evaluates on demand, so a relationship that a binding can express should be expressed as a binding rather than a change callback.


© 2026 SixtyFPS GmbH