vue-haptics

Haptic feedback for Vue 3 that also works on iOS Safari, where the Vibration API is not available
712 1
Install
npm install vue-haptics
Latest Version:4.0.0
Vue:3
License:MIT
Last Updated:Oct 7, 2026
Links: GitHub  ·  npm
Maintainer: artouc

vue-haptics

Haptic feedback for Vue 3 that also works on iOS Safari, where the Vibration API is not available.
Vibration API が使えない iOS Safari でも動く、Vue 3 向けの触覚フィードバックです。

Read more in English · 日本語で読む

English

Important: vue-haptics requires Vue 3.3 or later. A tap on an element with v-haptic plays one haptic tick on Android browsers with the Vibration API and on iOS Safari 18 or later; elsewhere, it does nothing. v4.0.0 removed vibration patterns and calling haptics from script, because iOS 26.5 and later play haptics only for a real tap.

Quick start

  1. Install the package.
npm install vue-haptics
  1. Register the plugin.
import { createApp } from "vue"
import { createHaptics } from "vue-haptics"
import App from "./App.vue"

createApp(App).use(createHaptics()).mount("#app")
  1. Add v-haptic to a button.
<template>
  <button v-haptic @click="save">Save</button>
</template>
  1. Open the page on a phone and tap the button. You should feel a short tick on Android and on iPhone.

Tip: Pass disabled: () => !settings.haptics to createHaptics() to let users turn haptics off across the whole app.

Technology

Haptics on iOS Safari without the Vibration API

iOS Safari does not implement navigator.vibrate(), but it plays a system haptic when the user toggles an <input type="checkbox" switch>. Since iOS 26.5, a switch toggled from script, including through label.click(), plays nothing; only a real tap does. So on iOS, v-haptic places a transparent label holding a switch over the element, and the user's own tap lands on that label.

Environment How it plays
Android Chrome and other browsers with the Vibration API navigator.vibrate(10) on click
iOS Safari 18 or later A transparent label over the element toggles a switch on each tap
Others, and SSR Does nothing

The idea of using <input switch> comes from use-haptic by Asuma Yamada, and the overlay that works on iOS 26.5 follows ios-haptics. vue-haptics started as a Vue port of use-haptic.

The overlay stays out of the way

The tap still reaches the element's own click handlers exactly once. The switch is kept out from under the finger, so a gesture that starts on the element can still scroll the page. If Vue rewrites the element's children, the overlay is put back, and it is removed when the element unmounts.

SSR safe

The directive renders nothing on the server and only touches the DOM after mount. isHapticsSupported() returns false during SSR.

Published with npm provenance

Releases are built and published from GitHub Actions with npm Trusted Publishing, so no npm token exists anywhere. Each version carries a provenance statement that links it to the commit and workflow run it was built from. The build job and the job that holds the publishing credential are separate, so a compromised dependency cannot publish a fake package. The workflow only stages each version, and it reaches users after a maintainer approves it with two-factor authentication.

Specification

v-haptic
Usage Behavior
v-haptic Plays one tick when the element is tapped
v-haptic="false" Does nothing. true turns it back on

On iOS, an element with position: static gets position: relative so the overlay can cover it, and its original position is restored on unmount. The overlay covers the whole element, so use v-haptic on controls such as buttons rather than on containers with links or inputs inside. In click handlers, event.target can be the overlay; use event.currentTarget to get the element.

Exports
Export Description
vHaptic The directive. Import it in <script setup> when you do not use the plugin
createHaptics(options?) Plugin that registers the directive globally
createHapticDirective(options?) Builds a directive with its own options
isHapticsSupported() Whether the device can play haptics. false during SSR
Option Type Description
disabled MaybeRefOrGetter<boolean> Suppresses haptics while true. Read at the moment of each tap
directive string | false createHaptics only. Name of the directive, "haptic" by default. false skips registration
Migrating from v3
v3 v4
v-haptic="40", v-haptic="[10, 60, 10]" v-haptic. Every tap plays one tick
v-haptic:pointerdown v-haptic. Haptics play on click
haptic(), useHaptics().trigger() Removed. Put v-haptic on the element the user taps
useHaptics().isSupported isHapticsSupported() after mount
pattern option, HapticPattern, DEFAULT_PATTERN, HAPTICS_KEY Removed

日本語

重要:vue-haptics には Vue 3.3 以上が必要です。v-haptic を付けた要素をタップすると、Vibration API のある Android のブラウザと iOS Safari 18 以上で、触覚フィードバックが1回鳴ります。それ以外の環境では何も起きません。iOS 26.5 以降は実際のタップでしか鳴らないため、v4.0.0 で振動のパターンとスクリプトから鳴らす機能を廃止しました。

クイックスタート

  1. パッケージをインストールします。
npm install vue-haptics
  1. プラグインを登録します。
import { createApp } from "vue"
import { createHaptics } from "vue-haptics"
import App from "./App.vue"

createApp(App).use(createHaptics()).mount("#app")
  1. ボタンに v-haptic を付けます。
<template>
  <button v-haptic @click="save">Save</button>
</template>
  1. スマホでページを開いてボタンを押します。Android でも iPhone でも、短い振動が返ってくれば動いています。

ヒント:createHaptics() に disabled: () => !settings.haptics を渡すと、アプリ全体の触覚フィードバックを利用者の設定でオフにできます。

テクノロジー

Vibration API のない iOS Safari でも鳴らせます

iOS Safari は navigator.vibrate() を実装していませんが、利用者が <input type="checkbox" switch> を切り替えるとシステムの触覚フィードバックを鳴らします。iOS 26.5 からは、label.click() を含めスクリプトから切り替えても鳴らず、実際のタップでしか鳴りません。そこで iOS では、v-haptic が要素の上にスイッチを持つ透明な label を重ね、利用者のタップがその label に直接当たるようにしています。

環境 鳴らし方
Android Chrome など Vibration API のあるブラウザ クリックで navigator.vibrate(10)
iOS Safari 18 以上 要素に重ねた透明な label が、タップごとにスイッチを切り替えます
それ以外と SSR 何もしません

<input switch> を使う発想は Asuma Yamada さんの use-haptic によるもので、iOS 26.5 でも動く重ね方は ios-haptics に倣っています。vue-haptics は use-haptic の Vue への移植として始まりました。

重ねた label は操作の邪魔をしません

タップは、要素の click のハンドラにもちょうど1回届きます。スイッチは指の下に置かないので、要素の上で始めた操作でもページをスクロールできます。Vue が要素の子を書き換えても label は付け直され、要素がアンマウントされると取り除かれます。

SSR でも安全に使えます

ディレクティブはサーバーでは何も出力せず、マウントした後にだけ DOM に触ります。isHapticsSupported() は SSR 中は false を返します。

npm の provenance 付きで公開しています

リリースは GitHub Actions から npm の Trusted Publishing でビルドして公開するので、npm のトークンはどこにも存在しません。各バージョンには、どのコミットとワークフローの実行からビルドしたかを示す provenance が付きます。ビルドするジョブと公開の証明書を持つジョブを分けているので、依存のどれかが乗っ取られても偽のパッケージは公開できません。ワークフローは各バージョンを段階公開するだけで、メンテナーが 2 要素認証を使って承認してから利用者に届きます。

仕様

v-haptic
書き方 動作
v-haptic 要素をタップすると1回鳴らします
v-haptic="false" 何もしません。true に戻すと鳴らします

iOS では、label で覆えるよう position: static の要素に position: relative を付け、アンマウントすると元に戻します。label は要素全体を覆うので、v-haptic はボタンのような操作の部品に付け、リンクや入力欄を中に持つ入れ物には付けないでください。click のハンドラでは event.target が label になることがあるので、要素は event.currentTarget で取ります。

export
export 説明
vHaptic ディレクティブです。プラグインを使わないときは <script setup> で import します
createHaptics(options?) ディレクティブを全体に登録するプラグインです
createHapticDirective(options?) 独自のオプションを持つディレクティブを作ります
isHapticsSupported() 端末が触覚フィードバックを鳴らせるかを返します。SSR 中は false です
オプション 型 説明
disabled MaybeRefOrGetter<boolean> true の間は鳴らしません。タップした時点の値で判断します
directive string | false createHaptics だけで使います。ディレクティブの名前で、既定は "haptic" です。false なら登録しません
v3 からの移行
v3 v4
v-haptic="40"、v-haptic="[10, 60, 10]" v-haptic。タップごとに1回鳴ります
v-haptic:pointerdown v-haptic。クリックで鳴ります
haptic()、useHaptics().trigger() 廃止しました。利用者がタップする要素に v-haptic を付けます
useHaptics().isSupported マウント後に isHapticsSupported()
pattern オプション、HapticPattern、DEFAULT_PATTERN、HAPTICS_KEY 廃止しました

Related Packages

@mxerf/tappt

Tiny, SSR-safe haptic feedback for the web. Works in Telegram Mini Apps, iOS Saf...

1,487 0
@cornerkit/vue

Vue 3 components, composables, and directives for iOS-style squircle corners

1,491 13
@nativescript-community/ui-checkbox

Checkbox: Material 3 MaterialCheckBox on Android, SF Symbols toggle button on iO...

7,793 1
@nativescript-community/perms

An unified permissions API for NativeScript on iOS and Android.

171,921 15
@nativescript-community/ui-canvas

Implement Canvas into your NativeScript apps.

45,311 38

Version History

Version Released Vue License
4.0.0 ^3.3.0 MIT
3.0.0 ^3.3.0 MIT
2.0.2 ^3.0.0 MIT
2.0.1 ^3.0.0 MIT