{"_id":"@codehogs/react-native-otp-fields","_rev":"3-68fc027809ef2d5baeed5ad85369c88d","name":"@codehogs/react-native-otp-fields","dist-tags":{"latest":"0.2.0"},"versions":{"0.1.0":{"name":"@codehogs/react-native-otp-fields","version":"0.1.0","keywords":["react-native","otp","pin-input","verification-code","sms-code","one-time-password"],"author":"","license":"MIT","_id":"@codehogs/react-native-otp-fields@0.1.0","maintainers":[{"name":"codehogs","email":"jananijayasuriya330@gmail.com"}],"homepage":"https://github.com/codehogs/react-native-otp-fields#readme","bugs":{"url":"https://github.com/codehogs/react-native-otp-fields/issues"},"dist":{"shasum":"61ef775dd8181a5af93b50b6640a40189d0701ff","tarball":"https://registry.npmjs.org/@codehogs/react-native-otp-fields/-/react-native-otp-fields-0.1.0.tgz","fileCount":21,"integrity":"sha512-9K3dHwf7rYoddZXAGEkMYV8Y90Z4kY+288eWVRVM22/AdKxiBmkpX8dnfVdVMaQlsPeGhE81pJd0YVZcR2cEcQ==","signatures":[{"sig":"MEYCIQDGz1E85Ftmu+/ItYOplMo5czwvxZDvdBKDGSmR10cT5wIhAMZ2ia0IWJJW+jLvtDmYSnaloF8f8d6hzvuOTYFzSu4K","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":47759},"jest":{"watchman":false,"transform":{"^.+\\.jsx?$":"babel-jest"},"testEnvironment":"node","testPathIgnorePatterns":["/node_modules/","/lib/"]},"main":"./lib/commonjs/index.js","types":"./types/index.d.ts","module":"./lib/module/index.js","source":"./src/index.js","exports":{".":{"types":"./types/index.d.ts","import":"./lib/module/index.js","require":"./lib/commonjs/index.js","react-native":"./src/index.js"}},"gitHead":"ef37376e0bda58ab934df06d460d8ca2dd85871f","scripts":{"lint":"eslint \"src/**/*.{js,jsx}\"","test":"jest","build":"bob build","clean":"del-cli lib","prepublishOnly":"npm run build && npm test"},"_npmUser":{"name":"codehogs","email":"jananijayasuriya330@gmail.com"},"repository":{"url":"git+https://github.com/codehogs/react-native-otp-fields.git","type":"git"},"_npmVersion":"10.9.2","description":"Lightweight and customizable OTP input for React Native.","directories":{},"sideEffects":false,"_nodeVersion":"22.17.0","eslintConfig":{"env":{"jest":true,"node":true,"es2022":true},"root":true,"rules":{"react/prop-types":"off","react/react-in-jsx-scope":"off"},"parser":"@babel/eslint-parser","extends":["eslint:recommended","plugin:react/recommended","plugin:react-hooks/recommended"],"globals":{"cancelAnimationFrame":"readonly","requestAnimationFrame":"readonly"},"settings":{"react":{"version":"detect"}},"parserOptions":{"sourceType":"module","ecmaVersion":2022,"babelOptions":{"presets":["@babel/preset-react"]},"ecmaFeatures":{"jsx":true},"requireConfigFile":false}},"react-native":"./src/index.js","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^29.7.0","react":"18.3.1","eslint":"^8.57.0","del-cli":"^6.0.0","babel-jest":"^29.7.0","@babel/core":"^7.26.0","react-native":"0.76.5","@babel/preset-env":"^7.26.0","@babel/preset-react":"^7.26.0","eslint-plugin-react":"^7.37.0","@babel/eslint-parser":"^7.26.0","react-native-builder-bob":"^0.40.18","eslint-plugin-react-hooks":"^5.0.0"},"peerDependencies":{"react":">=18.0.0","react-native":">=0.74.0"},"_npmOperationalInternal":{"tmp":"tmp/react-native-otp-fields_0.1.0_1774333623430_0.4839614941088686","host":"s3://npm-registry-packages-npm-production"},"react-native-builder-bob":{"output":"lib","source":"src","targets":[["commonjs",{"esm":true}],["module",{"esm":true}]]}},"0.1.1":{"name":"@codehogs/react-native-otp-fields","version":"0.1.1","keywords":["react-native","otp","pin-input","verification-code","sms-code","one-time-password"],"author":"","license":"MIT","_id":"@codehogs/react-native-otp-fields@0.1.1","maintainers":[{"name":"codehogs","email":"jananijayasuriya330@gmail.com"}],"homepage":"https://github.com/Janani-Withana/react-native-otp-fields#readme","bugs":{"url":"https://github.com/Janani-Withana/react-native-otp-fields/issues"},"dist":{"shasum":"b7f9e4a53f119b0c85eb6a8b4c50227e2589c620","tarball":"https://registry.npmjs.org/@codehogs/react-native-otp-fields/-/react-native-otp-fields-0.1.1.tgz","fileCount":21,"integrity":"sha512-knfr6AFFIbk0vBqBDQIvn15RdQUjbhZlQsWZ8cNFFDReYN7yY1QCdmji7RT5hmU9u8KWQC/EZKDACdXoUcDrZw==","signatures":[{"sig":"MEUCIGOGg3GrjQsdX3GRn2hKsWJ4+k9ra8YZ300ugy8pFEs3AiEA7BlcU0Vp/Cy/mLZNNAkB7k1PUMeBTw6rH728cgmvoa8=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":47942},"jest":{"watchman":false,"transform":{"^.+\\.jsx?$":"babel-jest"},"testEnvironment":"node","testPathIgnorePatterns":["/node_modules/","/lib/"]},"main":"./lib/commonjs/index.js","types":"./types/index.d.ts","module":"./lib/module/index.js","source":"./src/index.js","exports":{".":{"types":"./types/index.d.ts","import":"./lib/module/index.js","require":"./lib/commonjs/index.js","react-native":"./src/index.js"}},"gitHead":"e7e2f7a38cc708959e5df7b737b9714c45f2b741","scripts":{"lint":"eslint \"src/**/*.{js,jsx}\"","test":"jest","build":"bob build","clean":"del-cli lib","prepublishOnly":"npm run build && npm test"},"_npmUser":{"name":"codehogs","email":"jananijayasuriya330@gmail.com"},"repository":{"url":"git+https://github.com/Janani-Withana/react-native-otp-fields.git","type":"git"},"_npmVersion":"10.9.2","description":"Lightweight and customizable OTP input for React Native.","directories":{},"sideEffects":false,"_nodeVersion":"22.17.0","eslintConfig":{"env":{"jest":true,"node":true,"es2022":true},"root":true,"rules":{"react/prop-types":"off","react/react-in-jsx-scope":"off"},"parser":"@babel/eslint-parser","extends":["eslint:recommended","plugin:react/recommended","plugin:react-hooks/recommended"],"globals":{"cancelAnimationFrame":"readonly","requestAnimationFrame":"readonly"},"settings":{"react":{"version":"detect"}},"parserOptions":{"sourceType":"module","ecmaVersion":2022,"babelOptions":{"presets":["@babel/preset-react"]},"ecmaFeatures":{"jsx":true},"requireConfigFile":false}},"react-native":"./src/index.js","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^29.7.0","react":"18.3.1","eslint":"^8.57.0","del-cli":"^6.0.0","babel-jest":"^29.7.0","@babel/core":"^7.26.0","react-native":"0.76.5","@babel/preset-env":"^7.26.0","@babel/preset-react":"^7.26.0","eslint-plugin-react":"^7.37.0","@babel/eslint-parser":"^7.26.0","react-native-builder-bob":"^0.40.18","eslint-plugin-react-hooks":"^5.0.0"},"peerDependencies":{"react":">=18.0.0","react-native":">=0.74.0"},"_npmOperationalInternal":{"tmp":"tmp/react-native-otp-fields_0.1.1_1774417527112_0.24971150917463536","host":"s3://npm-registry-packages-npm-production"},"react-native-builder-bob":{"output":"lib","source":"src","targets":[["commonjs",{"esm":true}],["module",{"esm":true}]]}},"0.2.0":{"name":"@codehogs/react-native-otp-fields","version":"0.2.0","description":"Lightweight and customizable OTP input for React Native.","source":"./src/index.js","main":"./lib/commonjs/index.js","module":"./lib/module/index.js","react-native":"./src/index.js","types":"./types/index.d.ts","exports":{".":{"react-native":"./src/index.js","import":"./lib/module/index.js","require":"./lib/commonjs/index.js","types":"./types/index.d.ts"}},"sideEffects":false,"scripts":{"build":"bob build","prepublishOnly":"npm run build && npm test","clean":"del-cli lib","lint":"eslint \"src/**/*.{js,jsx}\"","test":"jest"},"keywords":["react-native","otp","pin-input","verification-code","sms-code","one-time-password"],"publishConfig":{"access":"public"},"homepage":"https://github.com/Janani-Withana/react-native-otp-fields#readme","bugs":{"url":"https://github.com/Janani-Withana/react-native-otp-fields/issues"},"repository":{"type":"git","url":"git+https://github.com/Janani-Withana/react-native-otp-fields.git"},"author":"","license":"MIT","peerDependencies":{"react":">=18.0.0","react-native":">=0.74.0"},"devDependencies":{"@babel/core":"^7.26.0","@babel/eslint-parser":"^7.26.0","@babel/preset-env":"^7.26.0","@babel/preset-react":"^7.26.0","babel-jest":"^29.7.0","del-cli":"^6.0.0","eslint":"^8.57.0","eslint-plugin-react":"^7.37.0","eslint-plugin-react-hooks":"^5.0.0","jest":"^29.7.0","react":"18.3.1","react-native":"0.76.5","react-native-builder-bob":"^0.40.18"},"react-native-builder-bob":{"source":"src","output":"lib","targets":[["commonjs",{"esm":true}],["module",{"esm":true}]]},"eslintConfig":{"root":true,"extends":["eslint:recommended","plugin:react/recommended","plugin:react-hooks/recommended"],"parser":"@babel/eslint-parser","parserOptions":{"ecmaVersion":2022,"sourceType":"module","ecmaFeatures":{"jsx":true},"requireConfigFile":false,"babelOptions":{"presets":["@babel/preset-react"]}},"env":{"es2022":true,"node":true,"jest":true},"globals":{"requestAnimationFrame":"readonly","cancelAnimationFrame":"readonly"},"settings":{"react":{"version":"detect"}},"rules":{"react/react-in-jsx-scope":"off","react/prop-types":"off"}},"jest":{"testEnvironment":"node","watchman":false,"transform":{"^.+\\.jsx?$":"babel-jest"},"testPathIgnorePatterns":["/node_modules/","/lib/"]},"_id":"@codehogs/react-native-otp-fields@0.2.0","gitHead":"fe4fda18c11b94b15d649c82e216894e239f0ee9","_nodeVersion":"22.17.0","_npmVersion":"10.9.2","dist":{"integrity":"sha512-OYVJXT4CkmW0pMwpgZU87i5Qiskkz2K9R/jrVZmMhEsnA+hNpP6/ouBAKWDtLPUYBx3ojmXgGhi0UlDOuBlBgw==","shasum":"e353661dcb16bf2c3c742143562dc5514fe211ba","tarball":"https://registry.npmjs.org/@codehogs/react-native-otp-fields/-/react-native-otp-fields-0.2.0.tgz","fileCount":21,"unpackedSize":84500,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIA4pJ+IXy2siTRyiX5o/jgufTWG3gmLwhrqnhlJI1Ap0AiAPJv0E0HAAC0i9mJ2dbyPo3rteBruGYfyEJTpCqCCiWw=="}]},"_npmUser":{"name":"codehogs","email":"jananijayasuriya330@gmail.com"},"directories":{},"maintainers":[{"name":"codehogs","email":"jananijayasuriya330@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/react-native-otp-fields_0.2.0_1774433838549_0.6466333320257289"},"_hasShrinkwrap":false}},"time":{"created":"2026-03-24T06:27:03.369Z","modified":"2026-03-25T10:17:18.818Z","0.1.0":"2026-03-24T06:27:03.597Z","0.1.1":"2026-03-25T05:45:27.258Z","0.2.0":"2026-03-25T10:17:18.715Z"},"bugs":{"url":"https://github.com/Janani-Withana/react-native-otp-fields/issues"},"license":"MIT","homepage":"https://github.com/Janani-Withana/react-native-otp-fields#readme","keywords":["react-native","otp","pin-input","verification-code","sms-code","one-time-password"],"repository":{"type":"git","url":"git+https://github.com/Janani-Withana/react-native-otp-fields.git"},"description":"Lightweight and customizable OTP input for React Native.","maintainers":[{"name":"codehogs","email":"jananijayasuriya330@gmail.com"}],"readme":"# @codehogs/react-native-otp-fields\n\n**Lightweight and customizable OTP input for React Native.**\n\nFixed-length OTP cells with auto-advance, backspace navigation, paste support, controlled and uncontrolled usage, optional masking, error styling, semantic color/size tokens (so you rarely fight `StyleSheet`), optional **group separators** (like a 3+3 layout), **`pasteTransformer`** (same idea as [input-otp](https://github.com/guilhermerodz/input-otp)), optional **digits-only** filtering, an imperative **ref** API (`focus` / `blur` / `clear` / `getValue`), and a completion callback. No native modules—pure JavaScript on top of React Native `TextInput`.\n\n## Requirements\n\n- React ≥ 18\n- React Native ≥ 0.74\n\n## Installation\n\n```bash\nnpm install @codehogs/react-native-otp-fields\n```\n\n```bash\nyarn add @codehogs/react-native-otp-fields\n```\n\n## Quick start\n\n```jsx\nimport React, { useState } from 'react';\nimport { View } from 'react-native';\nimport { OTPInput } from '@codehogs/react-native-otp-fields';\n\nexport default function App() {\n  const [otp, setOtp] = useState('');\n\n  return (\n    <View style={{ padding: 24 }}>\n      <OTPInput\n        length={6}\n        value={otp}\n        onChangeText={setOtp}\n        onComplete={(code) => console.log('Completed:', code)}\n      />\n    </View>\n  );\n}\n```\n\n## API\n\n### `OTPInput`\n\n| Prop | Type | Default | Description |\n|------|------|---------|-------------|\n| `length` | `number` | `6` | Number of OTP characters. |\n| `value` | `string` | — | Controlled value (whitespace stripped; if `digitsOnly`, non-digits stripped). |\n| `defaultValue` | `string` | `''` | Initial value when uncontrolled. |\n| `onChangeText` | `(value: string) => void` | — | Called whenever the combined code changes. |\n| `onComplete` | `(value: string) => void` | — | Called when `value` first reaches `length`; can fire again after edits and a new completion. |\n| `autoFocus` | `boolean` | `false` | Focus the first cell on mount. |\n| `editable` | `boolean` | `true` | Disables all cells when `false`. |\n| `secureTextEntry` | `boolean` | `false` | Mask digits (PIN-style). |\n| `keyboardType` | `TextInput` keyboard type | `'number-pad'` | e.g. `'number-pad'` or `'numeric'`. |\n| `placeholder` | `string` | `''` | Placeholder per cell. |\n| `hasError` | `boolean` | `false` | Applies error border (and `errorCellStyle`). |\n| `testID` | `string` | — | Container `testID`; cells use `${testID}-cell-${index}`. |\n| `digitsOnly` | `boolean` | `false` | Strip non-digits from input and from `value` / `defaultValue` when displaying. |\n| `pasteTransformer` | `(pasted: string) => string` | — | Applied on **paste** (multi-character input). Example: `(t) => t.replace(/-/g, '')`. |\n| `splitAfterIndexes` | `number[]` | — | e.g. `[2]` inserts a separator **after** the 3rd cell (indices are 0-based cell indexes). |\n| `separator` | `ReactNode` | — | Custom node for each gap; default is a small rounded bar. Pass `null` and use `renderSeparator` to hide, or override. |\n| `renderSeparator` | `({ afterIndex }) => ReactNode` | — | Full control per gap. |\n| `separatorContainerStyle` | `ViewStyle` | — | Wrapper around the separator. |\n| `separatorStyle` | `ViewStyle` | — | Extra styles for the **default** pill separator. |\n| `separatorWidth` / `separatorHeight` / `separatorColor` | `number` / `string` | `12` / `4` / `#D0D5DD` | Default separator appearance. |\n| `gap` | `number` | `8` | Space between cells (and around separators). |\n| `cellWidth` / `cellHeight` | `number` | `48` / `56` | Cell size. |\n| `borderRadius` / `borderWidth` | `number` | `12` / `1` | Cell shape. |\n| `borderColor` | `string` | `#D0D5DD` | Default cell border. |\n| `backgroundColor` | `string` | — | Cell background (optional). |\n| `textColor` | `string` | `#101828` | Character color. |\n| `placeholderTextColor` | `string` | `#999` | Placeholder color. |\n| `focusedBorderColor` / `focusedBackgroundColor` | `string` | `#101828` / — | Focused cell. |\n| `filledBorderColor` / `filledBackgroundColor` | `string` | — | When a cell has a character. |\n| `errorBorderColor` | `string` | `#F04438` | When `hasError`. |\n| `fontSize` / `fontFamily` | `number` / `string` | `20` / — | Typography shortcuts (still override with `textStyle`). |\n| `caretHidden` | `boolean` | `false` | Passed through to each `TextInput`. |\n\n**Override props** (merged after tokens): `containerStyle`, `cellStyle`, `focusedCellStyle`, `filledCellStyle`, `errorCellStyle`, `textStyle`.\n\n### Ref (`forwardRef`)\n\nPass a ref to call:\n\n| Method | Description |\n|--------|-------------|\n| `focus(index?)` | Focus cell `index` (default `0`). |\n| `blur()` | Blur all cells. |\n| `clear()` | Clears value (calls `onChangeText('')` in controlled mode; also resets completion tracking). |\n| `getValue()` | Returns the current combined string. |\n\n### Utilities\n\n- **`clampOtpValue(text, length)`** — Strips whitespace and truncates.\n- **`sanitizeOtpInput(text, length, options?)`** — Whitespace strip, optional `digitsOnly`, optional `pasteTransformer`, then truncate.\n- **`normalizeStoredOtpValue(text, length, digitsOnly?)`** — For normalizing controlled `value` / `defaultValue`.\n\n## Behavior\n\n- **Auto-advance:** After a digit in a cell, focus moves to the next cell.\n- **Backspace:** Clears the current cell if it has a digit; otherwise moves to the previous cell and clears it.\n- **Paste:** Pasting a multi-character code into any cell fills from the start (up to `length`) and moves focus to the last filled cell.\n- **SMS / OTP autofill:** Uses `textContentType=\"oneTimeCode\"` (iOS) and `autoComplete` / `importantForAutofill` (Android) to cooperate with system OTP suggestions where the OS supports it.\n- **Completion:** `onComplete` runs when the string length first reaches `length`; if the user edits the code, it can fire again after the next full completion.\n\n## Styling and layout\n\nPrefer **semantic props** (`gap`, `cellWidth`, `cellHeight`, `borderColor`, `textColor`, `focusedBorderColor`, `errorBorderColor`, …) for a design system–friendly setup; use `cellStyle` / `textStyle` when you need full control.\n\n**Grouped layout (e.g. 3 + 3):**\n\n```jsx\n<OTPInput\n  length={6}\n  splitAfterIndexes={[2]}\n  gap={10}\n  cellWidth={44}\n/>\n```\n\nDefault cells are **48×56** with **`gap: 8`**. The row uses `alignItems: 'center'` so separators line up with cells.\n\n## Controlled vs uncontrolled\n\n- **Controlled:** Pass `value` + `onChangeText` (recommended when the code is stored in parent state or a form library).\n- **Uncontrolled:** Omit `value` and optionally set `defaultValue`.\n\n## Testing\n\nWith `testID=\"login-otp\"`, the container has `testID=\"login-otp\"` and cells have `testID=\"login-otp-cell-0\"`, `login-otp-cell-1`, etc.\n\n## TypeScript\n\nType definitions are included (`types/index.d.ts`).\n\n## Local development (this repo)\n\n```bash\nnpm install\nnpm run build\nnpm test\n```\n\nThe package ships pre-built `lib/` output. `prepublishOnly` runs `build` and `tests` before `npm publish`; consumers do not need to compile the library.\n\nLink into an app:\n\n```bash\nnpm pack\n# In your app: npm install /path/to/codehogs-react-native-otp-fields-0.2.0.tgz\n```\n\nOr use `yarn link` / `npm link` per your workflow.\n\n## Publishing\n\n1. Bump `version` in `package.json`.\n2. `npm run build`\n3. `npm publish --access public` (after `npm login`; scoped packages need `--access public` for a free public package).\n\nEnsure `files` in `package.json` only ships `src`, `lib`, `types`, `README.md`, and `LICENSE`.\n\n## License\n\nMIT\n","readmeFilename":"README.md"}