JSFiddle - React, Tailwind, and code Playground

HTML

<script type="text/html" id="validation">
  <span data-bind="foreach: $data" class="errors">
        <span data-bind='text: $data'> </span>
  </span>
</script>

<p>
  First name:
  <input data-bind='valueWithValidation: firstName' />
</p>
<p>
  Last name:
  <input data-bind='valueWithValidation: lastName' />
</p>
<p>
  Last name:
  <input data-bind='valueWithValidation: phoneNr' />
</p>

<p data-bind="text: errorSummary, visible: errorSummary()" class="errors"></p>

CSS

.errors {
  color: red;
}

JavaScript

ko.bindingHandlers.valueWithValidation = {
  init: function(element, valueAccessor, allBindingsAccessor) {
    // Interception! Add validation markup to the DOM and
    // apply the template binding to it. Some of this code
    // can be more elegant, especially if you use jQuery or
    // a similar library.
    var validationElement = document.createElement("span");
    element.parentNode.insertBefore(validationElement, element.nextSibling);
    ko.applyBindingsToNode(validationElement, {
      template: {
        name: 'validation',
        data: valueAccessor().errors
      }
    });

    // The rest of this binding is handled by the default
    // value binding. Pass it on!
    ko.applyBindingsToNode(element, {
      value: valueAccessor(),
      valueUpdate: 'afterkeydown'
    });
  }
};


ko.extenders.regex = function(target, options) {
  // Default options
  options = options || {};
  var regexp = new RegExp(options.pattern || ".*");
  var message = options.message || "regex is mad at you, bro!";

  // Only create sub-observable if it hasn't been created yet
  target.errors = target.errors || ko.observableArray();

  function validate(newValue) {
    var matched = regexp.test(newValue);

    if (!matched && target.errors.indexOf(message) == -1) {
      target.errors.push(message);
    } else if (matched && target.errors.indexOf(message) >= 0) {
      // TODO: support multiple extender instances with same message yet different pattern
      target.errors.remove(message);
    }
  }

  //initial validation
  validate(target());

  //validate whenever the value changes
  target.subscribe(validate);

  //return the original observable
  return target;
};

function AppViewModel(first, last, phone) {
  // These example regexes are silly, but wonderful for demo purposes :-)

  var self = this;

  self.firstName = ko.observable(first);
  self.firstName = self.firstName.extend({
    regex: {
      pattern: "^[^0-9]*$",
      message: "No digits plz!"
    }
  })
 ...