Skip to main content

Vue Integration

The Vue integration provides a useWebHaptics composable that manages the haptics instance lifecycle with Vue’s reactivity system.

Installation

1

Install the package

npm install @accede-ai/webhaptics
2

Import the composable

import { useWebHaptics } from '@accede-ai/webhaptics/vue';

Basic Usage

The composable automatically initializes on component mount and cleans up on unmount:
<script setup lang="ts">
import { useWebHaptics } from '@accede-ai/webhaptics/vue';

const { trigger, cancel, isSupported } = useWebHaptics();

const handleClick = () => {
  trigger('medium');
};
</script>

<template>
  <button @click="handleClick">
    Click me
    <span v-if="!isSupported">(haptics not supported)</span>
  </button>
</template>

API Reference

useWebHaptics(options?)

Returns an object with haptic control methods.

Parameters

options
WebHapticsOptions
Configuration options for the WebHaptics instance

Return Value

trigger
(input?: HapticInput, options?: TriggerOptions) => void
Triggers a haptic feedback pattern. Accepts:
  • A number (duration in ms)
  • A preset name string ('light', 'medium', 'heavy', 'success', etc.)
  • An array of numbers (alternating on/off durations)
  • An array of Vibration objects
  • A HapticPreset object
cancel
() => void
Cancels any currently playing haptic pattern
isSupported
boolean
Indicates whether haptic feedback is supported in the current environment

Type Definitions

interface Vibration {
  duration: number;    // Duration in milliseconds
  intensity?: number;  // 0-1, controls vibration strength (default: 0.5)
  delay?: number;      // Delay before this vibration in ms
}

type HapticInput = 
  | number              // Simple duration
  | string              // Preset name
  | number[]            // Alternating on/off pattern
  | Vibration[]         // Full control
  | HapticPreset;

interface TriggerOptions {
  intensity?: number;   // Default intensity for vibrations without explicit intensity
}

interface WebHapticsOptions {
  debug?: boolean;
  showSwitch?: boolean;
}

Examples

Form Validation Feedback

<script setup lang="ts">
import { useWebHaptics } from '@accede-ai/webhaptics/vue';
import { ref } from 'vue';

const { trigger } = useWebHaptics();
const email = ref('');
const error = ref('');

const handleSubmit = () => {
  if (!email.value.includes('@')) {
    error.value = 'Invalid email';
    trigger('error');
    return;
  }
  
  error.value = '';
  trigger('success');
  // Submit form...
};
</script>

<template>
  <form @submit.prevent="handleSubmit">
    <input
      v-model="email"
      type="email"
      placeholder="Email"
    />
    <span v-if="error" class="error">{{ error }}</span>
    <button type="submit">Login</button>
  </form>
</template>

Interactive Toggle with Reactive Debug Mode

<script setup lang="ts">
import { useWebHaptics } from '@accede-ai/webhaptics/vue';
import { ref } from 'vue';

const debugMode = ref(false);
const { trigger } = useWebHaptics({ debug: debugMode.value });
const isActive = ref(false);

const handleToggle = () => {
  isActive.value = !isActive.value;
  trigger(isActive.value ? 'medium' : 'light');
};
</script>

<template>
  <div>
    <button @click="handleToggle" :class="{ active: isActive }">
      {{ isActive ? 'ON' : 'OFF' }}
    </button>
    
    <label>
      <input v-model="debugMode" type="checkbox" />
      Debug Mode
    </label>
  </div>
</template>
The debug option is watched reactively. When it changes, the instance updates automatically without needing to recreate the composable.

Slider with Step Feedback

<script setup lang="ts">
import { useWebHaptics } from '@accede-ai/webhaptics/vue';
import { ref, watch } from 'vue';

const { trigger } = useWebHaptics();
const value = ref(50);
const lastStep = ref(5);

watch(value, (newValue) => {
  const currentStep = Math.floor(newValue / 10);
  if (currentStep !== lastStep.value) {
    trigger('selection');
    lastStep.value = currentStep;
  }
});
</script>

<template>
  <div>
    <input
      v-model.number="value"
      type="range"
      min="0"
      max="100"
    />
    <span>{{ value }}%</span>
  </div>
</template>

List Selection with Feedback

<script setup lang="ts">
import { useWebHaptics } from '@accede-ai/webhaptics/vue';
import { ref } from 'vue';

interface Item {
  id: number;
  name: string;
}

const { trigger } = useWebHaptics();
const items = ref<Item[]>([
  { id: 1, name: 'Item 1' },
  { id: 2, name: 'Item 2' },
  { id: 3, name: 'Item 3' },
]);
const selectedId = ref<number | null>(null);

const selectItem = (id: number) => {
  selectedId.value = id;
  trigger('selection');
};
</script>

<template>
  <ul>
    <li
      v-for="item in items"
      :key="item.id"
      @click="selectItem(item.id)"
      :class="{ selected: selectedId === item.id }"
    >
      {{ item.name }}
    </li>
  </ul>
</template>

Custom Haptic Pattern

<script setup lang="ts">
import { useWebHaptics } from '@accede-ai/webhaptics/vue';
import type { Vibration } from '@accede-ai/webhaptics/vue';

const { trigger } = useWebHaptics();

const playCustomPattern = () => {
  const pattern: Vibration[] = [
    { duration: 50, intensity: 0.3 },
    { delay: 100, duration: 50, intensity: 0.6 },
    { delay: 100, duration: 50, intensity: 0.9 },
  ];
  trigger(pattern);
};
</script>

<template>
  <button @click="playCustomPattern">
    Play Custom Pattern
  </button>
</template>

Common Patterns

Composable with Reactive Options

<script setup lang="ts">
import { useWebHaptics } from '@accede-ai/webhaptics/vue';
import { ref, computed } from 'vue';

const isDevelopment = ref(import.meta.env.DEV);

const { trigger } = useWebHaptics({
  debug: isDevelopment.value,
  showSwitch: true
});
</script>

Notification System

<script setup lang="ts">
import { useWebHaptics } from '@accede-ai/webhaptics/vue';
import { ref, watch } from 'vue';

const { trigger, isSupported } = useWebHaptics();
const notifications = ref<string[]>([]);

watch(() => notifications.value.length, (newCount, oldCount) => {
  if (newCount > oldCount && isSupported) {
    trigger('nudge');
  }
});

const addNotification = (message: string) => {
  notifications.value.push(message);
};
</script>

<template>
  <div>
    <button @click="addNotification('New message!')">Send Notification</button>
    <ul>
      <li v-for="(notification, index) in notifications" :key="index">
        {{ notification }}
      </li>
    </ul>
  </div>
</template>

Long Press Button

<script setup lang="ts">
import { useWebHaptics } from '@accede-ai/webhaptics/vue';
import { ref } from 'vue';

const { trigger } = useWebHaptics();
const isPressed = ref(false);
let pressTimer: number;

const handleMouseDown = () => {
  isPressed.value = true;
  trigger('light');
  
  pressTimer = window.setTimeout(() => {
    trigger('heavy');
    handleLongPress();
  }, 500);
};

const handleMouseUp = () => {
  isPressed.value = false;
  clearTimeout(pressTimer);
};

const handleLongPress = () => {
  console.log('Long press detected!');
};
</script>

<template>
  <button
    @mousedown="handleMouseDown"
    @mouseup="handleMouseUp"
    @mouseleave="handleMouseUp"
    :class="{ pressed: isPressed }"
  >
    Hold me
  </button>
</template>

Available Presets

trigger('success')  // Ascending double-tap
trigger('warning')  // Two taps with hesitation
trigger('error')    // Three rapid harsh taps
The composable automatically handles lifecycle management using Vue’s onMounted and onUnmounted hooks. The debug option is watched reactively, so changes will automatically update the WebHaptics instance.

Build docs developers (and LLMs) love