useVibration
A specialized device hook for triggering device vibrations, safely handling unsupported environments.
Installation
npx shadcn@latest add https://r.fiberui.com/r/hooks/use-vibration.jsonA React hook that provides access to the Vibration API, allowing you to trigger haptic feedback loops. Perfect for mobile games, notifications, or interactive UI elements.
Browser Support
Checks for isSupported. The Vibration API is primarily supported on mobile
devices and may not have any effect on desktop browsers or devices without
vibration hardware.
Features
- Trigger Vibration - Create simple or complex vibration patterns
- Type Safe - Fully typed for TypeScript
- SSR Safe - Works safely with server-side rendering
Basic Usage
Basic Example
Basic Vibration
Trigger a simple 200ms haptic feedback.
Vibration API is not supported in this browser/device.
"use client";
import { useVibration } from "@repo/hooks/device/use-vibration";
import { Button } from "@repo/ui/components/button";
import { Vibrate } from "lucide-react";
export function Example1() {
const { vibrate, isSupported } = useVibration();
return (
<div className="flex flex-col items-center justify-center gap-4">
<div className="text-center">
<h3 className="text-lg font-medium">Basic Vibration</h3>
<p className="text-muted-foreground text-sm">
Trigger a simple 200ms haptic feedback.
</p>
</div>
<Button
onClick={() => vibrate(200)}
isDisabled={!isSupported}
className="gap-2"
>
<Vibrate className="h-4 w-4" />
Vibrate Device
</Button>
{!isSupported && (
<p className="text-destructive mt-2 text-sm">
Vibration API is not supported in this browser/device.
</p>
)}
</div>
);
}
Vibration Patterns
Vibration Patterns
Pass an array of numbers to define complex vibration sequences.
"use client";
import { useVibration } from "@repo/hooks/device/use-vibration";
import { Button } from "@repo/ui/components/button";
import { AlertTriangle, CheckCircle, Info } from "lucide-react";
export function Example2() {
const { vibrate, isSupported } = useVibration();
// Vibrate patterns defined in milliseconds: [vibrate, pause, vibrate, pause, ...]
const patterns = {
success: [100, 50, 100], // Short double tap
error: [500, 100, 500], // Two long vibrations
sos: [
100, 100, 100, 100, 100, 100, 300, 100, 300, 100, 300, 100, 100,
100, 100,
], // ... --- ...
};
return (
<div className="flex flex-col items-center justify-center gap-6">
<div className="text-center">
<h3 className="text-lg font-medium">Vibration Patterns</h3>
<p className="text-muted-foreground max-w-sm text-sm">
Pass an array of numbers to define complex vibration
sequences.
</p>
</div>
<div className="flex flex-wrap items-center justify-center gap-4">
<Button
variant="outline"
onClick={() => vibrate(patterns.success)}
isDisabled={!isSupported}
className="gap-2 border-green-500/20 text-green-600 hover:bg-green-500/10 dark:text-green-400 dark:hover:bg-green-500/20"
>
<CheckCircle className="h-4 w-4" />
Success Match
</Button>
<Button
variant="outline"
onClick={() => vibrate(patterns.error)}
isDisabled={!isSupported}
className="gap-2 border-red-500/20 text-red-600 hover:bg-red-500/10 dark:text-red-400 dark:hover:bg-red-500/20"
>
<AlertTriangle className="h-4 w-4" />
Error Alert
</Button>
<Button
variant="outline"
onClick={() => vibrate(patterns.sos)}
isDisabled={!isSupported}
className="gap-2"
>
<Info className="h-4 w-4" />
SOS Signal
</Button>
</div>
<Button
variant="ghost"
onClick={() => vibrate(0)} // Pass 0 to stop
isDisabled={!isSupported}
className="text-muted-foreground mt-2 text-sm"
>
Stop Current Vibration
</Button>
</div>
);
}
Game Haptics
Game Haptics
Combine actions with rapid haptic feedback.
Requires a mobile device with vibration support.
"use client";
import { useState, useRef } from "react";
import { useVibration } from "@repo/hooks/device/use-vibration";
import { Button } from "@repo/ui/components/button";
import { Crosshair, Target } from "lucide-react";
export function Example3() {
const { vibrate, isSupported } = useVibration();
const [score, setScore] = useState(0);
const targetRef = useRef<HTMLDivElement>(null);
const handleShoot = () => {
// Simple haptic feedback on every click/shot
vibrate(50);
// Randomly hit or miss
const hit = Math.random() > 0.5;
if (hit) {
setScore((s) => s + 10);
// Strong feedback for a hit!
vibrate([100, 50, 200]);
// Visual feedback
if (targetRef.current) {
targetRef.current.animate(
[
{ transform: "scale(1)", color: "inherit" },
{ transform: "scale(1.2)", color: "green" },
{ transform: "scale(1)", color: "inherit" },
],
{ duration: 300 },
);
}
}
};
return (
<div className="flex flex-col items-center justify-center gap-6">
<div className="text-center">
<h3 className="text-lg font-medium">Game Haptics</h3>
<p className="text-muted-foreground max-w-sm text-sm">
Combine actions with rapid haptic feedback.
</p>
</div>
<div className="flex items-center gap-4">
<span className="text-2xl font-bold tracking-tight">
Score: {score}
</span>
<Button variant="outline" size="sm" onClick={() => setScore(0)}>
Reset
</Button>
</div>
<div
ref={targetRef}
className="border-primary/50 relative flex items-center justify-center rounded-full border-2 border-dashed p-12"
>
<Target className="absolute h-16 w-16 opacity-20" />
<Button
size="lg"
className="relative z-10 h-24 w-24 rounded-full"
onClick={handleShoot}
isDisabled={!isSupported}
>
<Crosshair className="h-8 w-8" />
</Button>
</div>
{!isSupported && (
<p className="text-destructive text-sm">
Requires a mobile device with vibration support.
</p>
)}
</div>
);
}
Form Feedback
"use client";
import { useState } from "react";
import { useVibration } from "@repo/hooks/device/use-vibration";
import { Button } from "@repo/ui/components/button";
import { Input } from "@repo/ui/components/input";
import { Label } from "@repo/ui/components/label";
import { Card } from "@repo/ui/components/card";
export function Example4() {
const { vibrate } = useVibration();
const [pin, setPin] = useState("");
const [error, setError] = useState(false);
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
if (pin.length !== 4) {
setError(true);
// Quick error buzz to alert user
vibrate([50, 50, 50, 50]);
// Clear error state after a moment
setTimeout(() => setError(false), 2000);
return;
}
// Success hum
setError(false);
vibrate(200);
setPin("");
alert("PIN Accepted!");
};
const handleInput = (val: string) => {
// Light tap when typing
vibrate(10);
setPin(val);
};
return (
<Card className="mx-auto w-full max-w-sm p-6">
<form onSubmit={handleSubmit} className="flex flex-col gap-6">
<div className="text-center">
<h3 className="text-lg font-medium">Form Feedback</h3>
<p className="text-muted-foreground text-sm">
Submit an invalid PIN to trigger error vibration.
</p>
</div>
<div className="flex flex-col gap-3">
<Label htmlFor="pin">Enter 4-digit PIN</Label>
<Input
id="pin"
type="password"
inputMode="numeric"
maxLength={4}
placeholder="••••"
value={pin}
onChange={(e) => handleInput(e.target.value)}
className={`text-center text-xl tracking-widest transition-all ${
error
? "animate-in slide-in-from-left-2 border-red-500 ring-1 ring-red-500"
: ""
}`}
/>
{error && (
<p className="text-destructive text-xs">
PIN must be exactly 4 digits
</p>
)}
</div>
<Button type="submit" className="w-full">
Verify
</Button>
</form>
</Card>
);
}
API Reference
Hook Signature
function useVibration(): UseVibrationReturn;Return Value
| Property | Type | Description |
|---|---|---|
vibrate | (pattern?: number | number[]) => void | Triggers a vibration pattern |
isSupported | boolean | true if Vibration API is supported |
Hook Source Code
import { useCallback } from "react";
/**
* Provides a method to trigger device vibration.
* Safe to use in environments where vibration is not supported.
*/
export function useVibration() {
const vibrate = useCallback((pattern: number | number[] = 200) => {
if (
typeof window !== "undefined" &&
typeof navigator !== "undefined" &&
"vibrate" in navigator
) {
// Return value is boolean in supported browsers
try {
navigator.vibrate(pattern);
} catch (e) {
console.warn("Vibration API failed", e);
}
}
}, []);
const isSupported =
typeof window !== "undefined" &&
typeof navigator !== "undefined" &&
"vibrate" in navigator;
return { vibrate, isSupported };
}
useNetwork
A hook that monitors network connectivity status. Provides real-time updates on online/offline state and connection details like effective type and downlink speed.
useWakeLock
A specialized device hook that leverages the Screen Wake Lock API to prevent the device screen from dimming or turning off during usage.