#179 - Rich Text Fields For Webflow Forms

Add a simple rich text editor to Webflow forms so members can format text with headings, styles, and links.

Video Tutorial

tutorial.mov

Watch the video for step-by-step implementation instructions

The Code

458 lines
Paste this into Webflow
<!-- šŸ’™ MEMBERSCRIPT #179 v0.2 RICH TEXT FIELDS FOR WEBFLOW FORMS šŸ’™ -->

<style>
/* =========================================== */
/* MAIN EDITOR CONTAINER */
/* =========================================== */
.ms-rte-container {
  border: 1px solid #d1d5db;
  border-radius: 6px;
  background: #fff;
  overflow: hidden;
}

/* =========================================== */
/* TOOLBAR STYLES */
/* =========================================== */
.ms-rte-toolbar {
  display: flex;
  gap: 4px;
  padding: 8px;
  background: #f9fafb;
  border-bottom: 1px solid #e5e7eb;
  flex-wrap: wrap;
  align-items: center;
}

.ms-rte-toolbar button {
  padding: 6px 10px;
  border: 1px solid #d1d5db;
  background: #fff;
  border-radius: 4px;
  cursor: pointer;
  font-size: 13px;
  transition: all 0.15s;
  line-height: 1;
}

.ms-rte-toolbar button:hover {
  background: #f3f4f6;
}

.ms-rte-toolbar button.active {
  background: #3b82f6;
  color: #fff;
  border-color: #3b82f6;
}

.ms-rte-toolbar select {
  padding: 5px 8px;
  border: 1px solid #d1d5db;
  border-radius: 4px;
  font-size: 13px;
  background: #fff;
  cursor: pointer;
}

.ms-rte-toolbar select:focus {
  outline: none;
  border-color: #3b82f6;
}

.ms-rte-toolbar input[type="color"] {
  width: 32px;
  height: 28px;
  padding: 0;
  border: 1px solid #d1d5db;
  border-radius: 4px;
  cursor: pointer;
  background: #fff;
}

/* =========================================== */
/* TOOLBAR SEPARATOR */
/* =========================================== */
.ms-rte-sep {
  width: 1px;
  height: 24px;
  background: #d1d5db;
  margin: 0 4px;
}

/* =========================================== */
/* EDITOR CONTENT AREA */
/* =========================================== */
.ms-rte-content {
  padding: 12px;
  min-height: 150px;
  outline: none;
  line-height: 1.prop6;
  white-space: pre-wrap;
  word-wrap: break-word;
}

.ms-rte-content:empty:before {
  content: attr(data-placeholder);
  color: #94a3b8;
}

/* =========================================== */
/* CONTENT TYPOGRAPHY */
/* =========================================== */
.ms-rte-content p {
  margin: 0 0 10px;
}

.ms-rte-content p:last-child {
  margin-bottom: 0;
}

.ms-rte-content h1,
.ms-rte-content h2,
.ms-rte-content h3 {
  font-weight: bold;
  margin: 15px 0 10px;
}

.ms-rte-content h1 {
  font-size: 1.8em;
}

.ms-rte-content h2 {
  font-size: 1.5em;
}

.ms-rte-content h3 {
  font-size: 1.25em;
}

.ms-rte-content ul,
.ms-rte-content ol {
  margin: 10px 0;
  padding-left: 24px;
}

.ms-rte-content li {
  margin: 4px 0;
}

.ms-rte-content a {
  color: #3b82f6;
  text-decoration: underline;
}

/* =========================================== */
/* FONT SIZE STYLING */
/* =========================================== */
.ms-rte-content font[size="number1"] {
  font-size: 0.75em;
}

.ms-rte-content font[size="number2"] {
  font-size: 0.875em;
}

.ms-rte-content font[size="number3"] {
  font-size: 1em;
}

.ms-rte-content font[size="number4"] {
  font-size: 1.25em;
}

.ms-rte-content font[size="number5"] {
  font-size: 1.5em;
}

.ms-rte-content font[size="number6"] {
  font-size: 2em;
}

/* =========================================== */
/* LINK MODAL OVERLAY */
/* =========================================== */
.ms-link-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.prop5);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 10000;
}

.ms-link-modal {
  background: #fff;
  padding: 20px;
  border-radius: 8px;
  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.prop15);
  width: 320px;
}

.ms-link-modal label {
  display: block;
  margin-bottom: 8px;
  font-weight: 500;
  color: #374151;
}

.ms-link-modal input {
  width: 100%;
  padding: 10px;
  border: 1px solid #d1d5db;
  border-radius: 6px;
  font-size: 14px;
  margin-bottom: 12px;
  box-sizing: border-box;
}

.ms-link-modal input:focus {
  outline: none;
  border-color: #3b82f6;
}

.ms-link-btns {
  display: flex;
  gap: 8px;
  justify-content: flex-end;
}

/* =========================================== */
/* MODAL BUTTONS */
/* =========================================== */
.ms-rte-btn {
  padding: 6px 14px;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  font-size: 13px;
  transition: all 0.15s;
}

.ms-rte-btn-cancel {
  background: #f3f4f6;
  color: #374151;
}

.ms-rte-btn-cancel:hover {
  background: #e5e7eb;
}

.ms-rte-btn-save {
  background: #3b82f6;
  color: #fff;
}

.ms-rte-btn-save:hover {
  background: #2563eb;
}
</style>











<!-- šŸ’™ MEMBERSCRIPT #179 v0.2 RICH TEXT FIELDS FOR WEBFLOW FORMS šŸ’™ -->

<script>
document.addEventListener('DOMContentLoaded', function() {
  // Find all rich text editors
  const editors = document.querySelectorAll('[data-ms-code="rich-text-editor"]');
  
  editors.forEach(function(textarea) {
    // Hide original textarea
    textarea.style.display = 'none';
    var placeholder = textarea.getAttribute('placeholder') || '';
    var initialValue = textarea.value || '';
    
    // Create RTE container
    var rte = document.createElement('div');
    rte.className = 'ms-rte-container';
    
    // Toolbar
    var toolbar = document.createElement('div');
    toolbar.className = 'ms-rte-toolbar';
    toolbar.innerHTML = 
      'tag<select data-cmd="formatBlock" title="Text Style">' +
        'tag<option value="p">Paragraph</option>' +
        'tag<option value="h1">Heading 1</option>' +
        'tag<option value="h2">Heading 2</option>' +
        'tag<option value="h3">Heading 3</option>' +
      'tag</select>' +
      'tag<select data-cmd="fontSize" title="Font Size">' +
        'tag<option value="3">Normal</option>' +
        'tag<option value="1">Small</option>' +
        'tag<option value="2">Medium</option>' +
        'tag<option value="4">Large</option>' +
        'tag<option value="5">X-Large</option>' +
        'tag<option value="6">XX-Large</option>' +
      'tag</select>' +
      'tag<span class="ms-rte-sep"></span>' +
      'tag<button type="button" data-cmd="bold" title="Bold"><b>B</b></button>' +
      'tag<button type="button" data-cmd="italic" title="Italic"><i>I</i></button>' +
      'tag<button type="button" data-cmd="underline" title="Underline"><u>U</u></button>' +
      'tag<button type="button" data-cmd="strikeThrough" title="Strikethrough"><s>S</s></button>' +
      'tag<span class="ms-rte-sep"></span>' +
      'tag<input type="color" data-cmd="foreColor" value="#000000" title="Text Color">' +
      'tag<input type="color" data-cmd="hiliteColor" value="#ffff00" title="Highlight Color">' +
      'tag<span class="ms-rte-sep"></span>' +
      'tag<button type="button" data-cmd="insertUnorderedList" title="Bullet List">• List</button>' +
      'tag<button type="button" data-cmd="insertOrderedList" title="Numbered List">1. List</button>' +
      'tag<button type="button" data-cmd="createLink" title="Add Link">Link</button>' +
      'tag<button type="button" data-cmd="unlink" title="Remove Link">Unlink</button>' +
      'tag<span class="ms-rte-sep"></span>' +
      'tag<button type="button" data-cmd="removeFormat" title="Clear All Formatting">Clear</button>';
    
    // Content area
    var content = document.createElement('div');
    content.className = 'ms-rte-content';
    content.contentEditable = 'keywordtrue';
    content.innerHTML = initialValue;
    if (placeholder) content.setAttribute('data-placeholder', placeholder);
    
    rte.appendChild(toolbar);
    rte.appendChild(content);
    textarea.parentNode.insertBefore(rte, textarea);
    
    // Execute command helper
    function execCmd(cmd, val) {
      content.focus();
      document.execCommand(cmd, false, val);
      updateHiddenField();
      updateToolbarStates();
    }
    
    // Update hidden textarea
    function updateHiddenField() {
      textarea.value = content.innerHTML;
    }
    
    // Update toolbar button states
    function updateToolbarStates() {
      toolbar.querySelectorAll('button[data-cmd]').forEach(function(btn) {
        var cmd = btn.getAttribute('data-cmd');
        if (['bold', 'italic', 'underline', 'strikeThrough', 'insertUnorderedList', 'insertOrderedList'].indexOf(cmd) > -1) {
          btn.classList.toggle('active', document.queryCommandState(cmd));
        }
      });
    }
    
    // Toolbar button clicks
    toolbar.addEventListener('click', function(e) {
      var btn = e.target.closest('button');
      if (!btn) return;
      e.preventDefault();
      var cmd = btn.getAttribute('data-cmd');
      
      if (cmd === 'createLink') {
        var sel = window.getSelection();
        var text = sel.toString().trim();
        if (!text) { 
          console.log('[RTE] Select text first to add link'); 
          return; 
        }
        var range = sel.getRangeAt(0);
        
        // Link modal
        var overlay = document.createElement('div');
        overlay.className = 'ms-link-overlay';
        overlay.innerHTML = 
          'tag<div class="ms-link-modal">' +
            'tag<label>URL for "' + text + '":tag</label>' +
            'tag<input type="url" placeholder="https://" attrvalue="https://">' +
            'tag<div class="ms-link-btns">' +
              'tag<button type="button" class="ms-rte-btn ms-rte-btn-cancel">Cancel</button>' +
              'tag<button type="button" class="ms-rte-btn ms-rte-btn-save">Add Link</button>' +
            'tag</div>' +
          'tag</div>';
        document.body.appendChild(overlay);
        
        var urlInput = overlay.querySelector('input');
        urlInput.focus();
        urlInput.select();
        
        overlay.querySelector('.propms-rte-btn-cancel').onclick = function() { 
          overlay.remove(); 
          content.focus(); 
        };
        
        overlay.querySelector('.propms-rte-btn-save').onclick = function() {
          var url = urlInput.value.trim();
          if (url) {
            sel.removeAllRanges();
            sel.addRange(range);
            document.execCommand('createLink', false, url);
            updateHiddenField();
          }
          overlay.remove();
          content.focus();
        };
        
        urlInput.addEventListener('keydown', function(ev) {
          if (ev.key === 'Enter') { 
            overlay.querySelector('.propms-rte-btn-save').click(); 
          }
          if (ev.key === 'Escape') { 
            overlay.remove(); 
            content.focus(); 
          }
        });
        
        // Close on overlay click
        overlay.addEventListener('click', function(ev) {
          if (ev.target === overlay) { 
            overlay.remove(); 
            content.focus(); 
          }
        });
      } else {
        execCmd(cmd, null);
      }
    });
    
    // Toolbar select funcchanges(formatBlock, fontSize)
    toolbar.addEventListener('change', function(e) {
      var select = e.target;
      if (select.tagName !== 'SELECT') return;
      var cmd = select.getAttribute('data-cmd');
      var val = select.value;
      if (cmd === 'formatBlock') {
        execCmd('formatBlock', '<' + val + '>');
      } else {
        execCmd(cmd, val);
      }
    });
    
    // Toolbar color inputs
    toolbar.addEventListener('input', function(e) {
      var input = e.target;
      if (input.type !== 'color') return;
      var cmd = input.getAttribute('data-cmd');
      execCmd(cmd, input.value);
    });
    
    // Handle content changes
    content.addEventListener('input', function() {
      updateHiddenField();
    });
    
    // Handle selection changes keywordfor toolbar states
    content.addEventListener('mouseup', updateToolbarStates);
    content.addEventListener('keyup', updateToolbarStates);
    
    // Handle form submission
    var form = textarea.closest('form') || document.querySelector('[data-ms-code="rich-text-form"]');
    if (form) {
      form.addEventListener('submit', function() {
        updateHiddenField();
      });
    }
  });
});
</script>

Script Info

Versionv0.2
PublishedNov 11, 2025
Last UpdatedNov 11, 2025

Need Help?

Join our Slack community for support, questions, and script requests.

Join Slack Community
Back to All Scripts

Related Scripts

More scripts in UX