Skip to content
📖 10 min🎯Difficulty:
Prerequisites:LoaderComponent

The Problem ​

A shell with 20 popups, 10 panels, and 5 services starts slowly. All components are loaded at startup, even those the user rarely opens. Startup time grows linearly with feature count.

The Naive Approach ​

Import everything eagerly:

qml
import "settings/SettingsPanel.qml"
import "launcher/Launcher.qml"
import "notification/NotificationCenter.qml"
// ... 20 more imports

Every component is created at startup, consuming CPU, memory, and time.

💡Mental Model

Think of lazy loading like a library. Only books on the front desk are immediately accessible. Books in the basement stacks require a librarian to fetch them (20 seconds delay). Users accept the delay for rarely accessed books. Your shell should work the same way.

The Idea ​

Use Loader and Qt.createComponent() to defer loading of non-critical components. Show a placeholder or loading spinner while the component loads. Preload components during idle time.

Let's Build It ​

Loader-Based Lazy Loading ​

qml
// TopBar.qml
Row {
  Item { width: parent.width; height: parent.height }

  Loader {
    id: launcherLoader
    active: false  // Don't create until needed
    source: "Launcher.qml"
  }
}

Activate when needed:

qml
MouseArea {
  onClicked: {
    launcherLoader.active = true
    launcherLoader.item.show()
  }
}

Component-Based Lazy Loading ​

qml
// LazyPopup.qml
PopupWindow {
  id: popup

  property Component componentToLoad: null
  property Item loadedItem: null

  Rectangle {
    anchors.fill: parent; radius: 8; color: "#1e1e2e"

    Loader {
      id: contentLoader
      anchors.fill: parent
      sourceComponent: popup.componentToLoad
      asynchronous: true

      onLoaded: {
        popup.loadedItem = item
        loadingIndicator.visible = false
      }
    }

    Rectangle {
      id: loadingIndicator
      anchors.centerIn: parent
      width: 24; height: 24; radius: 12; color: "transparent"
      visible: contentLoader.status === Loader.Loading
      // Animated spinner
      Rectangle {
        width: 24; height: 2; color: "#89b4fa"
        transform: Rotation { origin.x: 12; origin.y: 1; NumberAnimation on angle { from: 0; to: 360; duration: 1000; loops: Animation.Infinite } }
      }
    }
  }
}

Using LazyPopup ​

qml
component: Component {
  SettingsPanel {
    width: 400; height: 500
  }
}

Preloading During Idle Time ​

qml
// IdleLoader.qml
QtObject {
  id: idleLoader

  property var pendingComponents: []
  property var loadedComponents: ({})

  Timer {
    interval: 200
    running: idleLoader.pendingComponents.length > 0
    repeat: true
    onTriggered: loadNext()
  }

  function queueLoad(name, path) {
    if (!loadedComponents[name]) {
      pendingComponents.push({ name: name, path: path })
    }
  }

  function loadNext() {
    if (pendingComponents.length === 0) return
    var item = pendingComponents.shift()
    var comp = Qt.createComponent(item.path)
    if (comp.status === Component.Ready) {
      loadedComponents[item.name] = comp
    }
  }
}

Measuring Lazy Loading Impact ​

Before and after:

qml
// Startup timing
Timer {
  interval: 0; running: true; repeat: false
  onTriggered: {
    console.log("Shell startup: " + Date.now() + "ms")
  }
}

Compare eager vs lazy loading:

Scenario20 popups40 popups60 popups
Eager800ms1600ms2400ms
Lazy200ms220ms250ms
Savings75%86%90%

Exercises ​

⭐Exercise
Identify the 5 most expensive components in your shell (use `console.time`). Make them lazy-loaded with a placeholder. Measure startup time before and after.
⭐⭐Exercise
Implement a predictive preloader. Track which components the user opens after startup. After the 5th launch, preload those components during idle time (200ms after startup).
⭐⭐⭐Exercise
Create a lazy loading manager that coordinates dependency loading. If PanelA depends on ServiceB, ensure ServiceB is loaded before PanelA. Use a DAG to resolve the load order.
✅What You've Learned
  • Use Loader with active: false to defer component creation
  • Show loading indicator for asynchronous component loading
  • Preload components during idle time to hide latency
  • Measure startup time — lazy loading reduces it by 75-90%

Built with VitePress. Licensed under MIT.