Skip to content

Instantly share code, notes, and snippets.

@dferber90
Last active October 17, 2025 11:35
Show Gist options
  • Save dferber90/a7e636e6dfa0178c016ed488a6557273 to your computer and use it in GitHub Desktop.
Save dferber90/a7e636e6dfa0178c016ed488a6557273 to your computer and use it in GitHub Desktop.

Revisions

  1. dferber90 revised this gist Dec 24, 2020. 1 changed file with 3 additions and 0 deletions.
    3 changes: 3 additions & 0 deletions usage-example.tsx
    Original file line number Diff line number Diff line change
    @@ -1,3 +1,6 @@
    // Usage example of useCaretPosition
    // Shows a red caret when the cursor is at the end

    import { useCaretPosition } from "./use-caret-position"

    export function SomeComponent () {
  2. dferber90 created this gist Dec 24, 2020.
    25 changes: 25 additions & 0 deletions usage-example.tsx
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,25 @@
    import { useCaretPosition } from "./use-caret-position"

    export function SomeComponent () {
    const [value, setValue] = React.useState<string>("");

    // actual usage
    const inputRef = React.useRef<HTMLInputElement>(null);
    const { selection, handleSelect } = useCaretPosition(inputRef);

    // make caret red when it's at the end
    const isCaretAtLastPosition =
    selection.start === selection.end && selection.start === props.value.length;

    return (
    <input
    ref={inputRef}
    type="text"
    value={props.value}
    onSelect={handleSelect}
    style={{
    caretColor: isCaretAtLastPosition ? "red" : "inherit",
    }}
    />
    )
    }
    36 changes: 36 additions & 0 deletions use-caret-position.tsx
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,36 @@
    /**
    * A React which returns the current caret position inside an input
    */
    export function useCaretPosition<T extends HTMLInputElement>(
    inputRef: React.RefObject<T>
    ) {
    const [selection, setSelection] = React.useState<{
    start: HTMLInputElement["selectionStart"];
    end: HTMLInputElement["selectionEnd"];
    direction: HTMLInputElement["selectionDirection"];
    }>({
    start: 0,
    end: 0,
    direction: "none",
    });

    React.useEffect(() => {
    if (!inputRef.current) return;
    setSelection({
    start: inputRef.current.selectionStart || 0,
    end: inputRef.current.selectionEnd || 0,
    direction: inputRef.current.selectionDirection || "none",
    });
    }, []);

    return {
    selection,
    handleSelect: (event: React.SyntheticEvent<T, Event>) => {
    setSelection({
    start: (event.target as T).selectionStart || 0,
    end: (event.target as T).selectionEnd || 0,
    direction: (event.target as T).selectionDirection || "none",
    });
    },
    };
    }