use-chat-submit

repository·main·Indexed 42 days ago

https://github.com/catnose99/use-chat-submit

A lightweight React hook for handling chat textarea submission behaviors, such as Enter to submit and Shift+Enter for line breaks. It features IME-safe handling for languages like Japanese and Chinese to prevent accidental submissions during character composition. Requires React 19+ and supports configurable modes like 'enter' and 'mod-enter' for flexible keyboard mapping across different platforms.

Tokens
3.4K
Snippets
10
Records
15
Agent score
54%

What's inside use-chat-submit

  1. How the Enter key modes work

    main

    The hook provides two modes to handle how the Enter key behaves, which is useful for balancing ease of use with IME (Input Method Editor) stability (e.g., for Japanese or Chinese users).

    • mod-enter: Recommended for IME-heavy languages. Enter and Shift+Enter both insert a line break. Cmd/Ctrl+Enter triggers the submission.
    • enter: Standard chat behavior. Enter triggers submission. Shift+Enter inserts a line break. Cmd/Ctrl+Enter also triggers submission.

    Both modes include a two-step check using KeyboardEvent.isComposing to ensure the hook never submits while a user is composing characters via an IME.

    | `options.mode` | Enter   | Shift+Enter | Cmd/Ctrl+Enter |
    | -------------- | ------- | ----------- | -------------- |
    | `mod-enter`    | Line break | Line break  | Submit         |
    | `enter`        | Submit  | Line break  | Submit         |
  2. Quick Start with useChatSubmit

    main

    To implement chat input behavior, import useChatSubmit and pass an onSubmit handler. Use getTextareaProps to spread properties onto a <textarea>. This function attaches onKeyDown and ref while allowing you to pass your own value and onChange handlers.

    Note: getTextareaProps() only attaches onKeyDown and ref, so you can freely pass any other props yourself.

    import * as React from "react";
    import { useChatSubmit } from "use-chat-submit";
    
    export function ChatBox() {
      const [text, setText] = React.useState("");
    
      const { getTextareaProps } =
        useChatSubmit({
          onSubmit: (value) => {
            console.log("submit:", value);
            setText(""); // clear input after submit
          },
          mode: "mod-enter", // switch behavior based on user preference
        });
    
      return (
        <textarea
          {...getTextareaProps({
            value: text,
            onChange: (e) => setText(e.target.value),
          })}
        />
      );
    }
  3. How useChatSubmit handles keyboard events

    main

    The hook uses a layered event handling approach via getTextareaProps:

    1. User Handlers First: If you provide an onKeyDown prop to getTextareaProps, it is executed before the library's internal logic.
    2. Interruption Support: If your custom handler calls event.preventDefault() or event.stopPropagation(), the library's internal submission logic will be skipped.
    3. IME Safety: The hook monitors compositionstart, compositionend, and compositioncancel events. It prevents submission while the user is composing characters (e.g., using Japanese or Chinese IMEs) to avoid submitting partial or incorrect text.
  4. Configure useChatSubmit options

    main

    The useChatSubmit hook accepts a UseChatSubmitOptions object to customize behavior:

    OptionTypeDefaultDescription
    onSubmit(value, ctx)(value: string, ctx: { target: HTMLTextAreaElement }) => void— (required)Called on submit. Access the underlying textarea via ctx.target.
    mode"mod-enter""enter""mod-enter"
    modKey"meta""ctrl""auto"
    allowEmptySubmitbooleanfalseAllow submitting an empty string.
    stopPropagationbooleanfalseCall e.stopPropagation() when submitting.
    enabledboolean"non-mobile"true
    shortcutHintLabelStyle"auto""symbols""text"
    userAgentHintstring—Optional UA string for SSR to reduce detection lag.
  5. Use the UseChatSubmitReturn properties

    main

    The useChatSubmit hook returns an object containing the following properties:

    PropertyTypeDescription
    getTextareaProps(userProps?)(userProps?: React.TextareaHTMLAttributes<HTMLTextAreaElement> & { ref?: React.Ref<HTMLTextAreaElement> }) => React.TextareaHTMLAttributes<HTMLTextAreaElement>Safely composes props for <textarea>. You may pass a ref.
    textareaRefReact.RefObject<HTMLTextAreaElement>Read-only ref to the textarea element.
    triggerSubmit()() => voidProgrammatic submit trigger.
    shortcutHintLabels{ submit: string; lineBreak: string } | undefinedUI-ready labels. undefined until the platform is detected.
    isEnabledbooleanWhether the hook is currently enabled.
  6. Limitations and Requirements

    main

    When using use-chat-submit, keep the following constraints in mind:

    • Element Support: Only <textarea> is supported. <input> elements are not supported because they do not allow multi-line input.
    • React Version: Requires React 19+ as a peer dependency.
    • Submission Logic: The hook does not submit if the textarea is disabled or readOnly. If allowEmptySubmit is set to false (the default), it will not submit if the trimmed value is empty.
  7. Detect user agent summary with getUserAgentSummary

    main

    The getUserAgentSummary function provides information about the user's device, specifically whether it is an Apple device and whether it is a mobile device. It first attempts to use the modern navigator.userAgentData API and falls back to parsing the navigator.userAgent string if necessary.

    Returns undefined if called in a non-browser environment (where window is undefined).

    Return Type: UserAgentSummary object:

    • isAppleDevice: boolean (true if platform is macOS or iOS)
    • isMobile: boolean (true if mobile device)
    const summary = getUserAgentSummary();
    if (summary) {
      console.log(summary.isAppleDevice, summary.isMobile);
    }
  8. Format a list of keyboard keys with beautifyKeyTextList

    main

    The beautifyKeyTextList function is a convenience wrapper that applies beautifyKeyText to an array of keys. This is useful for displaying keyboard shortcut combinations (e.g., ⌘ + K).

    Parameters:

    • keys: An array of BeautifyKeyTextInputKey ('meta' | 'ctrl' | 'shift' | 'enter').
    • isApple: Boolean indicating if the user is on an Apple device.
    • hintLabelStyle: The desired output style ('auto' | 'symbols' | 'text').

    Returns: An array of formatted strings.

    import { beautifyKeyTextList } from './utils';
    
    const formattedKeys = beautifyKeyTextList({
      keys: ['meta', 'k'],
      isApple: true,
      hintLabelStyle: 'symbols'
    });
    // Note: 'k' is passed through as is because it's not a BeautifyKeyTextInputKey,
    // but the function maps over the provided array.
    // Result: ["⌘", "k"]
  9. Access the UseChatSubmitReturn interface

    main

    The useChatSubmit hook returns an object with the following properties:

    • getTextareaProps: A function that accepts React.TextareaHTMLAttributes and returns composed props including a merged ref and onKeyDown handler.
    • textareaRef: A read-only React.RefObject pointing to the HTMLTextAreaElement.
    • triggerSubmit: A function to manually trigger the onSubmit callback (respecting allowEmptySubmit and element state).
    • shortcutHintLabels: An object containing submit and lineBreak metadata (keys and uniqueness) used for rendering UI hints. Returns undefined if keys cannot be resolved.
    • isEnabled: A boolean indicating if the submission logic is currently active.
    • isComposingRef: A read-only React.RefObject<boolean> indicating if an IME composition is currently in progress.
  10. Use the useChatSubmit hook

    main

    The useChatSubmit hook manages chat input submission logic for a textarea. It handles keyboard shortcuts (like Enter or Cmd/Ctrl+Enter), prevents accidental submissions during IME composition (important for multilingual input), and provides UI hint labels for shortcuts.

    To use it, pass an onSubmit callback in the options and spread the returned getTextareaProps() onto your textarea element.

    import { useChatSubmit } from 'use-chat-submit';
    
    const MyChatInput = () => {
      const {
        getTextareaProps,
        shortcutHintLabels
      } = useChatSubmit({
        onSubmit: (value, ctx) => {
          console.log('Submitting:', value);
        },
        mode: 'mod-enter', // Submit on Cmd/Ctrl + Enter
      });
    
      return (
        <div>
          <textarea {...getTextareaProps()} />
          {shortcutHintLabels && (
            <div>
              Submit: {shortcutHintLabels.submit.keys.join(' + ')}
            </div>
          )}
        </div>
      );
    };
  11. Format keyboard shortcut keys with beautifyKeyText

    main

    The beautifyKeyText function converts raw keyboard key names into human-readable or symbol-based labels, optimized for the user's platform (Apple vs. non-Apple) and a preferred display style.

    Parameters:

    • key: The key to format ('meta', 'ctrl', 'shift', or 'enter').
    • isApple: A boolean indicating if the user is on an Apple device (used to decide between symbols like ⌘ vs Win).
    • hintLabelStyle: Determines the output format:
      • 'auto': Default behavior.
      • 'symbols': Uses symbols (e.g., ⌘, ⇧, ⏎) on Apple devices.
      • 'text': Returns the raw key name (e.g., 'meta', 'ctrl').

    Mapping Logic:

    KeyStyle: symbols (Apple)Style: symbols (Non-Apple)Style: text
    meta⌘Winmeta
    ctrlcontrolCtrlctrl
    shift⇧Shiftshift
    enter⏎Enterenter
    import { beautifyKeyText } from './utils';
    
    const label = beautifyKeyText({
      key: 'meta',
      isApple: true,
      hintLabelStyle: 'symbols'
    }); // Returns "⌘"