JSFiddle - React, Tailwind, and code Playground

HTML

<script src="https://code.jquery.com/ui/1.10.4/jquery-ui.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.11.1/jquery.validate.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.11.1/additional-methods.min.js"></script>
<link rel="stylesheet" href="https://code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0-alpha/css/bootstrap.min.css">
<form id="daterange-form">
    <div class="form-group">
        <label for="startDate">Start Date</label>
        <input type="text" id="startDate" name="startDate" class="validDate form-control" />
    </div>

    <div class="form-group">
        <label for="endDate">End Date</label>
        <input type="text" id="endDate" name="endDate" class="validDate form-control" />
    </div>
    <button type="submit" class="btn">Submit</button>
</form>

CSS

body {
    padding: 20px
}

label.error {
    background: #c00;
    color: white;
    padding: 4px 8px;
    border-radius: 4px;
    margin-top: 8px;
}

JavaScript

// Custom Rules
$.validator.addMethod('dateBefore', function(value, element, params) {
    // if end date is valid, validate it as well
    console.log('dateBefore', value, element, params)
    var end = $(params);
    if (!end.data('validation.running')) {
        $(element).data('validation.running', true);
        // The validator internally keeps track of which element is being validated currently.  This ensures that validating 'end' will not trample 'start'
        // see http://stackoverflow.com/questions/22107742/jquery-validation-date-range-issue
        setTimeout($.proxy(
            function() {
                this.element(end);
            }, this), 0);
        // Ensure clearing the 'flag' happens after the validation of 'end' to prevent endless looping
        setTimeout(function() {
            $(element).data('validation.running', false);
        }, 0);
    }
    return this.optional(element) || this.optional(end[0]) || new Date(value) < new Date(end.val());
}, 'Must be before its end date');

$.validator.addMethod('dateAfter', function(value, element, params) {
    // if start date is valid, validate it as well
    console.log('dateAfter', value, element, params)
    var start = $(params);
    if (!start.data('validation.running')) {
        $(element).data('validation.running', true);
        // The validator internally keeps track of which element is being validated currently.  This ensures that validating 'end' will not trample 'start'
        // see http://stackoverflow.com/questions/22107742/jquery-validation-date-range-issue
        setTimeout($.proxy(
            function() {
                this.element(start);
            }, this), 0);
        // Ensure clearing the 'flag' happens after the validation of 'end' to prevent endless looping
        setTimeout(function() {
            $(element).data('validation.running', false);
        }, 0);
    }
    return this.optional(element) || this.optional(start[0]) || new Date(value) > new...