Slint Kit Documentation

Choices and values

Tag Input for Slint

KitTagInput · Slint Kit 0.3.0

Input for creating tags with a controlled chip list.

Input for creating tags with a controlled chip list.
Native app built from the example below.

Try the component

Download the complete project, extract it and run cargo run --locked in its folder. Rust 1.92+; Slint Kit 0.3.0 from crates.io. You can also copy the four files below.

Type Research and press Enter to add a tag. Remove a chip with its close button; Rust retains stable TagId values.

ui/app.slint

export { KitFormValidation, HtmlInputKind, NumberInputFormat, KitInputConstraints, KitValidationResult, KitValidityState } from "@kit/kit.slint";
// SPDX-License-Identifier: GPL-3.0-or-later
import { KitTagInput, ChipRow, KitTheme, KitThemes } from "@kit/kit.slint";
export component App inherits Window {
    title: "KitTagInput example";
    preferred-width: 520px;
    preferred-height: 300px;
    max-width: 100000px;
    max-height: 100000px;
    background: root.theme.canvas;
    in-out property <KitTheme> theme: KitThemes.light;
    in property <[ChipRow]> chips;
    in-out property <string> draft;
    in property <string> error;
    callback create(string);
    callback remove(int);
    VerticalLayout {
        padding: 24px;
        spacing: 16px;
        alignment: start;
        control := KitTagInput {
            theme: root.theme; label: "Project tags"; chips: root.chips; text <=> root.draft; error: root.error;
            create-requested(text) => { root.create(text); }
            remove-requested(token) => { root.remove(token); }
        }
        result := Text { text: root.chips.length + (root.chips.length == 1 ? " tag" : " tags"); color: root.theme.text-primary; }
    }

}

src/main.rs

// SPDX-License-Identifier: GPL-3.0-or-later
use slint::{ComponentHandle, ModelRc, VecModel};
use std::{cell::{Cell, RefCell}, rc::Rc};
mod ui { slint::include_modules!(); }
#[derive(Clone, Copy, PartialEq, Eq)]
struct TagId(i32);
#[derive(Clone)]
struct Tag { id: TagId, label: String }
fn publish(app: &ui::App, tags: &[Tag]) {
    app.set_chips(ModelRc::new(VecModel::from(tags.iter().map(|tag| ui::ChipRow { token: tag.id.0, label: tag.label.as_str().into(), removable: true }).collect::<Vec<_>>())));
}
fn main() -> Result<(), slint::PlatformError> {
    let app = ui::App::new()?;
    slint_kit::init_form_validation!(ui, app);
    let tags = Rc::new(RefCell::new(vec![Tag { id: TagId(1), label: "Design".into() }]));
    let next = Cell::new(2_i32);
    publish(&app, &tags.borrow());
    let weak = app.as_weak();
    let state = tags.clone();
    app.on_create(move |draft| { if let Some(app) = weak.upgrade() {
        let label = draft.trim();
        if label.is_empty() || label.chars().count() > 40 { app.set_error("Use 1 to 40 characters".into()); return; }
        if state.borrow().iter().any(|tag| tag.label == label) { app.set_error("Tag already exists".into()); return; }
        let token = next.get();
        next.set(token.checked_add(1).expect("tag identity capacity"));
        state.borrow_mut().push(Tag { id: TagId(token), label: label.into() });
        let snapshot = state.borrow().clone();
        publish(&app, &snapshot); app.set_draft("".into()); app.set_error("".into());
    } });
    let weak = app.as_weak();
    app.on_remove(move |token| { if let Some(app) = weak.upgrade() {
        tags.borrow_mut().retain(|tag| tag.id != TagId(token));
        let snapshot = tags.borrow().clone(); publish(&app, &snapshot);
    } });
    app.run()
}

Cargo.toml and build.rs

Cargo.toml

[package]
name = "slint-kit-tag-input-example"
version = "0.1.0"
edition = "2024"
rust-version = "1.92"
license = "GPL-3.0-or-later"
publish = false

[dependencies]
slint-kit = "=0.3.0"
slint = { version = "=1.18.1", default-features = false, features = ["std", "compat-1-18", "backend-winit", "renderer-skia", "accessibility"] }

[build-dependencies]
slint-kit-build = "=0.3.0"

[workspace]

build.rs

fn main() { slint_kit_build::compile("ui/app.slint").expect("compile component example"); }

Files: Cargo.toml · build.rs · ui/app.slint · src/main.rs

Bind text, supply chips and handle create-requested(string) to validate and create a tag. Handle remove-requested(token) to remove an existing item.

Enter passes the draft to the controller without adding a chip locally. Errors remain visible near the editor and actions respect FieldAccess.

Choose between built-in options and custom composition

Shared options

Visual composition

The file below produces the screenshot. For controller-driven components, it shows the displayed state without supplying the Rust wiring. Create a project.

Show the screenshot source
Input for creating tags with a controlled chip list.
Native screenshot of the same example, rendered with Skia.
// SPDX-License-Identifier: GPL-3.0-or-later
import { KitTagInput, KitTheme, KitThemes } from "@kit/kit.slint";

export component Example inherits Window {
    title: "KitTagInput example";
    preferred-width: 420px;
    preferred-height: 220px;
    max-width: 100000px;
    max-height: 100000px;
    background: root.theme.canvas;
    in-out property <KitTheme> theme: KitThemes.light;
    in-out property <string> status;
    VerticalLayout {
        padding: 24px; spacing: 16px; alignment: start;
        Text { text: "Tag Input"; color: root.theme.text-primary; font-size: 20px * root.theme.text-scale; font-weight: 600; }
        KitTagInput { theme: root.theme; label: "Project tags"; chips: [{ token: 11, label: "Design", removable: true }, { token: 23, label: "Research", removable: true }]; text: ""; create-requested(value) => { root.status = "Create tag: " + value; } }
        if root.status != "": Text { text: root.status; color: root.theme.text-secondary; font-size: 12px * root.theme.text-scale; wrap: word-wrap; }
    }
}

Download this example

Public API

Inherits Rectangle.

Properties

Name Type Direction
required bool in
required-message string in
custom-validity string in-out
validity KitValidityState out
will-validate bool out
validation-message string out
theme KitTheme in
control-size ControlSize in
access FieldAccess in
chips [ChipRow] in
label string in
error string in
text string in-out

Types and variants

Type Declaration
struct ChipRow token: int, label: string, removable: bool
enum ControlSize compact, medium, comfortable
enum FieldAccess editable, read-only, disabled
struct KitValidityState valid: bool, value-missing: bool, type-mismatch: bool, pattern-mismatch: bool, too-long: bool, too-short: bool, range-underflow: bool, range-overflow: bool, step-mismatch: bool, bad-input: bool, custom-error: bool,

Callbacks

Functions

See also