Skip to content

Repository files navigation

text-input-cursor

An Expo module for retrieving the on-screen coordinates of the text cursor in TextInput components. This module enables precise positioning of UI elements such as autocomplete dropdowns, mention suggestions, and tooltips relative to the cursor position.

Installation

npm install text-input-cursor

or

yarn add text-input-cursor

Demo

Mention Demo

Performance Demo

Usage

Standard Mode

The TextInputWithCursor component provides automatic cursor tracking with real-time coordinate updates on every text change.

import { useState } from 'react';
import { View, StyleSheet } from 'react-native';
import { TextInputWithCursor, type CursorCoordinates } from 'text-input-cursor';

export default function Example() {
  const [text, setText] = useState('');
  const [cursorCoords, setCursorCoords] = useState<CursorCoordinates | null>(null);

  return (
    <View style={styles.container}>
      <View>
        <TextInputWithCursor
          value={text}
          onChangeText={setText}
          onCursorCoordinatesChange={({ coordinates }) => {
            setCursorCoords(coordinates);
          }}
          placeholder="Type @ to mention someone..."
          multiline
          style={styles.input}
        />
        {cursorCoords && (
          <View 
            style={[
              styles.dropdown,
              {
                top: cursorCoords.y,
                left: cursorCoords.x,
              }
            ]}
          >
            {/* Your dropdown content */}
          </View>
        )}
      </View>
    </View>
  );
}

Performance Mode

For performance-critical applications, use getCursorCoordinates to fetch coordinates only when needed. This approach eliminates unnecessary calculations during normal typing and only queries cursor position when specific triggers are detected.

import { useState, useRef } from 'react';
import { View, TextInput } from 'react-native';
import { getCursorCoordinates, type CursorCoordinates } from 'text-input-cursor';

export default function PerformanceExample() {
  const [text, setText] = useState('');
  const [cursorCoords, setCursorCoords] = useState<CursorCoordinates | null>(null);
  const inputRef = useRef<TextInput>(null);

  const handleTextChange = async (newText: string) => {
    setText(newText);
    
    // Code to detect @ symbol
    // Code to check if mention is still active (no space after @)
    
    if (/* mention detected */) {
      // Fetch coordinates only when needed
      const coords = await getCursorCoordinates(inputRef);
      setCursorCoords(coords);
    } else {
      setCursorCoords(null);
    }
  };

  return (
    <View style={styles.container}>
      <View>
        <TextInput
          ref={inputRef}
          value={text}
          onChangeText={handleTextChange}
          placeholder="Type @ to mention someone..."
          multiline
          style={styles.input}
        />
        {cursorCoords && (
          <View 
            style={[
              styles.dropdown,
              {
                top: cursorCoords.y,
                left: cursorCoords.x,
              }
            ]}
          >
            {/* Your dropdown content */}
          </View>
        )}
      </View>
    </View>
  );
}

Important: Coordinate Positioning

The returned coordinates (x, y) are relative to the TextInput component, not the screen. To position elements correctly:

  1. Wrap the TextInput and positioned elements in a common parent View
  2. Use position: 'absolute' on the positioned element
  3. Apply the coordinates directly to the element's top and left style properties

The parent View establishes the coordinate space for both the TextInput and the absolutely positioned element. Make sure the parent View doesn't have a padding.

API Reference

TextInputWithCursor

A component that extends React Native's TextInput with automatic cursor tracking.

Props

  • All standard TextInput props
  • onCursorCoordinatesChange?: (event: CursorCoordinatesChangeEvent) => void

CursorCoordinatesChangeEvent

{
  coordinates: CursorCoordinates | null;
  cursorPosition: number;
}

getCursorCoordinates

Function to retrieve cursor coordinates on demand.

function getCursorCoordinates(
  inputRef: RefObject<TextInput>
): Promise<CursorCoordinates | null>

CursorCoordinates

{
  x: number;          // Horizontal position (points)
  y: number;          // Vertical position (points)
  lineHeight: number; // Height of cursor line (points)
}

Returns null if the input reference is unavailable, platform is unsupported, or an error occurs.

Platform Support

Platform Support
iOS
Android
Web

Implementation Details

This module uses native platform APIs for accurate cursor positioning. Improvements and suggestions are welcome!

  • iOS: UITextView.caretRect(for:) and UITextField.caretRect(for:)
  • Android: Layout.getPrimaryHorizontal(), Layout.getLineTop(), and Layout.getLineBottom()
  • Web: Shadow element technique that creates an invisible mirror div with identical styles and text content, using getBoundingClientRect() on a span element to measure the exact cursor position

License

MIT

About

An expo module to get the text cursor's on screen coordinates. Helps to build suggestion tooltips etc.

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages