A highly customizable React component library for code input fields. Perfect for verification codes, OTP inputs, PIN entries, and more. Supports two distinct design patterns: individual boxes and single line inputs.
- β Two Design Variants: Box-style (individual inputs) and Line-style (single input with spacing)
 - β Full TypeScript Support: Complete type safety and IntelliSense
 - β Highly Customizable: Extensive styling options for borders, colors, fonts, and spacing
 - β Separator Support: Add custom separators at specified positions
 - β Keyboard Navigation: Arrow keys, backspace, and automatic focus management
 - β Paste Support: Smart paste handling with automatic formatting
 - β Ref Methods: Programmatic control with getValue, setValue, clear, and focus
 - β Responsive Design: Mobile-friendly with responsive breakpoints
 - β Accessibility: ARIA-compliant and keyboard accessible
 - β Zero Dependencies: Lightweight with no external dependencies
 
npm install react-segmented-inputDon't forget import package styles!
import "react-segmented-input/style.css";import React, { useRef } from "react";
import { BoxCodeInput } from "react-segmented-input";
import "react-segmented-input/style.css";
function App() {
  const inputRef = useRef(null);
  const handleComplete = (value) => {
    console.log("Code entered:", value);
  };
  const getValue = () => {
    const value = inputRef.current?.getValue();
    console.log("Current value:", value);
  };
  return (
    <div>
      <BoxCodeInput
        ref={inputRef}
        numberOfChars={6}
        separatorPositions={[3]}
        separatorChar="-"
        gap={12}
        onChange={(value) => console.log("Current value:", value)}
        onComplete={handleComplete}
        autoFocus
      />
      <button onClick={getValue}>Get Value</button>
    </div>
  );
}
export default App;import React, { useRef } from "react";
import { LineCodeInput } from "react-segmented-input";
import "react-segmented-input/style.css";
function App() {
  const inputRef = useRef(null);
  const handleComplete = (value) => {
    console.log("Code entered:", value);
  };
  return (
    <div>
      <LineCodeInput
        ref={inputRef}
        numberOfChars={8}
        separatorPositions={[4]}
        separatorChar="/"
        letterSpacing={12}
        onChange={(value) => console.log("Current value:", value)}
        onComplete={handleComplete}
      />
    </div>
  );
}
export default App;import React, { useRef } from "react";
import { BoxCodeInput, CodeInputRef } from "react-segmented-input";
import "react-segmented-input/style.css";
function App(): JSX.Element {
  const inputRef = useRef<CodeInputRef>(null);
  const handleComplete = (value: string): void => {
    console.log("Code entered:", value);
  };
  const getValue = (): void => {
    const value = inputRef.current?.getValue();
    console.log("Current value:", value);
  };
  return (
    <div>
      <BoxCodeInput
        ref={inputRef}
        numberOfChars={6}
        separatorPositions={[3]}
        separatorChar="-"
        gap={12}
        onChange={(value: string) => console.log("Current value:", value)}
        onComplete={handleComplete}
        autoFocus
      />
      <button onClick={getValue}>Get Value</button>
    </div>
  );
}
export default App;import React, { useRef } from "react";
import { LineCodeInput, CodeInputRef } from "react-segmented-input";
import "react-segmented-input/style.css";
function App(): JSX.Element {
  const inputRef = useRef<CodeInputRef>(null);
  const handleComplete = (value: string): void => {
    console.log("Code entered:", value);
  };
  return (
    <div>
      <LineCodeInput
        ref={inputRef}
        numberOfChars={8}
        separatorPositions={[4]}
        separatorChar="/"
        letterSpacing={12}
        onChange={(value: string) => console.log("Current value:", value)}
        onComplete={handleComplete}
      />
    </div>
  );
}
export default App;| Prop | Type | Default | Description | 
|---|---|---|---|
numberOfChars | 
number | 
required | Number of characters/boxes | 
borderTop | 
boolean | 
true | 
Show top border | 
borderRight | 
boolean | 
true | 
Show right border | 
borderBottom | 
boolean | 
true | 
Show bottom border | 
borderLeft | 
boolean | 
true | 
Show left border | 
border | 
boolean | 
true | 
Show all borders (overrides individual border props) | 
borderThickness | 
number | 
1 | 
Border thickness in pixels | 
borderColor | 
string | 
'#ccc' | 
Border color (hex, rgb, or named colors) | 
backgroundColor | 
string | 
'transparent' | 
Background color | 
fontSize | 
number | 
16 | 
Font size in pixels | 
fontWeight | 
number | 
400 | 
Font weight (100-900) | 
textColor | 
string | 
'#000' | 
Text color | 
borderRadius | 
number | 
4 | 
Border radius in pixels | 
width | 
number | 
40 | 
Width of each box in pixels | 
height | 
number | 
40 | 
Height of each box in pixels | 
gap | 
number | 
8 | 
Space between boxes in pixels | 
separatorPositions | 
number[] | 
[] | 
Positions where separators should appear | 
separatorChar | 
string | 
'-' | 
Character to use as separator | 
onChange | 
(value: string) => void | 
- | Called when value changes | 
onComplete | 
(value: string) => void | 
- | Called when all characters are entered | 
value | 
string | 
'' | 
Controlled value | 
placeholder | 
string | 
'' | 
Placeholder text for each box | 
disabled | 
boolean | 
false | 
Disable the input | 
autoFocus | 
boolean | 
false | 
Auto focus first input on mount | 
className | 
string | 
'' | 
Additional CSS class | 
style | 
React.CSSProperties | 
{} | 
Additional inline styles | 
| Prop | Type | Default | Description | 
|---|---|---|---|
numberOfChars | 
number | 
required | Number of characters | 
borderTop | 
boolean | 
true | 
Show top border | 
borderRight | 
boolean | 
true | 
Show right border | 
borderBottom | 
boolean | 
true | 
Show bottom border | 
borderLeft | 
boolean | 
true | 
Show left border | 
border | 
boolean | 
true | 
Show all borders | 
borderThickness | 
number | 
1 | 
Border thickness in pixels | 
borderColor | 
string | 
'#ccc' | 
Border color | 
backgroundColor | 
string | 
'transparent' | 
Background color | 
fontSize | 
number | 
16 | 
Font size in pixels | 
fontWeight | 
number | 
400 | 
Font weight | 
textColor | 
string | 
'#000' | 
Text color | 
textAlign | 
'center' | 'left' | 'right' | 
'center' | 
Text alignment | 
letterSpacing | 
number | 
8 | 
Space between characters in pixels | 
borderRadius | 
number | 
4 | 
Border radius in pixels | 
paddingTop | 
number | 
12 | 
Top padding in pixels | 
paddingRight | 
number | 
16 | 
Right padding in pixels | 
paddingBottom | 
number | 
12 | 
Bottom padding in pixels | 
paddingLeft | 
number | 
16 | 
Left padding in pixels | 
width | 
number | 
- | Fixed width (auto-calculated if not provided) | 
separatorPositions | 
number[] | 
[] | 
Positions for separators | 
separatorChar | 
string | 
'-' | 
Separator character | 
onChange | 
(value: string) => void | 
- | Value change callback | 
onComplete | 
(value: string) => void | 
- | Completion callback | 
value | 
string | 
'' | 
Controlled value | 
placeholder | 
string | 
'' | 
Placeholder text | 
disabled | 
boolean | 
false | 
Disable input | 
autoFocus | 
boolean | 
false | 
Auto focus on mount | 
className | 
string | 
'' | 
CSS class | 
style | 
React.CSSProperties | 
{} | 
Inline styles | 
Both components support ref methods for programmatic control:
| Method | Description | 
|---|---|
getValue() | 
Returns the current input value | 
setValue(value: string) | 
Sets the input value | 
clear() | 
Clears the input and focuses first field | 
focus() | 
Focuses the appropriate input field | 
import React, { useRef } from "react";
import { BoxCodeInput } from "react-segmented-input";
import "react-segmented-input/style.css";
function App() {
  const inputRef = useRef(null);
  const handleGetValue = () => {
    const value = inputRef.current?.getValue();
    alert(`Current value: ${value}`);
  };
  const handleClear = () => {
    inputRef.current?.clear();
  };
  const handleSetValue = () => {
    inputRef.current?.setValue("123456");
  };
  return (
    <div>
      <BoxCodeInput
        ref={inputRef}
        numberOfChars={6}
        borderColor="#3b82f6"
        backgroundColor="white"
      />
      <div>
        <button onClick={handleGetValue}>Get Value</button>
        <button onClick={handleClear}>Clear</button>
        <button onClick={handleSetValue}>Set Value</button>
      </div>
    </div>
  );
}
export default App;import React, { useRef } from "react";
import { BoxCodeInput, CodeInputRef } from "react-segmented-input";
import "react-segmented-input/style.css";
function App(): JSX.Element {
  const inputRef = useRef<CodeInputRef>(null);
  const handleGetValue = (): void => {
    const value = inputRef.current?.getValue();
    alert(`Current value: ${value}`);
  };
  const handleClear = (): void => {
    inputRef.current?.clear();
  };
  const handleSetValue = (): void => {
    inputRef.current?.setValue("123456");
  };
  return (
    <div>
      <BoxCodeInput
        ref={inputRef}
        numberOfChars={6}
        borderColor="#3b82f6"
        backgroundColor="white"
      />
      <div>
        <button onClick={handleGetValue}>Get Value</button>
        <button onClick={handleClear}>Clear</button>
        <button onClick={handleSetValue}>Set Value</button>
      </div>
    </div>
  );
}
export default App;<BoxCodeInput
  numberOfChars={11}
  separatorPositions={[1, 4, 7]}
  separatorChar=" "
  borderColor="#6b7280"
  textColor="#374151"
  placeholder="0"
  width={35}
  height={40}
/><LineCodeInput
  numberOfChars={16}
  separatorPositions={[4, 8, 12]}
  separatorChar=" "
  borderColor="#f59e0b"
  textColor="#92400e"
  letterSpacing={8}
  textAlign="center"
  placeholder="0000 0000 0000 0000"
/><LineCodeInput
  numberOfChars={26}
  separatorPositions={[2, 6, 10, 14, 18, 22]}
  separatorChar=" "
  borderColor="#8b5cf6"
  textColor="#6d28d9"
  fontSize={14}
  letterSpacing={4}
  textAlign="left"
  width={400}
  placeholder="TR00 0000 0000 0000 0000 0000 00"
/><BoxCodeInput
  numberOfChars={6}
  borderColor="#10b981"
  backgroundColor="#f0fdf4"
  textColor="#065f46"
  fontSize={24}
  fontWeight={600}
  borderRadius={8}
  width={50}
  height={50}
  autoFocus
  onComplete={(code) => {
    // Verify OTP
    verifyOTP(code);
  }}
/><LineCodeInput
  numberOfChars={8}
  separatorPositions={[4]}
  separatorChar="-"
  borderColor="#ec4899"
  backgroundColor="#fdf2f8"
  textColor="#be185d"
  fontSize={20}
  fontWeight={500}
  letterSpacing={15}
  borderRadius={12}
  paddingTop={20}
  paddingBottom={20}
  paddingLeft={25}
  paddingRight={25}
  style={{
    boxShadow: "0 4px 6px -1px rgba(0, 0, 0, 0.1)",
  }}
/>Separators are added after the specified character positions:
// For numberOfChars={8} and separatorPositions={[2, 6]}
// Input: "12345678"
// Display: "12-345678" (separator after 2nd position)
//          "12-3456-78" (separator after 6th position)
<LineCodeInput
  numberOfChars={8}
  separatorPositions={[2, 6]}
  separatorChar="-"
/>// Uncontrolled (recommended for most cases)
<BoxCodeInput numberOfChars={6} onChange={(value) => console.log(value)} />;
// Controlled
function ControlledExample() {
  const [value, setValue] = useState("");
  return <BoxCodeInput numberOfChars={6} value={value} onChange={setValue} />;
}function ValidatedInput() {
  const [value, setValue] = useState("");
  const [isValid, setIsValid] = useState(true);
  const handleChange = (newValue) => {
    setValue(newValue);
    // Custom validation logic
    setIsValid(newValue.length === 0 || /^\d+$/.test(newValue));
  };
  return (
    <BoxCodeInput
      numberOfChars={6}
      value={value}
      onChange={handleChange}
      borderColor={isValid ? "#10b981" : "#ef4444"}
      backgroundColor={isValid ? "#f0fdf4" : "#fef2f2"}
    />
  );
}The components provide CSS classes for custom styling:
/* BoxCodeInput */
.box-code-input {
  /* Container styles */
}
.box-code-input__field {
  /* Individual input field styles */
}
.box-code-input__separator {
  /* Separator styles */
}
/* LineCodeInput */
.line-code-input {
  /* Container styles */
}
.line-code-input__field {
  /* Input field styles */
}.my-custom-input .box-code-input__field {
  border: 2px solid #3b82f6;
  border-radius: 8px;
  font-family: "Monaco", "Menlo", monospace;
}
.my-custom-input .box-code-input__field:focus {
  border-color: #1d4ed8;
  box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
}The components include responsive breakpoints:
@media (max-width: 640px) {
  .box-code-input {
    gap: 4px; /* Reduced gap on mobile */
  }
  .line-code-input__field {
    font-size: 14px !important;
    letter-spacing: 4px !important;
    padding: 8px 12px !important;
  }
}- Lightweight: ~4KB gzipped
 - Zero Dependencies: No external dependencies
 - Optimized Rendering: Minimal re-renders with React best practices
 - Memory Efficient: Proper cleanup and ref management
 
import { render, fireEvent, screen } from "@testing-library/react";
import { BoxCodeInput } from "react-segmented-input";
test("handles input correctly", () => {
  const handleChange = jest.fn();
  render(
    <BoxCodeInput
      numberOfChars={4}
      onChange={handleChange}
      data-testid="code-input"
    />
  );
  const inputs = screen.getAllByRole("textbox");
  fireEvent.change(inputs[0], { target: { value: "1" } });
  expect(handleChange).toHaveBeenCalledWith("1");
});Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
- Fork the repository
 - Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
 
This project is licensed under the MIT License - see the LICENSE file for details.
If you find this package helpful, please consider:
- β Starring the repository
 - π Reporting bugs
 - π‘ Suggesting new features
 - π Improving documentation
 
- Chrome β₯ 60
 - Firefox β₯ 60
 - Safari β₯ 12
 - Edge β₯ 79
 
