Use Case

    Rich Text Chat & Messaging Input

    Add a compact messaging input with Scribe: floating toolbar, send-on-Enter wired via a keydown handler, message HTML via getHTML(), and XSS-safe rendering of received messages — all under 50KB with zero dependencies.

    Floating
    Compact toolbar
    Enter
    Send on keydown
    XSS-safe
    Render messages

    Everything a chat input needs

    Compact, minimal input

    A floating toolbar keeps the chat input small until the user selects text. Inline formatting via editor.bold() and editor.italic() — no bulky chrome in a single-line-ish input.

    Send on Enter

    Wire a keydown handler: Enter sends the message and clears the input via setHTML(""), while Shift+Enter inserts a newline. Pull the message HTML with getHTML().

    XSS-safe rendering

    Messages from other users are untrusted HTML. Scribe sanitizes HTML it processes — strip <script>, inline event handlers, and dangerous markup before rendering received messages into the DOM.

    Custom plugins for extras

    Scribe has a plugin architecture. TODO(verify): @mentions are not confirmed as a built-in feature — implement mention autocomplete as a custom plugin rather than assuming it ships by default.

    Compact Input with Send-on-Enter

    Wire a keydown handler so Enter sends and Shift+Enter inserts a newline, then read the message with getHTML().

    import { Scribe } from 'scribejs-editor';
    import 'scribejs-editor/dist/scribe.css';
    
    // A compact chat input: a floating toolbar keeps the surface minimal
    const editor = Scribe.init('#chat-input', {
      toolbar:     'floating',
      autofocus:   true,
      placeholder: 'Type a message...',
    });
    
    // Send on Enter, newline on Shift+Enter
    const inputEl = document.querySelector('#chat-input');
    inputEl.addEventListener('keydown', (e) => {
      if (e.key !== 'Enter' || e.shiftKey) return; // Shift+Enter -> newline
      e.preventDefault();
      send();
    });
    
    function send() {
      if (editor.isEmpty()) return;
      const html = editor.getHTML();   // sanitized HTML for the message payload
      socket.emit('message', { html });
      editor.setHTML('');              // clear the input after sending
    }
    
    document.querySelector('#send-btn').addEventListener('click', send);
    
    // Inline formatting from compact toolbar buttons
    document.querySelector('#bold').addEventListener('click',   () => editor.bold());
    document.querySelector('#italic').addEventListener('click', () => editor.italic());

    XSS-Safe Rendering of Received Messages

    Incoming message HTML is untrusted. Run it through Scribe's sanitization before inserting it into the DOM.

    import { Scribe } from 'scribejs-editor';
    
    // Incoming messages arrive as HTML from other users — never trust it raw.
    // Run it back through Scribe's sanitizer before rendering into the DOM.
    function renderIncoming(rawHtml, containerEl) {
      // Scribe sanitizes HTML it sets; using a throwaway instance gives you
      // the same XSS-safe cleaning pipeline for received content.
      const sanitizer = Scribe.init(document.createElement('div'));
      sanitizer.setHTML(rawHtml);          // strips <script>, on* handlers, etc.
      const safeHtml = sanitizer.getHTML(); // clean, safe markup
      sanitizer.destroy();
    
      const bubble = document.createElement('div');
      bubble.className = 'message-bubble';
      bubble.innerHTML = safeHtml;          // safe: already sanitized by Scribe
      containerEl.appendChild(bubble);
    }
    
    socket.on('message', (msg) => {
      renderIncoming(msg.html, document.querySelector('#messages'));
    });
    
    // TODO(verify): @mentions are NOT a confirmed built-in Scribe feature.
    // If you want @mention autocomplete, build it as a custom Scribe plugin
    // that listens for keydown, shows a picker, and inserts a mention node.
    // Do not assume mentions ship by default.

    React Chat Input Component

    A complete React component with send-on-Enter, Shift+Enter newline, and input clearing.

    import { useRef, useEffect, useCallback } from 'react';
    import { Scribe } from 'scribejs-editor';
    
    export function ChatInput({ onSend }) {
      const containerRef = useRef(null);
      const editorRef    = useRef(null);
    
      useEffect(() => {
        const el = containerRef.current;
        editorRef.current = Scribe.init(el, {
          toolbar:     'floating',
          autofocus:   true,
          placeholder: 'Type a message...',
        });
    
        // Send on Enter; Shift+Enter inserts a newline
        const onKeyDown = (e) => {
          if (e.key !== 'Enter' || e.shiftKey) return;
          e.preventDefault();
          submit();
        };
        el.addEventListener('keydown', onKeyDown);
    
        return () => {
          el.removeEventListener('keydown', onKeyDown);
          editorRef.current?.destroy();
        };
      }, []);
    
      const submit = useCallback(() => {
        const editor = editorRef.current;
        if (!editor || editor.isEmpty()) return;
        onSend(editor.getHTML());  // sanitized HTML message
        editor.setHTML('');
      }, [onSend]);
    
      return (
        <div className="chat-input-row">
          <div ref={containerRef} className="chat-input" />
          <button onClick={submit}>Send</button>
        </div>
      );
    }

    Everything a messaging input expects

    Compact floating toolbar
    Inline bold, italic, links
    Send on Enter via keydown
    Shift+Enter inserts a newline
    getHTML() for the message payload
    setHTML("") to clear after send
    isEmpty() to skip blank messages
    XSS-safe rendering of received HTML
    Built-in paste sanitization
    Plugin architecture for custom features
    TODO(verify): @mentions as a custom plugin
    Zero dependencies, under 50KB

    Ship your chat input today.

    Compact input, send-on-Enter, XSS-safe rendering. Everything in 50KB — free under MIT.

    Chat & messaging input — common questions

    How do I build a rich text chat input in JavaScript?

    Initialize Scribe Editor with toolbar: 'floating' for a compact input, add a keydown handler so Enter sends and Shift+Enter inserts a newline, and read the message with editor.getHTML(). Clear the input after sending with editor.setHTML(''). Scribe is under 50KB gzipped with zero dependencies and works with React, Vue 3, Svelte, and Vanilla JS.

    How do I wire send-on-Enter with Shift+Enter for a newline?

    Add a keydown listener on the editor element. If e.key is 'Enter' and e.shiftKey is false, call e.preventDefault() and send the message; otherwise let the default behavior insert a newline. On send, grab editor.getHTML() and then call editor.setHTML('') to clear the input.

    How do I render received chat messages safely?

    Treat incoming message HTML as untrusted. Scribe sanitizes the HTML it processes, so you can run received content through its sanitization (for example by setting it on an instance and reading getHTML()) to strip script tags and inline event handlers before inserting the message into the DOM. This gives XSS-safe rendering of user messages.

    Does Scribe Editor support @mentions out of the box?

    TODO(verify): @mentions are not a confirmed built-in Scribe feature. Scribe does provide a plugin architecture, so the recommended approach is to build mention autocomplete as a custom plugin — listen for the trigger character on keydown, show a picker, and insert a mention node. Do not assume mentions ship by default.