JSFiddle - React, Tailwind, and code Playground

HTML

<link rel="stylesheet" href="http://netdna.bootstrapcdn.com/twitter-bootstrap/2.3.1/css/bootstrap-combined.min.css">
<div id="grid" style="padding:10px;">
    <form class="form-inline">
    <div class="input-prepend"> <span class="add-on"><i class="icon-search"></i></span><input class="span2"
        type="text" data-bind="value: searchTerm, valueUpdate:'afterkeydown'"/>
                    </div>
        <button class="btn pull-right disabled">Edit</button>
        <button class="btn pull-right">Add</button>
    </form>
    <table class="table table-bordered table-striped table-hover">
        <thead>
            <tr data-bind="foreach: columns">
                <th data-bind="html: $data.labelHTML, click: $parent.sort"></th>
            </tr>
        </thead>
        <tbody data-bind="foreach: models">
            <tr>
                <td data-bind="text: $data.ID"></td>
                <td data-bind="text: $data.FirstName"></td>
                <td data-bind="text: $data.LastName"></td>
                <td data-bind="text: $data.Username"></td>
            </tr>
        </tbody>
    </table>
    <div class="pagination pagination-centered">
        <ul data-bind="foreach: ko.utils.range(1,pageCount)">
            <li data-bind="css: { active: $data===$root.page()}"><a data-bind="text: $data, click: function() { $root.page($data)}"></a>
            </li>
        </ul>
    </div>
</div>

CSS

i.blank {
    display: inline-block;
    width: 14px;
    height: 14px;
    line-height: 14px;
    vertical-align: text-top;
    margin-top:1px;
}

JavaScript

function Grid(Model, Element, Options) {
    var self = this;
    self.options = Options || {};
    self.sort = function (column) {
        if (column === self.sortedColumn) {
            self.sortedColumn.toggleSort();
        } else {
            var oldSort = self.sortedColumn;
            oldSort.sortDirection(null);
            self.sortedColumn = column;
            self.sortedColumn.sortDirection("asc");
        }
        self.internalModels.sort(function (left, right) {
            return self.sortedColumn.sort(left, right, self.sortedColumn.property, self.sortedColumn.sortDirection());
        });
    };
    self.sortedColumn = Model.columns()[0];
    self.columns = Model.columns;
    self.searchTerm = ko.observable("");
    self.internalModels = ko.observableArray(Model.Get());
    self.filteredModels = ko.computed(function () {
        var searchText = self.searchTerm().toLowerCase();
        var internalModels = self.internalModels();
        var models = [];
        var searchMatches = [];
        if (searchText !== "") {
            for (var i = 0; i < internalModels.length; i++) {
                var obj = internalModels[i];
                var matches = findInProperties(searchText,obj,null);
                if(matches.length>0) {
                    models.push(obj);
                    searchMatches.push({"object": obj, "results": matches});
                }
            }
        } else {
            models = self.internalModels().slice(0);
        }
        return models;
    });
    function findInProperties(search,obj,field) {
        var matches = [];
        for (var property in obj) {
            if (obj.hasOwnProperty(property)) {
                var match = getMatch(search,obj,property);
                if(match) matches.push(match);
            }
        } 
        return matches;
    }
    function getMatch(search,obj,field) { 
        if(field!==null) {
            var index =...