Live Demo: https://seawind543.github.io/react-token-input/
React TokenInput (react-customize-token-input)
A react token (tag) controlled input component, which support:
- Accept customize data structure.
 - Customize token (tag) Look & Feel on the 
labelDemo,delete buttonDemo, or even overridethe whole Token componentDemo. - Customize separate characters to separate the end-user input string. Demo
 - Inline editing on exist token.
 - Paste values. Demo
 - Preprocessing function to normalized user input value. It could be helpful to reproduce a single value into multiple values too. Demo
 - Validate function.
 
- Install the latest version of react and react-customize-token-input:
 
yarn add react react-customize-token-input
- At this point you can import 
react-customize-token-inputand its styles in your application by: 
import TokenInput from 'react-customize-token-input';
// Be sure to include styles at some point, probably during your bootstraping
import 'react-customize-token-input/dist/react-customize-token-input.css';
// Could find the not minimize version to easily customize style from:
// 'react-customize-token-input/dist/react-customize-token-input.original.css';- Run 
yarn installto install required packages. - Run 
yarn devto launchwebpack-dev-server. - After step 2, you will see following message output in command console.
 
「wds」: Project is running at http://0.0.0.0:8000/
「wds」: webpack output is served from /
「wds」: Content not from webpack is served from ../docs
Note: To stop the program, just type
ctrl + cin command console.
- After step 3 complete, you could access 
http://localhost:8000/to see result. 
See Live Examples: https://seawind543.github.io/react-token-input/
Note: Sources code of Examples in the folder examples/
/**
 * @template VT, ET
 * @typedef {Object} TokenInputProps
 */
interface TokenInputProps<VT = string, ET = string> {
  /**
   * @prop {CSSProperties} [style]
   * @description An optional prop, for assigning style to TokenInput
   */
  style?: CSSProperties;
  /**
   * @prop {string} [className]
   * @description An optional prop, for assigning class name to TokenInput
   */
  className?: string;
  /**
   * @prop {string} [placeholder]
   * @description An optional prop, for assigning placeholder to TokenInput
   */
  placeholder?: string;
  /**
   * @prop {boolean} [readOnly = false]
   * @description An optional prop, to control TokenInput is `readOnly mode`
   */
  readOnly?: boolean;
  /**
   * @prop {boolean} [disableCreateOnBlur]
   * @description An optional prop, to control TokenInput creates a new token when blurring on the creator
   */
  disableCreateOnBlur?: boolean;
  /**
   * @prop {boolean} [autoFocus = false]
   * @description
   * An optional prop, to control TokenInput is `autoFocus mode`.
   * Will be deprecated in the next major release. Took ref.current.focus() instead.
   */
  autoFocus?: boolean;
  /**
   * @template VT
   * @prop {VT[]} tokenValues
   * @description
   * The array of tokenValue of TokenInput.
   * This array will be used to render the tokens.
   *
   * Type: VT
   * Description:
   * Customize data structure data
   * Could be string | number | object | customized data structure...etc.
   */
  tokenValues: VT[];
  // TokenCreator props
  /**
   * @prop {TokenSeparator[]} [separators]
   * @description
   * An array of characters to split the user input string into array.
   * For example,
   * Split the user input string `abc;def` into `['abc', 'def']`
   * by separators `[';']`
   *
   * @see {@link TokenSeparator}
   * Note:
   * It take the `String.prototype.split(separators.join('|'))`
   * and `RegExp` to split the user input string.
   * 
   * @example
   * ```js
   * value.split(separators.join('|'));
   * ```
   *
   * Make sure your customized separators could be used with
   * (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp)[`RegExp`]}.
   */
  separators?: TokenSeparator[];
  /**
   * @prop {SpecialKeyDownConfig} [specialKeyDown=DEFAULT_SPECIAL_KEY_DOWN_CONFIG]
   * @description
   * [Beta; Might be change in the future version]
   * Current only apply to the `TokenCreator`
   *
   * The settings to control the behavior of specials keyDown's event handler.
   * Recommend to use the built-in constant `KEY_DOWN_HANDLER_CONFIG_OPTION` to config the setting.
   *
   * @see KEY_DOWN_HANDLER_CONFIG_OPTION for the accepted config values
   * @see DEFAULT_SPECIAL_KEY_DOWN_CONFIG for the default settings
   */
  specialKeyDown?: SpecialKeyDownConfig;
  /**
   * @prop {OnInputValueChange} [onInputValueChange]
   * @description
   * A callback function invoked when end-user typing but not become token yet
   *
   * @example
   * ```js
   * onInputValueChange(newValue, previousValue)
   * ```
   *
   * @param {InputString} newValue
   * The end-user's input string
   *
   * @param {InputString} previousValue
   * The previous input string
   *
   * @returns {void}
   */
  onInputValueChange?: OnInputValueChange;
  /**
   * @prop {OnPreprocess} [onPreprocess]
   * @description
   * A callback function to `preprocessing` the user input string.
   *
   * Note: This function execute after `split by TokenSeparator[]` but before `onBuildTokenValue`
   * inputString -> spilt(inputString) -> preprocess(spilt(inputString)) -> onBuildTokenValue(preprocess(spilt(inputString)))
   *
   * [Use case 1]
   *  Make your normalize process in this function, such as `String.prototype.trim()`.
   *
   * [Use case 2]
   * Sometimes, we will want to auto-fit the user input, this function could help with it.
   * For example, the user input string is `www.google.com`,
   * and we want to auto-fit it into `http://www.google.com` and `https://www.google.com`.
   *
   * @example
   * ```js
   * onPreprocess(inputStringValues)
   * ```
   *
   * @param {InputString[]} inputStringValues
   * The user input string values
   * (An array of string, which split from the original input string via the `separators`)
   *
   * @returns {InputString[]}
   * An array of string
   */
  onPreprocess?: OnPreprocess;
  /**
   * @template VT, ET
   * @prop {OnTokenValueValidate<VT, ET>} [onTokenValueValidate=defaultTokenValueValidate]
   * @description
   * A callback function to validate a tokenValue
   * (The returned result will be set into the TokenMeta & pass to `onGetTokenErrorMessage`)
   *
   * @example
   * ```js
   * onTokenValueValidate(tokenValue, index, tokenValues)
   * ```
   *
   * @param {VT} tokenValue
   * The tokenValue built by `onBuildTokenValue`
   *
   * @param {Index} index
   * The array index of this tokenValue in tokenValues
   *
   * @param {VT[]} tokenValues
   * The array of tokenValue of TokenInput
   *
   * @returns {TokenMeta<ET>['error']}
   * The customized error.
   * Specific the token's validate status or errorMessage.
   * Could be `an error message` to display, or an error object for further operations.
   *
   * @see TokenMeta for more information about TokenMeta<ET>['error']
   *
   * Note: Return `Nullish` types means the token is valid.
   * @see Nullish
   */
  onTokenValueValidate?: OnTokenValueValidate<VT, ET>;
  // Token related props
  /**
   * @template VT
   * @prop {OnTokenValuesChange<VT>} [onTokenValuesChange]
   * @description
   * A callback function invoked when tokenValues update
   *
   * @example
   * ```js
   * onTokenValuesChange(modifiedTokenValues)
   * ```
   *
   * @param {VT[]} modifiedTokenValues
   * The new tokenValues
   *
   * @returns {void}
   */
  onTokenValuesChange?: OnTokenValuesChange<VT>;
  /**
   * @template VT
   * @prop {OnBuildTokenValue<VT>} [onBuildTokenValue=defaultBuildTokenValue]
   * @description
   * A callback function to build `user input string value` into
   * the `tokenValue` (customized data structure).
   *
   * Note: You could make your normalize process in this function too.
   *
   * @example
   * ```js
   * onBuildTokenValue(inputString)
   * ```
   *
   * @param {InputString} inputString
   * The user input value // (A value split by TokenSeparator[])
   * Example:
   * - Input string "ABC, DEF" and separators is `,`
   * - The `onBuildTokenValue` will be called twice as
   * ```
   * onBuildTokenValue('ABC') and onBuildTokenValue('DEF')
   * ```
   *
   * @returns {VT}
   * The customized data structure data
   * Could be string | number | object | customized data structure...etc.
   */
  onBuildTokenValue?: OnBuildTokenValue<VT>;
  /**
   * @prop {Component} [customizeTokenComponent]
   * A customize react component to rendering a token
   * Apply this to customize all token function.
   *
   * @example
   * ```js
   * customizeTokenComponent={MyToken}
   * ```
   *
   * @returns {ReactElement | null}
   */
  customizeTokenComponent?: (
    props: TokenProps<VT, ET>
  ) => ReactElement | null;
  /**
   * @template VT, ET
   * @prop {OnGetTokenClassName<VT, ET>} [onGetTokenClassName]
   * @description
   * A callback function to getting customizes `className` to set on a `token`
   *
   * ```js
   * onGetTokenClassName(tokenValue, tokenMeta)
   * ```
   *
   * @param {VT} tokenValue
   * The tokenValue built by `onBuildTokenValue`
   *
   * @param {TokenMeta<ET>} tokenMeta
   * The token's meta data
   *
   * @returns {undefined | string}
   * The customizes className
   */
  onGetTokenClassName?: OnGetTokenClassName<VT, ET>;
  /**
   * @template VT, ET
   * @prop  {OnGetTokenDisplayLabel<VT, ET>} [onGetTokenDisplayLabel=defaultGetTokenEditableValue]
   * @description
   * A callback function to getting displayable `label` of a token
   * Apply this to customize the token's content
   * For example, render token with `icon` or `Additional text`
   *
   * @example
   * ```js
   * onGetTokenDisplayLabel(tokenValue, tokenMeta)
   * ```
   *
   * @param {VT} tokenValue
   * The tokenValue built by `onBuildTokenValue`
   *
   * @param {TokenMeta<ET>} tokenMeta
   * The token's meta data
   *
   * @returns {InputString | ReactNode}
   * The token's display content.
   */
  onGetTokenDisplayLabel?: OnGetTokenDisplayLabel<VT, ET>;
  /**
   * @prop {OnRenderTokenDeleteButtonContent} [onRenderTokenDeleteButtonContent]
   * @description
   * A callback function to render content of the delete button of token
   * Apply this to customize the token's content of the delete button.
   * For example, replace the built-in `x` by Google font material-icons
   *
   * @example
   * ```js
   * onRenderTokenDeleteButtonContent()
   * ```
   *
   * @returns {ReactNode}
   * The content of the delete button of the token.
   * By default, TokenInput render a built-in `x` icon
   */
  onRenderTokenDeleteButtonContent?: OnRenderTokenDeleteButtonContent;
  /**
   * @template VT, ET
   * @prop {OnGetIsTokenEditable<VT, ET>} [onGetIsTokenEditable=defaultGetIsTokenEditable]
   * @description
   * A callback function to determine whether the token is `inline editable`.
   *
   * @example
   * ```js
   * onGetIsTokenEditable(tokenValue, tokenMeta)
   * ```
   *
   * @param {VT} tokenValue
   * The tokenValue built by `onBuildTokenValue`
   *
   * @param {TokenMeta<ET>} tokenMeta
   * The token's meta data
   *
   * @returns {boolean}
   * - `true`: Editable.
   * - `false`: Not editable.
   */
  onGetIsTokenEditable?: OnGetIsTokenEditable<VT, ET>;
  /**
   * @template VT, ET
   * @prop {OnGetTokenEditableValue<VT, ET>} [onGetTokenEditableValue=defaultGetTokenEditableValue]
   * @description
   * A callback function to getting `string input value`
   * from `tokenValue` for the end-user to perform `inline edit`
   *
   * @example
   * ```js
   * onGetTokenEditableValue(tokenValue, tokenMeta)
   * ```
   *
   * @param {VT} tokenValue
   * The tokenValue built by `onBuildTokenValue`
   *
   * @param {TokenMeta<ET>} tokenMeta
   * The token's meta data
   *
   * @returns {InputString}
   * The value for end-user to `edit` in an input field
   */
  onGetTokenEditableValue?: OnGetTokenEditableValue<VT, ET>;
  /**
   * @template VT, ET
   * @prop {OnGetTokenErrorMessage<VT, ET>} [onGetTokenErrorMessage=defaultGetTokenErrorMessage]
   * @description
   * A callback function to getting the `Error Message` to
   * apply into the `title` attribute of the built-in Token Component
   *
   * @example
   * ```js
   * onGetTokenErrorMessage(tokenValue, tokenMeta)
   * ```
   *
   * @param {VT} tokenValue
   * The tokenValue built by `onBuildTokenValue`
   *
   * @param {TokenMeta<ET>} tokenMeta
   * The token's meta data
   *
   * @returns {string | Nullish}
   * The `Error Message` of the token.
   * Return `string type` will let the built-in Token component apply the message
   * into the `title` attribute. Otherwise, will simply be ignored
   */
  onGetTokenErrorMessage?: OnGetTokenErrorMessage<VT, ET>;
  /**
   * @prop {React.FocusEventHandler<HTMLInputElement>} [onCreatorFocus]
   * @description
   * A callback function invoked on TokenCreator focused
   *
   * @example
   * ```js
   * onCreatorFocus(e)
   * ```
   *
   * @param {React.FocusEvent<HTMLInputElement>} event
   * The FocusEvent of the input of TokenCreator
   *
   * @returns {void}
   */
  onCreatorFocus?: React.FocusEventHandler<HTMLInputElement>;
  /**
   * @prop {React.FocusEventHandler<HTMLInputElement>} [onCreatorBlur]
   * @description
   * A callback function invoked on TokenCreator blur
   *
   * @example
   * ```js
   * onCreatorBlur(e)
   * ```
   *
   * @param {React.FocusEvent<HTMLInputElement>} event
   * The FocusEvent of the input of TokenCreator
   *
   * @returns {void}
   */
  onCreatorBlur?: React.FocusEventHandler<HTMLInputElement>;
  /**
   * @prop {React.KeyboardEventHandler<HTMLInputElement>} [onCreatorKeyDown]
   * @description
   * A callback function invoked when keyDown on TokenCreator
   *
   * @example
   * ```js
   * onCreatorKeyDown(e)
   * ```
   *
   * @param {React.KeyboardEvent<HTMLInputElement>} event
   * The KeyboardEvent of the input of TokenCreator
   *
   * @returns {void}
   */
  onCreatorKeyDown?: React.KeyboardEventHandler<HTMLInputElement>;
}TokenInput provide the following method in the ref of it.
| Method | Description | Parameter | Return | 
|---|---|---|---|
| focus | Set focus on TokenInput. It will focus on the creator not the inline-editor | Same as HTMLElement.focus() | void | 
| setCreatorValue | Set value of TokenCreator | value: string | void | 
| getCreatorValue | Get value of TokenCreator | void | string | 
| createTokens | Trigger tokens create. If param.value undefined, then apply the value of TokenCreator directly. | value?: string | void | 
Could reference Demo, and its source code ExampleRefMethods in the folder examples/.
If you are using TypeScript, reference the code below for the typing of useRef.
import TokenInput, { type TokenInputRef } from 'react-customize-token-input';
const tokenInputRef = useRef<TokenInputRef>(null);
// ... omit
const handleFocusButtonClick = () => {
  tokenInputRef.current?.focus();
}
// ... omit
<TokenInput
  ref={tokenInputRef}
  tokenValues={values}
  onTokenValuesChange={setValues}
/>TokenInput has the following Predefined KeyDown event handlers.
| KeyDown | Description | Note | 
|---|---|---|
| Backspace | In case the current inputValue is an empty string, the latest token in the list tail will be deleted. | 
|
| Escape | Clear the input-box's value. | A.K.A. Reset. | 
| Enter | Create a token with the inputValue and continually focused on the inputBox for the next inputting. | |
| Tab | Same as onEnter. | 
  | 
| KeyDown | Description | Note | 
|---|---|---|
| Escape | End editing without change the value of the token. | A.K.A. Reset | 
| Enter | End editing and apply the new value. In case the new value is an empty string, will perform the onEscape. | 
    style = undefined,
    className = undefined,
    placeholder = undefined,
    readOnly = false,
    disableCreateOnBlur = undefined,
    autoFocus = false,
    // TokenCreator
    separators = DEFAULT_SEPARATORS,
    /*
    [
      ',',
      ';',
      '\n', // for copy and paste
      '\r', // for copy and paste
      '\r\n', // for copy and paste
    ];
    */
    specialKeyDown = DEFAULT_SPECIAL_KEY_DOWN_CONFIG,
    /*
    {
      onBackspace: KEY_DOWN_HANDLER_CONFIG_OPTION.ON,
      onTab: KEY_DOWN_HANDLER_CONFIG_OPTION.OFF,
      onEnter: KEY_DOWN_HANDLER_CONFIG_OPTION.ON,
      onEscape: KEY_DOWN_HANDLER_CONFIG_OPTION.ON,
    },
    */
    onInputValueChange = undefined,
    onPreprocess = undefined,
    onTokenValueValidate = defaultTokenValueValidate,
    onTokenValuesChange = undefined,
    // Token
    onBuildTokenValue = defaultBuildTokenValue,
    customizeTokenComponent = undefined,
    onGetTokenClassName = undefined,
    onGetTokenDisplayLabel = defaultGetTokenEditableValue,
    onRenderTokenDeleteButtonContent = undefined,
    onGetIsTokenEditable = defaultGetIsTokenEditable,
    onGetTokenEditableValue = defaultGetTokenEditableValue,
    onGetTokenErrorMessage = defaultGetTokenErrorMessage,Your CustomizeTokenComponent will receive these props from TokenInput. You could decide where & how to use them to customize your Token component.
Could also reference this Demo and its source code ExampleCustomizeToken in the folder examples/.
/**
 * @template VT, ET
 * @typedef {Object} TokenProps
 */
export interface TokenProps<VT = string, ET = string> {
  /**
   * @property {boolean} readOnly
   * @description
   * Same as TokenInputProps {@see 'TokenInputProps['readOnly']}
   */
  readOnly: boolean;
  /**
   * @type {VT}
   * @description This token's tokenValue
   */
  tokenValue: VT;
  /**
   * @template ET
   * @type {TokenMeta<ET>} tokenMeta
   * @description This token's meta data
   */
  tokenMeta: TokenMeta<ET>;
  /**
   * @template VT, ET
   * @prop {OnGetTokenClassName<VT, ET>} [onGetClassName]
   * @description
   * Same as TokenInputProps {@see TokenInputProps['onGetTokenClassName']}
   */
  onGetClassName?: OnGetTokenClassName<VT, ET>;
  /**
   * @template VT, ET
   * @prop  {OnGetTokenDisplayLabel<VT, ET>} [onGetTokenDisplayLabel=defaultGetTokenEditableValue]
   * @description
   * Same as TokenInputProps {@see TokenInputProps['onGetTokenDisplayLabel']}
   */
  onGetDisplayLabel: OnGetTokenDisplayLabel<VT, ET>;
  /**
   * @callback OnRenderTokenDeleteButtonContent
   * @description
   * Same as TokenInputProps {@see TokenInputProps['onRenderTokenDeleteButtonContent']}
   */
  onRenderDeleteButtonContent?: OnRenderTokenDeleteButtonContent;
  /**
   * @template VT, ET
   * @callback OnGetIsTokenEditable
   * @description
   * Same as TokenInputProps {@see TokenInputProps['onGetIsTokenEditable']}
   */
  onGetIsEditable: OnGetIsTokenEditable<VT, ET>;
  /**
   * @template VT, ET
   * @callback OnGetTokenEditableValue
   * @description
   * Same as TokenInputProps {@see TokenInputProps['onGetTokenEditableValue']}
   */
  onGetEditableValue: OnGetTokenEditableValue<VT, ET>;
  /**
   * @template VT
   * @callback OnBuildTokenValue
   * @description
   * Same as TokenInputProps {@see TokenInputProps['onBuildTokenValue']}
   */
  onBuildTokenValue: OnBuildTokenValue<VT>;
  /**
   * @template VT, ET
   * @callback OnGetTokenErrorMessage
   * @description
   * Same as TokenInputProps {@see TokenInputProps['onGetTokenErrorMessage']}
   */
  onGetErrorMessage: OnGetTokenErrorMessage<VT, ET>;
  /**
   * @callback
   * @description
   * A callback function, which you should `call`
   * when the end-user `start editing`
   *
   * Note:
   * Call this function to tell TokenInput it is start to editing the token.
   * As result, TokenInput will set `tokenMeta.activate` to `true`
   *
   * @example
   * ```js
   * onEditStart()
   * ```
   *
   * @returns {void}
   */
  onEditStart: () => void;
  /**
   * @callback
   * @description
   * A callback function, which you should `call`
   * when end-user `end the edit`
   *
   * Note:
   * Call this function to tell TokenInput to finish the `editing` of the token.
   * As result, TokenInput will set `tokenMeta.activate` to `false`.
   *
   * Also, TokenInput will based on the value of the parameter newTokenValue to
   * update the tokenValue of the token,
   * and call the callback `onTokenValuesChange`
   *
   * @example
   * ```js
   * onEditEnd(newTokenValue);
   * // or
   * onEditEnd();
   * ```
   *
   * @param {VT} [newTokenValue]
   * The new tokenValue built by `onBuildTokenValue.
   *
   * Note:
   * if `newTokenValue` is `undefined`,
   * TokenInput will treat as `Cancel` (Edit will end without update the tokenValue).
   * The callback `onTokenValuesChange` will also not be called.
   *
   * @returns {void}
   */
  onEditEnd: (newTokenValue?: VT) => void;
  /**
   * @callback
   * @description
   * A callback function, which you should `call`
   * when the end-user `delete` the token
   *
   * Note:
   * Call this function to tell TokenInput to delete the token.
   * As result, TokenInput will remove the token,
   * and call `onTokenValuesChange` to update tokenValues.
   *
   * @example
   * ```js
   * onDelete()
   * ```
   *
   * @returns {void}
   */
  onDelete: () => void;
}