{"_id":"@amrshbib/react-native-haptic-feedback","name":"@amrshbib/react-native-haptic-feedback","dist-tags":{"latest":"0.0.1"},"versions":{"0.0.1":{"name":"@amrshbib/react-native-haptic-feedback","version":"0.0.1","description":"Semantic haptics for React Native. Real Taptic Engine on iOS (selection, impact, notification) and Core Haptics for arbitrary durations; VibrationEffect primitives on Android. One vocabulary, no dead 400ms buzz.","main":"src/index.js","types":"index.d.ts","react-native":"src/index.js","sideEffects":false,"keywords":["react-native","haptic","haptics","haptic-feedback","vibration","vibrate","taptic","taptic-engine","core-haptics","ios","android"],"author":{"name":"Amr Shbib","email":"amr.shbib@mythings.app"},"license":"MIT","homepage":"https://github.com/amrshbib/react-native-haptic-feedback#readme","repository":{"type":"git","url":"git+https://github.com/amrshbib/react-native-haptic-feedback.git"},"bugs":{"url":"https://github.com/amrshbib/react-native-haptic-feedback/issues"},"peerDependencies":{"react":"*","react-native":">=0.71.0"},"engines":{"node":">=18"},"_id":"@amrshbib/react-native-haptic-feedback@0.0.1","_nodeVersion":"25.2.1","_npmVersion":"11.6.2","dist":{"integrity":"sha512-BxDwgNMpaW+Fuzfw7Qk2C68celL6d2TvnCw2hRGj+F+rrn6cdo7hm3/KPkPMrPh5F4VusCLvZD5Dnazcx3E5Og==","shasum":"e20b14c2e7c12d37a2de0f312a68e4234a605b42","tarball":"https://registry.npmjs.org/@amrshbib/react-native-haptic-feedback/-/react-native-haptic-feedback-0.0.1.tgz","fileCount":16,"unpackedSize":45951,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIEu7pXLEAv1XK2BbOimR1wrVSJUmJsOFgtQfsYYsPIANAiBU1ap0uCVS1+VnwmGzbzqENw6SuFDJ1GoNQyxRt5ztSQ=="}]},"_npmUser":{"name":"amrshbib","email":"amr.shbib@gmail.com"},"directories":{},"maintainers":[{"name":"amrshbib","email":"amr.shbib@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/react-native-haptic-feedback_0.0.1_1789210105740_0.5559742564121857"},"_hasShrinkwrap":false}},"time":{"created":"2026-09-12T10:48:25.586Z","0.0.1":"2026-09-12T10:48:25.875Z","modified":"2026-09-12T10:48:26.102Z"},"maintainers":[{"name":"amrshbib","email":"amr.shbib@gmail.com"}],"description":"Semantic haptics for React Native. Real Taptic Engine on iOS (selection, impact, notification) and Core Haptics for arbitrary durations; VibrationEffect primitives on Android. One vocabulary, no dead 400ms buzz.","homepage":"https://github.com/amrshbib/react-native-haptic-feedback#readme","keywords":["react-native","haptic","haptics","haptic-feedback","vibration","vibrate","taptic","taptic-engine","core-haptics","ios","android"],"repository":{"type":"git","url":"git+https://github.com/amrshbib/react-native-haptic-feedback.git"},"author":{"name":"Amr Shbib","email":"amr.shbib@mythings.app"},"bugs":{"url":"https://github.com/amrshbib/react-native-haptic-feedback/issues"},"license":"MIT","readme":"# @amrshbib/react-native-haptic-feedback\n\nSemantic haptics for React Native. Nine named events — the ones Apple designed — routed to the\nreal Taptic Engine on iOS and to `VibrationEffect` primitives on Android, plus Core Haptics for\nanything with a duration.\n\nReact Native ships `Vibration`, and on iOS it plays exactly one thing: the 400 ms slab an\nincoming call uses. It ignores durations, ignores patterns, and is far too heavy to put behind\na button. That is why most apps end up with `Platform.OS === \"android\" && Vibration.vibrate(20)`\nand no haptics at all on the platform that has the best ones. This is the fix.\n\n## Install\n\n```sh\nnpm install @amrshbib/react-native-haptic-feedback\ncd ios && pod install\n```\n\nAutolinked on both platforms. Android's `VIBRATE` permission is merged in from the library\nmanifest — there is nothing to add to the app. Rebuild after installing; restarting Metro is not\nenough.\n\n## Use\n\n```js\nimport Haptics, { HAPTIC } from \"@amrshbib/react-native-haptic-feedback\";\n\nHaptics.selection();          // a value changed under the finger\nHaptics.light();              // a tap that did something\nHaptics.success();            // it worked\nHaptics.trigger(HAPTIC.ERROR);\n```\n\n### The nine\n\n| Name | Means | iOS | Android (API 30+) |\n| --- | --- | --- | --- |\n| `selection` | a value changed under the finger — a tab, a chip, a stepper | `UISelectionFeedbackGenerator` | `PRIMITIVE_TICK` @ 0.45 |\n| `impactLight` | something small landed | impact, light | `PRIMITIVE_CLICK` @ 0.5 |\n| `impactMedium` | something landed | impact, medium | `PRIMITIVE_CLICK` @ 0.75 |\n| `impactHeavy` | something heavy landed | impact, heavy | `PRIMITIVE_CLICK` @ 1.0 |\n| `impactSoft` | a dull edge | impact, soft | `PRIMITIVE_TICK` @ 1.0 |\n| `impactRigid` | a hard edge | impact, rigid | `PRIMITIVE_CLICK` @ 1.0 |\n| `notificationSuccess` | it worked | notification, success | two clicks, 90 ms apart |\n| `notificationWarning` | read this | notification, warning | click then tick |\n| `notificationError` | it did not work | notification, error | three clicks |\n\nAndroid falls back a rung at a time — API 30 primitives, then API 29 predefined effects, then an\nAPI 26 waveform with amplitudes, then a bare duration. A caller asking for `selection` on a 2017\nhandset still gets the shortest thing that phone can do.\n\n### Durations\n\n```js\nHaptics.vibrate(30);                 // 30 ms, honoured on both platforms\nHaptics.pattern([0, 40, 80, 40]);    // wait, buzz, wait, buzz\nHaptics.cancel();\n```\n\n`pattern` takes the same shape as React Native's own `Vibration.vibrate` on Android, so patterns\nwritten for it port across — and now run on iOS too, through Core Haptics.\n\n### Waiting\n\n```js\nconst stop = Haptics.pulse({ interval: 900 });\n// …\nstop();\n```\n\nA tick every `interval` until you stop it — for the thing a spinner cannot say, when the phone is\nface-down or the eyes are elsewhere. It stops itself after `limit` ticks (default 30) so a request\nthat never returns cannot leave a phone buzzing in a pocket.\n\n### The switch\n\n```js\nHaptics.setEnabled(false);  // one gate, held in JS, no bridge crossing to say no\n```\n\nWire it to wherever the app keeps the user's answer. Held in JS on purpose: a gate that had to\ncross the bridge to decline would cost a round trip per tap to do nothing.\n\n### Handlers\n\n```js\n<Button onPress={Haptics.haptic(handleSave, HAPTIC.SUCCESS)} />\n```\n\nThe tick fires before the handler — the touch is what is being answered, not the result of it.\n\n### What the device can do\n\n```js\nconst { supported, engine, systemEnabled } = await Haptics.getCapabilities();\n```\n\n`engine` is `\"core-haptics\"`, `\"vibrator\"`, or `\"none\"` — the last meaning no motor at all, which\nis most simulators and every iPad. `systemEnabled` reflects Android's touch-feedback setting;\niOS has no public read for System Haptics and always reports `true`, so do not branch on it — the\nplatform enforces its own setting below this library either way.\n\n### Also\n\n- **`prepare()`** — both platforms power the motor down between uses, and the first tick after\n  idle lands late enough to feel detached from the touch. Call it when a screen that will tick\n  opens; not per tap, which is the same latency moved.\n- **Repeats are collapsed.** Two ticks of the same kind inside 24 ms are one tick — well under a\n  deliberate double-tap, and enough to keep a fast-scrolling list from returning a smear.\n- **A missing native module never throws.** A build that has not been recompiled, a simulator, a\n  web target: every call goes quiet, with one dev-mode warning. Haptics are decoration, and\n  decoration must not be able to take a screen down.\n- **Android honours the system touch-feedback setting** for the nine named events, and not for\n  `vibrate`/`pattern` — turning touch feedback off is a statement about taps, not about the buzz\n  that says a voice recording has started.\n\n## Requirements\n\nReact Native ≥ 0.71 · iOS 13 · Android API 24\n\n## License\n\nMIT © Amr Shbib\n","readmeFilename":"README.md","_rev":"1-98bbcc01f2b1ec943baebf449952f641"}