JSFiddle - React, Tailwind, and code Playground

HTML

<script src="//cdnjs.cloudflare.com/ajax/libs/knockout/2.3.0/knockout-debug.js"></script>
<script src="http://www.appelsiini.net/download/jquery.viewport.js"></script>
<ul data-bind="foreach: stations">
    <li>
        <img class="lazy" data-bind="lazyImage: imageUrl" />
    </li>
</ul>

CSS

ul {
    position: relative;
    width: 100%;
    padding: 0;
    list-style-type: none;
}
    
li img {
  width: 100%;
}

JavaScript

var Station,
    Viewmodel;

ko.bindingHandlers.lazyImage = {
    update: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
        var $element     = $(element),
            // we unwrap our imageUrl to get a subscription to it,
            // so we're called when it changes.
            // Use ko.utils.unwrapObservable for older versions of Knockout
            imageSource  = ko.unwrap(valueAccessor());
        
        $element.attr('src', imageSource);
        
        // we don't want to remove the lazy class after the temp image
        // has loaded. Set placehold.it to something that identifies
        // your real placeholder image
        if (imageSource.indexOf('placehold.it') === -1) {
            $element.one('load', function() {
                $(this).removeClass('lazy');
            });
        }
    }
};

Station = function Station(tempUrl, thumbUrl) {
    var that = this;
    
    this.showPlaceholder = ko.observable(true);
    this.imageTemp       = ko.observable(tempUrl);
    this.imageThumb      = ko.observable(thumbUrl);
    this.imageUrl        = ko.computed(function() {
        return that.showPlaceholder() ? that.imageTemp() : that.imageThumb();
    });
};

Viewmodel = function Viewmodel() {
    
    this.stations = ko.observableArray([]);
    
    for (var i=0; i < 10; i++) {
        this.stations.push( new Station('https://cdn.sstatic.net/Sites/stackoverflow/img/apple-touch-icon.png?v=c78bd457575a', 'https://cdn.sstatic.net/Sites/stackoverflow/img/apple-touch-icon.png?v=c78bd457575a') );
    }
};

ko.applyBindings( new Viewmodel() );

var lazyInterval = setInterval(function () {
    $('.lazy:in-viewport').each(function () {
      if (ko.dataFor(this)) {
        ko.dataFor(this).showPlaceholder(false);
      }
    });
}, 1000);