{"id":49369692,"url":"https://github.com/appandflow/react-native-transformer-text-input","last_synced_at":"2026-04-27T22:01:12.387Z","repository":{"id":353856024,"uuid":"1121851343","full_name":"AppAndFlow/react-native-transformer-text-input","owner":"AppAndFlow","description":"TextInput component that allows transforming text synchronously with a worklet.","archived":false,"fork":false,"pushed_at":"2026-04-25T21:47:42.000Z","size":1539,"stargazers_count":76,"open_issues_count":2,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-25T23:27:10.532Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/AppAndFlow.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2025-12-23T16:55:31.000Z","updated_at":"2026-04-25T21:47:45.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/AppAndFlow/react-native-transformer-text-input","commit_stats":null,"previous_names":["appandflow/react-native-transformer-text-input"],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/AppAndFlow/react-native-transformer-text-input","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AppAndFlow%2Freact-native-transformer-text-input","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AppAndFlow%2Freact-native-transformer-text-input/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AppAndFlow%2Freact-native-transformer-text-input/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AppAndFlow%2Freact-native-transformer-text-input/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/AppAndFlow","download_url":"https://codeload.github.com/AppAndFlow/react-native-transformer-text-input/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AppAndFlow%2Freact-native-transformer-text-input/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32356602,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-27T20:07:02.737Z","status":"ssl_error","status_checked_at":"2026-04-27T20:07:00.910Z","response_time":128,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-04-27T22:01:10.777Z","updated_at":"2026-04-27T22:01:12.379Z","avatar_url":"https://github.com/AppAndFlow.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cimg width=\"100%\" height=\"autp\" alt=\"react-native-transformer-input by App\u0026Flow\" src=\"https://github.com/user-attachments/assets/994aa73e-2db6-434c-bdd5-3069658e7c2c\" /\u003e\n\n\nTextInput component that allows transforming text synchronously with a worklet.\n\n\n## About\nApp \u0026 Flow is a Montreal-based React Native engineering and consulting studio. We partner with the world’s top companies and are recommended by [Expo](https://expo.dev/consultants). Need a hand? Let’s build together. team@appandflow.com\n\n## Demo\n\n\n\nhttps://github.com/user-attachments/assets/b22041b2-f5c1-4b2a-9fd0-960d7f5b3cf9\n\n\n\n\n\n\n## Motivation\n\nTransforming input as users type is common — phone numbers, credit cards, usernames. Existing approaches have trade-offs:\n\n**Pattern-based masking** (e.g., [react-native-advanced-input-mask](https://github.com/IvanIhnatsiuk/react-native-advanced-input-mask)) uses declarative patterns like `+1 ([000]) [000]-[0000]`. This works well for fixed formats, but patterns can't express conditional logic, variable-length formats, or transformations that depend on context.\n\n**Controlled inputs** (`value` + `onChangeText` + state) give you full JS flexibility, but create a native → JS → re-render → native round-trip. This causes visible lag and cursor flicker—the input feels sluggish because keystrokes are corrected asynchronously.\n\n**This library** combines JS flexibility with synchronous execution. Your transform runs as a worklet on the UI thread — no bridge delay, no flicker. Write any logic you need with the responsiveness of a native input.\n\n## Installation\n```sh\nnpm install react-native-transformer-text-input\n```\n\n## Usage\n```tsx\nimport { useRef } from 'react';\nimport {\n  Transformer,\n  TransformerTextInput,\n  type TransformerTextInputInstance,\n} from 'react-native-transformer-text-input';\n\n// Transformer that formats input as a lowercase username with @ prefix\nconst usernameTransformer = new Transformer(({ value }) =\u003e {\n  'worklet';\n\n  const cleaned = value.replace(/[^0-9a-zA-Z]/g, '').toLowerCase();\n  return { value: cleaned ? '@' + cleaned : '' };\n});\n\nfunction UsernameTextInput() {\n  const inputRef = useRef\u003cTransformerTextInputInstance\u003e(null);\n\n  const handleSubmit = () =\u003e {\n    const username = inputRef.current?.getValue();\n    console.log('Submitted:', username);\n  };\n\n  return (\n    \u003cTransformerTextInput\n      ref={inputRef}\n      transformer={usernameTransformer}\n      placeholder=\"@username\"\n      autoCapitalize=\"none\"\n      autoCorrect={false}\n      onSubmitEditing={handleSubmit}\n    /\u003e\n  );\n}\n```\n\n## API\n\n### Transformer\n\nCreate a transformer by passing a worklet function:\n\n- **Constructor**: `new Transformer(worklet)`\n- **worklet input**: an object with\n  - `value`: current text value.\n  - `previousValue`: previous text value (falls back to `value` on first call).\n  - `selection`: current selection `{ start, end }`.\n  - `previousSelection`: previous selection `{ start, end }` (falls back to `selection` on first call).\n- **worklet return**:\n  - Return `null` or `undefined` to apply no transform.\n  - Return an object where each field can also be `null` or `undefined` to leave that part unchanged:\n    - `value?: string | null` to update the text.\n    - `selection?: { start: number; end: number } | null` to update the selection.\n\n### TransformerTextInput\n\n`TransformerTextInput` wraps React Native `TextInput` and applies a `Transformer` on the UI thread.\n\n- **Props**: all `TextInput` props (except `value`) plus:\n  - `transformer`: a `Transformer` instance.\n- **Ref**: `TransformerTextInputInstance` with:\n  - `getValue(): string` - Returns the current text value.\n  - `update(options): void` - Programmatically update the input.\n    - `options.value: string` - The new text value.\n    - `options.selection?: { start: number; end: number }` - Optional cursor/selection position.\n    - `options.transform?: boolean` - Whether to run the transformer on the new value (default: `true`).\n  - `clear(): void` - Clear the input value.\n\n## Notes\n\n- The transformer must be a worklet; the `Transformer` constructor will throw if it isn't.\n- Prefer creating `Transformer` instances at module scope to avoid recreating worklets on every render.\n- This library supports the New Architecture only.\n\n## Selection Control\n\nSelection control is needed because transforms can insert or remove characters, which would otherwise move the cursor unpredictably. The transformer can return a `selection` to fully control the caret/selection after a change.\n\nDefault behavior when no `selection` is returned:\n- If the cursor was at the end, it stays at the end.\n- If the cursor was in the middle, it moves forward by the number of inserted/removed characters.\n- If the position is ambiguous, it falls back to the end.\n\n## Built-in Transformers (Experimental)\n\n\u003e **Warning**: Built-in transformers are experimental. Breaking changes may occur in minor versions.\n\nThe library includes ready-to-use transformers for common use cases.\n\n### PatternTransformer\n\nFormats input using a pattern string with placeholder characters.\n\n```tsx\nimport { PatternTransformer } from 'react-native-transformer-text-input/formatters/pattern';\n\nconst dateTransformer = new PatternTransformer({\n  pattern: '##/##/####',  // # = digit, A = letter, * = alphanumeric\n});\n\n// Formats as: 12/31/2024\n\u003cTransformerTextInput\n  transformer={dateTransformer}\n  keyboardType=\"number-pad\"\n/\u003e\n```\n\nOptions:\n- `pattern`: Pattern string where `#` matches digits, `A` matches letters, `*` matches alphanumeric.\n- `definitions`: Custom placeholder definitions (e.g., `{ 'X': /[0-9A-F]/i }` for hex).\n- `showTrailingLiterals`: Show literal characters after the last input (default: `false`).\n\n### PhoneNumberTransformer\n\nFormats phone numbers as the user types.\n\n```tsx\nimport { PhoneNumberTransformer } from 'react-native-transformer-text-input/formatters/phone-number';\n\nconst phoneTransformer = new PhoneNumberTransformer({\n  country: 'US',           // Only 'US' supported currently\n  debug: false,            // Enable debug logging (default: false)\n});\n\n// Formats as: +1 (555) 123-4567\n\u003cTransformerTextInput\n  transformer={phoneTransformer}\n  keyboardType=\"phone-pad\"\n/\u003e\n```\n\n## Acknowledgments\n\n- [react-native-live-markdown](https://github.com/Expensify/react-native-live-markdown) and [react-native-advanced-input-mask](https://github.com/IvanIhnatsiuk/react-native-advanced-input-mask) for examples of how to extend TextInput.\n- [react-native-worklets](https://github.com/software-mansion/react-native-reanimated/tree/main/packages/react-native-worklets) for the worklet runtime powering UI-thread execution.\n\n## Contributing\n\n- [Development workflow](CONTRIBUTING.md#development-workflow)\n- [Sending a pull request](CONTRIBUTING.md#sending-a-pull-request)\n- [Code of conduct](CODE_OF_CONDUCT.md)\n\n## License\n\nMIT\n\n---\n\nMade with [create-react-native-library](https://github.com/callstack/react-native-builder-bob)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fappandflow%2Freact-native-transformer-text-input","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fappandflow%2Freact-native-transformer-text-input","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fappandflow%2Freact-native-transformer-text-input/lists"}