diff --git a/src/libs/timeline/qml/CategoryLabel.qml b/src/libs/timeline/qml/CategoryLabel.qml
index d5ab08400e6b90f0e4327a794d8c650ca03ac1e6..3f474b4e7dac8e08cc5dad21e7fb0fee1fff1af4 100644
--- a/src/libs/timeline/qml/CategoryLabel.qml
+++ b/src/libs/timeline/qml/CategoryLabel.qml
@@ -37,7 +37,6 @@ Item {
 
     property QtObject model
     property QtObject notesModel
-    property bool mockup
     property string text: model ? model.displayName : ""
     property bool expanded: model && model.expanded
     property var labels: model ? model.labels : []
@@ -57,11 +56,6 @@ Item {
 
     property bool reverseSelect: false
 
-    visible: model && (mockup || (!model.hidden && !model.empty))
-
-    height: model ? Math.max(txt.height, model.height) : 0
-    width: 150
-
     MouseArea {
         id: dragArea
         anchors.fill: txt
@@ -109,23 +103,15 @@ Item {
         visible: expanded
         Repeater {
             model: labels.length
-            Loader {
+            SynchronousReloader {
                 id: loader
                 asynchronous: dragOffset - draggerParent.contentY + y + txt.height >
                               draggerParent.height
+
                 active: expanded
                 width: labelContainer.width
                 height: column.parentModel ? column.parentModel.rowHeight(index + 1) : 0
 
-                onAsynchronousChanged: {
-                    if (!asynchronous && active && status !== Loader.Ready) {
-                        // Trigger a synchronous reload to avoid glitches
-                        var component = sourceComponent;
-                        sourceComponent = undefined;
-                        sourceComponent = component;
-                    }
-                }
-
                 sourceComponent: RowLabel {
                     label: labels[index];
                     onSelectBySelectionId: {
diff --git a/src/libs/timeline/qml/SynchronousReloader.qml b/src/libs/timeline/qml/SynchronousReloader.qml
new file mode 100644
index 0000000000000000000000000000000000000000..9092327e9c43b027631bf69b0707a1ad059c8404
--- /dev/null
+++ b/src/libs/timeline/qml/SynchronousReloader.qml
@@ -0,0 +1,43 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms and
+** conditions see http://www.qt.io/terms-conditions. For further information
+** use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+import QtQuick 2.0
+
+Loader {
+    onAsynchronousChanged: {
+        if (!asynchronous && active && status !== Loader.Ready) {
+            // Trigger a synchronous reload to avoid glitches
+            var component = sourceComponent;
+            sourceComponent = undefined;
+            sourceComponent = component;
+        }
+    }
+}
+
diff --git a/src/libs/timeline/qml/TimelineLabels.qml b/src/libs/timeline/qml/TimelineLabels.qml
index 300e98408f3080fc5738dae6a892cd7157d9d263..604658572bf9d50db16f3021e4904be3c0897d69 100644
--- a/src/libs/timeline/qml/TimelineLabels.qml
+++ b/src/libs/timeline/qml/TimelineLabels.qml
@@ -75,77 +75,87 @@ Flickable {
             }
 
             model: modelProxy.models
-            delegate: Rectangle {
-                color: categories.color
-
-                property int visualIndex: DelegateModel.itemsIndex
-                height: label.visible ? label.height : 0
+            delegate: SynchronousReloader {
+                id: loader
+                asynchronous: y < categories.contentY + categories.height &&
+                              y + height > categories.contentY
+                active: modelData !== null &&
+                        (modelProxy.height === 0 || (!modelData.hidden && !modelData.empty))
+                height: active ? Math.max(modelData.height, modelData.defaultRowHeight) : 0
                 width: categories.width
+                property int visualIndex: DelegateModel.itemsIndex
 
-                CategoryLabel {
-                    id: label
-                    model: modelData
-                    mockup: modelProxy.height === 0
-                    notesModel: modelProxy.notes
-                    visualIndex: parent.visualIndex
-                    dragging: categories.dragging
-                    reverseSelect: categories.reverseSelect
-                    onDragStarted: categories.dragging = true
-                    onDragStopped: categories.dragging = false
-                    draggerParent: categories
-                    width: 150
-                    dragOffset: parent.y
-
-                    onDropped: {
-                        categories.moveCategories(sourceIndex, targetIndex);
-                        labelsModel.items.move(sourceIndex, targetIndex);
-                    }
-
-                    onSelectById: {
-                        categories.selectItem(index, eventId)
+                sourceComponent: Rectangle {
+                    color: categories.color
+                    height: loader.height
+                    width: loader.width
+
+                    CategoryLabel {
+                        id: label
+                        model: modelData
+                        notesModel: modelProxy.notes
+                        visualIndex: loader.visualIndex
+                        dragging: categories.dragging
+                        reverseSelect: categories.reverseSelect
+                        onDragStarted: categories.dragging = true
+                        onDragStopped: categories.dragging = false
+                        draggerParent: categories
+                        width: 150
+                        height: parent.height
+                        dragOffset: parent.y
+
+                        onDropped: {
+                            categories.moveCategories(sourceIndex, targetIndex);
+                            labelsModel.items.move(sourceIndex, targetIndex);
+                        }
+
+                        onSelectById: {
+                            categories.selectItem(index, eventId)
+                        }
+
+                        onSelectNextBySelectionId: {
+                            categories.selectItem(index, modelData.nextItemBySelectionId(
+                                    selectionId, zoomer.rangeStart,
+                                    categories.selectedModel === index ? categories.selectedItem :
+                                                                         -1));
+                        }
+
+                        onSelectPrevBySelectionId: {
+                            categories.selectItem(index,  modelData.prevItemBySelectionId(
+                                    selectionId, zoomer.rangeStart,
+                                    categories.selectedModel === index ? categories.selectedItem :
+                                                                         -1));
+                        }
                     }
 
-                    onSelectNextBySelectionId: {
-                        categories.selectItem(index, modelData.nextItemBySelectionId(selectionId,
-                                zoomer.rangeStart,
-                                categories.selectedModel === index ? categories.selectedItem : -1));
+                    TimeMarks {
+                        id: timeMarks
+                        model: modelData
+                        mockup: modelProxy.height === 0
+                        anchors.right: parent.right
+                        anchors.left: label.right
+                        anchors.top: parent.top
+                        anchors.bottom: parent.bottom
+                        property int visualIndex: loader.visualIndex
+
+                        // Quite a mouthful, but works fine: Add up all the row counts up to the one
+                        // for this visual index and check if the result is even or odd.
+                        startOdd: (labelsModel.rowCounts.slice(0, visualIndex).reduce(
+                                       function(prev, rows) {return prev + rows}, 0) % 2) === 0
+
+                        onRowCountChanged: labelsModel.updateRowCount(visualIndex, rowCount)
+                        onVisualIndexChanged: labelsModel.updateRowCount(visualIndex, rowCount)
                     }
 
-                    onSelectPrevBySelectionId: {
-                        categories.selectItem(index, modelData.prevItemBySelectionId(selectionId,
-                                zoomer.rangeStart,
-                                categories.selectedModel === index ? categories.selectedItem : -1));
+                    Rectangle {
+                        opacity: loader.y === 0 ? 0 : 1
+                        color: "#B0B0B0"
+                        height: 1
+                        anchors.left: parent.left
+                        anchors.right: parent.right
+                        anchors.top: parent.top
                     }
                 }
-
-                TimeMarks {
-                    id: timeMarks
-                    model: modelData
-                    mockup: modelProxy.height === 0
-                    anchors.right: parent.right
-                    anchors.left: label.right
-                    anchors.top: parent.top
-                    anchors.bottom: parent.bottom
-                    property int visualIndex: parent.visualIndex
-
-                    // Quite a mouthful, but works fine: Add up all the row counts up to the one
-                    // for this visual index and check if the result is even or odd.
-                    startOdd: (labelsModel.rowCounts.slice(0, visualIndex).reduce(
-                                   function(prev, rows) {return prev + rows}, 0) % 2) === 0
-
-                    onRowCountChanged: labelsModel.updateRowCount(visualIndex, rowCount)
-                    onVisualIndexChanged: labelsModel.updateRowCount(visualIndex, rowCount)
-                }
-
-                Rectangle {
-                    visible: label.visible
-                    opacity: parent.y == 0 ? 0 : 1
-                    color: "#B0B0B0"
-                    height: 1
-                    anchors.left: parent.left
-                    anchors.right: parent.right
-                    anchors.top: parent.top
-                }
             }
         }
 
diff --git a/src/libs/timeline/qml/timeline.qrc b/src/libs/timeline/qml/timeline.qrc
index 4dc7e8c5a7f6bada03b15b6f666b6066e791e0df..540244538c086933fd9e5f1907f13328c37294c8 100644
--- a/src/libs/timeline/qml/timeline.qrc
+++ b/src/libs/timeline/qml/timeline.qrc
@@ -33,5 +33,6 @@
         <file>TimelineLabels.qml</file>
         <file>TimelineContent.qml</file>
         <file>RowLabel.qml</file>
+        <file>SynchronousReloader.qml</file>
     </qresource>
 </RCC>