use-chat-submit
v1.0.8
Published
React hook to handle chat textarea submit behaviors (Enter vs Mod+Enter) with IME-safe handling.
Readme
A small React Hook that brings the chat input behavior you’d expect:
Enterto submit.Shift+Enterto insert a line break.Cmd/Ctrl+Enterto submit.Enterto insert a line break (preferred for Japanese input 🇯🇵).
It prevents accidental submissions while using an IME, works seamlessly with your own handlers, and normalizes Cmd vs. Ctrl differences across platforms.
Features
- Switch between submit/line-break keys based on user preference
- Works smoothly with IME languages (e.g., Japanese, Chinese)
- Zero dependencies
- Supports React 19+
- Just 1.5 KB gzipped 🚀
Installation
pnpm add use-chat-submit
# or
npm i use-chat-submit
# or
yarn add use-chat-submitQuick Start
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),
})}
/>
);
}getTextareaProps() only attaches onKeyDown and ref, so you can freely pass any other props yourself.
// Equivalent behavior
<textarea
{...getTextareaProps()}
value={text}
onChange={(e) => setText(e.target.value)}
/>Key Behavior (Modes)
| options.mode | Enter | Shift+Enter | Cmd/Ctrl+Enter |
| -------------- | ------- | ----------- | -------------- |
| mod-enter | Line break | Line break | Submit |
| enter | Submit | Line break | Submit |
- Never submits while an IME is composing (two-step check with
KeyboardEvent.isComposingon keydown/keyup). <textarea>does not submit forms implicitly on Enter, so you don’t need to block default form behavior.- For languages that rely heavily on IME (like Chinese or Japanese), using
mod-enteris recommended.
Why It Helps
- Safely composes with your handlers (user → library). Respects
event.defaultPreventedandevent.isPropagationStopped(). - Smooths out differences between Safari and Chrome in IME composition handling. Detects reliably using native keydown/keyup events.
- Normalizes Cmd vs. Ctrl with
modKey: "auto". Also exposes platform-aware shortcut hints for your UI.
API
useChatSubmit
Options (UseChatSubmitOptions)
| Option | Type | Default | Description |
| --- | --- | --- | --- |
| 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" | Key mapping behavior for Enter/Shift+Enter/Cmd/Ctrl+Enter. |
| modKey | "meta" | "ctrl" | "auto" | "auto" (recommended) | Which modifier counts as “mod”. Auto = Cmd (⌘) on macOS, Ctrl elsewhere. |
| allowEmptySubmit | boolean | false | Allow submitting an empty string. |
| stopPropagation | boolean | false | Call e.stopPropagation() when submitting. |
| enabled | boolean | "non-mobile" | true | Enable the behavior. "non-mobile" enables only on non‑mobile devices. |
| shortcutHintLabelStyle | "auto" | "symbols" | "text" | "auto" | Style for shortcut hint labels. |
| userAgentHint | string | — | Optional UA string for SSR to reduce detection lag. |
Return Value (UseChatSubmitReturn)
| Property | Type | Description |
| --- | --- | --- |
| getTextareaProps(userProps?) | (userProps?: React.TextareaHTMLAttributes<HTMLTextAreaElement> & { ref?: React.Ref<HTMLTextAreaElement> }) => React.TextareaHTMLAttributes<HTMLTextAreaElement> | Safely composes props for <textarea>. You may pass a ref. |
| textareaRef | React.RefObject<HTMLTextAreaElement> | Read-only ref to the textarea element. |
| triggerSubmit() | () => void | Programmatic submit trigger. |
| shortcutHintLabels | { submit: string; lineBreak: string } \| undefined | UI-ready labels. undefined until the platform is detected. See the demo. |
| isEnabled | boolean | Whether the hook is currently enabled. |
Implementation Notes
- Handler composition is “user → library”. If the user calls
preventDefault(), internal logic does not run. - Leave line breaks to the browser’s default behavior (no manual
"\n"insertion). - In
entermode,preventDefault()repurposes Enter for submit (Shift+Enter still inserts a line break). - Does not submit when
disabled,readOnly, or when the value is empty withallowEmptySubmit=falseandvalue.trim()==="".
Limitations & Support
- Only
<textarea>is supported (<input>doesn’t allow multi-line input) - React 19+ (peer dependency)
License
MIT
