diff --git a/docs/useVibrate.md b/docs/useVibrate.md
new file mode 100644
index 00000000..e69de29b
diff --git a/src/__stories__/useVibrate.story.tsx b/src/__stories__/useVibrate.story.tsx
new file mode 100644
index 00000000..f521771c
--- /dev/null
+++ b/src/__stories__/useVibrate.story.tsx
@@ -0,0 +1,20 @@
+import { storiesOf } from '@storybook/react';
+import * as React from 'react';
+import { useVibrate, useToggle } from '..';
+import ShowDocs from './util/ShowDocs';
+
+const Demo = () => {
+ const [vibrating, toggleVibrating] = useToggle(false);
+
+ useVibrate(vibrating, [300, 100, 200, 100, 1000, 300]);
+
+ return (
+
+
+
+ );
+};
+
+storiesOf('UI|useVibrate', module)
+ .add('Docs', () => )
+ .add('Demo', () => );
diff --git a/src/index.ts b/src/index.ts
index 65872134..745dae63 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -86,6 +86,7 @@ export { default as useUnmount } from './useUnmount';
export { default as useUpdate } from './useUpdate';
export { default as useUpdateEffect } from './useUpdateEffect';
export { default as useUpsert } from './useUpsert';
+export { default as useVibrate } from './useVibrate';
export { default as useVideo } from './useVideo';
export { default as useStateValidator } from './useStateValidator';
export { useWait, Waiter } from './useWait';
diff --git a/src/useVibrate.ts b/src/useVibrate.ts
new file mode 100644
index 00000000..23f7c619
--- /dev/null
+++ b/src/useVibrate.ts
@@ -0,0 +1,37 @@
+import { useEffect } from 'react';
+
+export type VibrationPattern = number | number[];
+
+const isVibrationApiSupported = typeof navigator === 'object' && 'vibrate' in navigator;
+
+const useVibrateMock = () => {};
+
+function useVibrate(enabled: boolean = true, pattern: VibrationPattern = [1000, 1000], loop: boolean = true): void {
+ useEffect(() => {
+ let interval;
+
+ if (enabled) {
+ navigator.vibrate(pattern);
+
+ if (loop) {
+ const duration = pattern instanceof Array ? pattern.reduce((a, b) => a + b) : (pattern as number);
+
+ interval = setInterval(() => {
+ navigator.vibrate(pattern);
+ }, duration);
+ }
+ }
+
+ return () => {
+ if (enabled) {
+ navigator.vibrate(0);
+
+ if (loop) {
+ clearInterval(interval);
+ }
+ }
+ };
+ }, [enabled]);
+}
+
+export default isVibrationApiSupported ? useVibrate : useVibrateMock;