react-native-controlled-mentions

repository·main·Indexed 43 days ago

https://github.com/dabakovich/react-native-controlled-mentions

A fully controlled React Native mentions component that adds mention, hashtag, and custom pattern highlighting to TextInput without requiring native modules. Version 3.1.0 features a hook-based API via useMentions for managing text input state, custom trigger configurations, and a flexible system for rendering suggestion lists and parsed mention values.

Tokens
7.1K
Snippets
19
Records
28
Agent score
54%

What's inside react-native-controlled-mentions

  1. Requirement: Pattern regex must include a single capturing group

    main

    When defining patterns in patternsConfig, your regular expression must contain exactly one capturing group. If you provide a regex without a capturing group, it will fail in v3.

    Incorrect (v2 style): pattern: /https?://gi

    Correct (v3 style): pattern: /(https?://)/gi

    // v3 requirement
    const patternsConfig: PatternsConfig = {
      url: {
        pattern: /(https?:\/\/[^\s]+)/gi,
        textStyle: { color: 'blue' },
      },
    };
  2. Get started with the useMentions hook

    main

    To enable mentions in a controlled TextInput, use the useMentions hook. You must pass the current value and an onChange callback to the hook, then spread the returned textInputProps onto your TextInput component.

    import { TextInput } from 'react-native';
    import { useMentions } from 'react-native-controlled-mentions';
    
    const Mentions = () => {
      const [textValue, setTextValue] = useState('');
    
      const { textInputProps } = useMentions({
        value: textValue,
        onChange: setTextValue,
      });
    
      return <TextInput {...textInputProps} />;
    };
  3. Update Suggestions implementation with SuggestionsProvidedProps

    main

    The way suggestion components receive props has changed. Use the SuggestionsProvidedProps interface. The callback for selecting a suggestion is now onSelect (previously onSuggestionPress).

    To render suggestions, pass the specific trigger's properties from the triggers object (returned by useMentions) to your component.

    import { FC } from 'react';
    import { SuggestionsProvidedProps } from 'react-native-controlled-mentions';
    
    const Suggestions: FC<SuggestionsProvidedProps & { suggestions: any[] }> = ({
      keyword,
      onSelect,
      suggestions,
    }) => {
      if (keyword == null) return null;
    
      return (
        <View>
          {suggestions
            .filter((one) => one.name.toLocaleLowerCase().includes(keyword.toLocaleLowerCase()))
            .map((one) => (
              <Pressable key={one.id} onPress={() => onSelect(one)}>
                <Text>{one.name}</Text>
              </Pressable>
            ))}
        </View>
      );
    };
    
    // Usage inside App component:
    // <Suggestions {...triggers.mention} suggestions={users} />
  4. Implement a Suggestions component

    main

    Create a custom component to render the list of suggestions. This component should accept SuggestionsProvidedProps as props. It receives a keyword (the text typed after the trigger) and an onSelect callback to handle the user's choice.

    import { Pressable, View, Text } from 'react-native';
    import { SuggestionsProvidedProps } from 'react-native-controlled-mentions';
    
    const suggestions = [
      { id: '1', name: 'David' },
      { id: '2', name: 'Mary' },
    ];
    
    const Suggestions: FC<SuggestionsProvidedProps> = ({ keyword, onSelect }) => {
      if (keyword == null) {
        return null;
      }
    
      return (
        <View>
          {suggestions
            .filter((one) => one.name.toLocaleLowerCase().includes(keyword.toLocaleLowerCase()))
            .map((one) => (
              <Pressable key={one.id} onPress={() => onSelect(one)} style={{ padding: 12 }}>
                <Text>{one.name}</Text>
              </Pressable>
            ))}
        </View>
      );
    };
  5. Configure triggers with triggersConfig

    main

    Use triggersConfig to define the symbols (like @ or #) that trigger mentions and how they should be styled within the TextInput.

    Important: Define the triggersConfig object outside of your functional component or wrap it in useMemo to prevent unnecessary re-renders.

    import { TextInput } from 'react-native';
    import { useMentions, TriggersConfig } from 'react-native-controlled-mentions';
    
    // Create config as static object out of function component
    // Or memoize it inside FC using `useMemo`
    const triggersConfig: TriggersConfig<'mention'> = {
      mention: {
        // Symbol that will trigger keyword change
        trigger: '@',
    
        // Style which mention will be highlighted in the `TextInput`
        textStyle: { fontWeight: 'bold', color: 'blue' },
      },
    };
    
    const Mentions = () => {
      const [textValue, setTextValue] = useState('');
    
      const { textInputProps } = useMentions({
        value: textValue,
        onChange: setTextValue,
    
        // Add the config here
        triggersConfig,
      });
    
      return <TextInput {...textInputProps} />;
    };
  6. Migrate from v2 to v3

    main

    Version 3 introduces a major architectural shift from a component-based API (MentionInput) to a hook-based API (useMentions). Key changes include:

    • API Pattern: Use the useMentions hook to get textInputProps and triggers, then spread them onto a standard React Native TextInput.
    • Configuration: The partTypes array is replaced by two separate objects: triggersConfig (for character-based triggers like @ or #) and patternsConfig (for regex-based patterns like URLs).
    • Mention Format: The internal string format for mentions has changed from @[Name](id) to {@}[Name](id). You must update existing initial values to include the {@} prefix.
    • Regex Requirements: All regex patterns in patternsConfig must now include a single capturing group (e.g., /(pattern)/gi).
    • Suggestions: The suggestions callback has changed from onSuggestionPress to onSelect, and the props interface is now SuggestionsProvidedProps.
    ### Summary of Key API Changes
    
    | Feature | v2 | v3 |
    | :--- | :--- | :--- |
    | **Main Component** | `<MentionInput />` | `useMentions()` hook + `<TextInput />` |
    | **Configuration** | `partTypes` array | `triggersConfig` + `patternsConfig` objects |
    | **Mention Format** | `@[Name](id)` | `{@}[Name](id)` |
    | **Suggestions Props** | `MentionSuggestionsProps` | `SuggestionsProvidedProps` |
    | **Suggestion Callback** | `onSuggestionPress` | `onSelect` |
    | **Pattern Regex** | Optional capturing group | Required capturing group |
  7. Replace MentionInput with useMentions hook

    main

    In v3, instead of using the MentionInput component, you use the useMentions hook. The hook returns textInputProps (which should be spread onto a React Native TextInput) and a triggers object used to access configuration for your suggestion components.

    Note: Ensure you import TextInput from react-native.

    import { useMentions } from 'react-native-controlled-mentions';
    import { TextInput } from 'react-native';
    
    const App = () => {
      const [value, setValue] = useState('Hello {@}[Mary](2)!');
    
      const { textInputProps, triggers } = useMentions({
        value,
        onChange: setValue,
        triggersConfig,
        patternsConfig,
      });
    
      return (
        <TextInput
          {...textInputProps}
          placeholder="Type here..."
        />
      );
    };
  8. Convert partTypes to triggersConfig and patternsConfig

    main

    In v3, configuration is split into two distinct objects to improve type safety and organization.

    1. triggersConfig: Used for character-based triggers (e.g., @ or #). Use the TriggersConfig<T> type where T is a union of your trigger names.
    2. patternsConfig: Used for regex-based patterns (e.g., URLs). Use the PatternsConfig type.

    Performance Tip: Define these configurations outside your component or wrap them in useMemo to prevent unnecessary re-renders.

    // Triggers (e.g., @ and #)
    const triggersConfig: TriggersConfig<'mention' | 'hashtag'> = {
      mention: {
        trigger: '@',
        textStyle: { fontWeight: 'bold', color: 'blue' },
      },
      hashtag: {
        trigger: '#',
        allowedSpacesCount: 0,
        textStyle: { fontWeight: 'bold', color: 'grey' },
      },
    };
    
    // Patterns (e.g., URLs)
    const patternsConfig: PatternsConfig = {
      url: {
        pattern: /(https?:\/\/[^\s]+)/gi, // Must include capturing group
        textStyle: { color: 'blue' },
      },
    };
  9. Display suggestions using the triggers object

    main

    The useMentions hook returns a triggers object. You can access specific trigger data (e.g., triggers.mention) and pass it directly to your Suggestions component as props to provide the current keyword and the onSelect handler.

    import { TextInput } from 'react-native';
    import { useMentions, TriggersConfig } from 'react-native-controlled-mentions';
    import { Suggestions } from './suggestions';
    
    const triggersConfig: TriggersConfig<'mention'> = {
      mention: {
        trigger: '@',
        textStyle: { fontWeight: 'bold', color: 'blue' },
      },
    };
    
    const Mentions = () => {
      const [textValue, setTextValue] = useState('');
    
      const { textInputProps, triggers } = useMentions({
        value: textValue,
        onChange: setTextValue,
        triggersConfig,
      });
    
      return (
        <>
          <Suggestions {...triggers.mention} />
          <TextInput {...textInputProps} />
        </>
      );
    };
  10. Understand the TriggerData structure

    main

    When a mention is identified or styled, the library provides TriggerData containing the metadata for that specific mention instance:

    • id: The unique identifier for the mention.
    • name: The display name.
    • trigger: The trigger characters used (e.g., @).
    • original: The original text segment in the input.
    • type: (Implicitly part of the context) The name of the trigger.
  11. Render mention values in custom components

    main

    To render a mention-formatted string outside of a TextInput (e.g., in a message bubble), use parseValue to break the string into parts and map over them with a custom renderer.

    import { Part, Config, parseValue, isTriggerConfig } from 'react-native-controlled-mentions';
    
    const renderPart = (part: Part, index: number) => {
      if (!part.config) {
        return <Text key={index}>{part.text}</Text>;
      }
    
      if (isTriggerConfig(part.config)) {
        return (
          <Text
            key={`${index}-${part.config.trigger}`}
            style={part.config.textStyle}
          >
            {part.text}
          </Text>
        );
      }
    
      return (
        <Text key={`${index}-pattern`} style={part.config.textStyle}>
          {part.text}
        </Text>
      );
    };
    
    const ValueRenderer = ({ value, configs }: { value: string, configs: Config[] }) => {
      const { parts } = parseValue(value, configs);
      return <Text>{parts.map(renderPart)}</Text>;
    };
    import { Part, Config, parseValue, isTriggerConfig } from 'react-native-controlled-mentions';
    
    const renderPart = (part: Part, index: number) => {
      if (!part.config) {
        return <Text key={index}>{part.text}</Text>;
      }
    
      if (isTriggerConfig(part.config)) {
        return (
          <Text
            key={`${index}-${part.config.trigger}`}
            style={part.config.textStyle}
            onPress={() => console.log('Pressed', part.data)}
          >
            {part.text}
          </Text>
        );
      }
    
      return (
        <Text key={`${index}-pattern`} style={part.config.textStyle}>
          {part.text}
        </Text>
      );
    };
    
    const renderValue: FC = (value: string, configs: Config[]) => {
      const { parts } = parseValue(value, configs);
      return <Text>{parts.map(renderPart)}</Text>;
    };