JSFiddle - React, Tailwind, and code Playground

HTML

<script src="http://ajax.aspnetcdn.com/ajax/knockout/knockout-3.0.0.js"></script>
<div data-bind="template: {afterRender: hide}">
    <table data-bind="html: html">
    </table>
</div>

<div id="spinner" style="display: none">Loading...</div>

<button onclick="go()">Go!</button>

CSS

#spinner {
    position: absolute;
    top: 20px;
    left: 20px;
    background-color: white;
    border: 1px solid green;
}

.stripe {
    background-color: red;
}

JavaScript

// Use a non-observable array to build the 5000 items
plain_items = [];

// Build 5000 items, as a performance challenge!
for (var i = 0; i <= 5000; i++) {
    plain_items.push("Item " + i)
}

// Put the items in a VM
list = function() {
    this.items = ko.observableArray()
    
    // This HTML will be displayed in a single DOM manipulation via the html binding,
    // which is faster than inserting each row one by one into the DOM.
    this.html = ko.computed(function() {
        var html = "", i = 0, item
        for (i = 0; i < this.items().length; i++) {
            var item = this.items()[i];
            html = html + "<tr><td>" + item + "</td><td><button onclick=\"retrieve(" + i + ", 'delete')\">delete</button></td></tr>"
        }
        return html;
    }, this)
}
vm = new list()

// Trigger the rendering from a button click
go = function() {
    // Show the spinner immediately...
    $("#spinner").show();
    
    // ... by using a timeout.
    window.setTimeout(function() {
        ko.applyBindings(vm) 
        vm.items(plain_items)
    }, 1)
}

// Hide the spinner by applying this function to a template: {afterRender: ...} binding
hide = function() {
    $("#spinner").hide()
}

// Retrieve the item from the index
retrieve = function(i, action) {
    var item = vm.items()[i]
    if (action == "delete") vm.items.remove(item)
    // You could put other actions here...
    // else if (action == "edit") vm.edit(item)
}