Input PIN

A specialized input for entering PIN codes or verification numbers.


Automatic Installation

This method is still working on progress, please use manual installation for now.

Manual Installation

Install dependencies

npm install radix-ui

Copy the code

components/input-pin/input-pin.tsx
import { OTPInput, OTPInputContext, OTPInputProps } from 'input-otp'
import * as React from 'react'
import { type InputPinStyles, inputPinStyles } from './input-pin.css'
 
type InputPinProps = OTPInputProps & InputPinStyles
type InputPinContextValue = Pick<InputPinProps, 'inputSize' | 'hasError'>
 
const InputPinContext = React.createContext<InputPinContextValue>({
  inputSize: 'md',
  hasError: false,
})
 
const InputPin = React.forwardRef<React.ComponentRef<typeof OTPInput>, InputPinProps>(
  (
    { className, inputSize = 'md', hasError = false, containerClassName, ...props },
    forwardedRef
  ) => {
    const styles = inputPinStyles({ inputSize, hasError })
    return (
      <InputPinContext.Provider value={{ inputSize, hasError }}>
        <OTPInput
          ref={forwardedRef}
          containerClassName={styles.container({ className: containerClassName })}
          className={styles.input({ className })}
          {...props}
        />
      </InputPinContext.Provider>
    )
  }
)
 
const InputPinGroup = React.forwardRef<
  React.ComponentRef<'div'>,
  React.ComponentPropsWithoutRef<'div'>
>(({ className, ...props }, forwardedRef) => {
  const styles = inputPinStyles()
  return <div ref={forwardedRef} className={styles.group({ className })} {...props} />
})
 
const InputPinSlot = React.forwardRef<
  React.ComponentRef<'div'>,
  React.ComponentPropsWithoutRef<'div'> & { index: number }
>(({ index, className, ...props }, forwardedRef) => {
  const inputOTPContext = React.useContext(OTPInputContext)
  const { inputSize, hasError } = React.useContext(InputPinContext)
  const { char, hasFakeCaret, isActive } = inputOTPContext.slots[index]
  const styles = inputPinStyles({ isActive, inputSize, hasError })
  return (
    <div ref={forwardedRef} className={styles.slot({ className })} {...props}>
      {char}
      {hasFakeCaret && (
        <div className={styles.slotCaretContainer()}>
          <div className={styles.slotCaret()} />
        </div>
      )}
    </div>
  )
})
 
interface InputPinSeparatorProps extends React.ComponentPropsWithoutRef<'div'> {
  innerClassName?: string
  orientation?: 'horizontal' | 'vertical'
}
 
const InputPinSeparator = React.forwardRef<React.ComponentRef<'div'>, InputPinSeparatorProps>(
  ({ className, innerClassName, orientation = 'horizontal', ...props }, forwardedRef) => {
    const { inputSize } = React.useContext(InputPinContext)
    const styles = inputPinStyles({ inputSize, orientation })
    return (
      <div ref={forwardedRef} className={styles.separator({ className })} {...props}>
        <div className={styles.separatorInner({ className: innerClassName })} />
      </div>
    )
  }
)
 
InputPin.displayName = 'InputPin'
InputPinGroup.displayName = 'InputPinGroup'
InputPinSlot.displayName = 'InputPinSlot'
InputPinSeparator.displayName = 'InputPinSeparator'
 
export { InputPin, InputPinGroup, InputPinSlot, InputPinSeparator }

Usage

Imports

import {
  InputPin,
  InputPinGroup,
  InputPinSlot,
  InputPinSeparator
} from '#/components/input-pin'

Example

Browse the Storybook for more examples.

Edit on GitHub

Last updated on

On this page