JSFiddle - React, Tailwind, and code Playground

HTML

<textarea id="text" cols="50" rows="3"></textarea>

CSS

http://jsfiddle.net/HXdnd/4/#run

JavaScript

// Caret positioning code
function getCaretPosition(el) { 
  if (el.selectionStart) { 
    return el.selectionStart; 
  } else if (document.selection) { 
    el.focus(); 

    var r = document.selection.createRange(); 
    if (r == null) { 
      return 0; 
    } 

    var re = el.createTextRange(), 
        rc = re.duplicate(); 
    re.moveToBookmark(r.getBookmark()); 
    rc.setEndPoint('EndToStart', re); 

    return rc.text.length; 
  }
    
  return 0; 
}

function setCaretPosition(el, caretPos) {
  if (el.createTextRange) {
    var range = el.createTextRange();
    range.move('character', caretPos);
    range.select();
  } else if (el.selectionStart) {
    el.focus();
    el.setSelectionRange(caretPos, caretPos);
  } else {
    el.focus();
  }
}


// Formatting code

var area = document.getElementById('text');

area.addEventListener('keyup', function(e) {
  formatTextarea(area, 3, 55);
}, false);

function formatTextarea(textarea, maxLines, maxChars) {
  var caret = getCaretPosition(textarea);
  var value = textarea.value;
  var lines = value.split('\n');
    
  lines.forEach(function(line, li) {
    var words = line.split(/\s+/);
    line = words[0] || '';
      
    for (var i = 1, length = words.length; i < length; i++) {
      if ((line.length + 2 + words[i].length) <= maxChars) {
        if (words[i] !== ' ')
          line += ' ' + words[i];    
      } else {
        if (lines.length <= li + 1) {
          lines.push(words[i]);
        } else {
          lines[li + 1] = words[i] + ' ' + lines[li + 1];
        }
      }
    }
      
    lines[li] = line;
  });
    
  textarea.value = lines.join('\n');
  setCaretPosition(textarea, caret);
  // evt: textarea.value = lines.slice(0, maxLines).join('\n');
}