JSFiddle - React, Tailwind, and code Playground

HTML

<script src="http://cdn.ractivejs.org/edge/ractive.js"></script>
<main></main>

<script id='template' type='text/ractive'>
    <button on-click='undo'>undo</button>
    <button on-click='redo'>redo</button>
    
    <label>
        <p>What is your name?</p>
        <input value='{{name}}'/>
    </label>
    
    <label>
        <p>Enter some numbers:</p>
        <input type='number' value='{{a}}'/> <input type='number' value='{{b}}'/>
    </label>
    
    <h1>Hello jbbkjbjkbkjb {{name}}!</h1>
    <p>{{a}} + {{b}} = <strong>{{a+b+1}}</strong></p>
</script>

CSS

body {
    font-family: 'Helvetica Neue', arial, sans-serif;
    font-weight: 200;
    color: #353535;
}

h1, h2, h3, h4, h5, h6 {
    font-weight: 200;
}

JavaScript

var getTraverseFunction = function ( isUndo ) {
    return function () {
        var bound, state, promise;
        
        bound = isUndo ? 0 : this._undoStack.length - 1;
        if ( this._undoStackIndex === bound ) return;
        
        // decrement pointer
        this._undoStackIndex += isUndo ? -1 : 1;
        state = JSON.parse( this._undoStack[ this._undoStackIndex ] );
        
        this._traversingStack = true;
        
        promise = this.reset( state );
        this._traversingStack = false;
        
        return promise;
    };
};

var Undoable = Ractive.extend({
    data: {
        _undoStackSize: 50
    },
    
    init: function () {
        this._undoStack = [], this._undoStackIndex = -1;
        
        this.observe( function ( data ) {
            if ( this._traversingStack ) return;

            this._undoStackIndex += 1;
            this._undoStack[ this._undoStackIndex ] = JSON.stringify( data );
            
            // most recent change should become the end of the stack
            this._undoStack.length = this._undoStackIndex + 1;
        });
    },
    
    undo: getTraverseFunction( true ),
    redo: getTraverseFunction( false )
});

var ractive = new Undoable({
    el: 'main',
    template: '#template',
    data: { name: 'world', a: 2, b: 2 }
});

ractive.on( 'undo', ractive.undo );
ractive.on( 'redo', ractive.redo );