useDeviceOrientation
A comprehensive hook to access the device's physical orientation and tilt angles (alpha, beta, gamma) relative to the Earth's frame of reference.
Installation
npx shadcn@latest add https://r.fiberui.com/r/hooks/use-device-orientation.jsonA React hook that provides real-time access to the device's physical orientation using the DeviceOrientation Event. Used for games, parallax effects, or augmented reality.
Browser Support
Checks for isSupported. Use requestPermission for iOS 13+ devices which
require user interaction to grant access to motion sensors.
Features
- Real-time Updates - Tracks alpha, beta, and gamma rotation values
- IOS Support - Handles permission request flow for iOS 13+
- Type Safe - Fully typed return values
- SSR Safe - Works safely with server-side rendering
Basic Usage
Display the current device orientation data. (Try this on a mobile device!)
"use client";
import { useDeviceOrientation } from "@repo/hooks/device/use-device-orientation";
import { Button } from "@repo/ui/components/button";
import { Smartphone } from "lucide-react";
export function Example1() {
const { orientation, requestPermission, error } = useDeviceOrientation();
// Round values for display
const alpha = orientation.alpha ? Math.round(orientation.alpha) : 0;
const beta = orientation.beta ? Math.round(orientation.beta) : 0;
const gamma = orientation.gamma ? Math.round(orientation.gamma) : 0;
return (
<div className="flex flex-col gap-4">
<div className="grid grid-cols-3 gap-4 text-center">
<div className="rounded-lg border p-4">
<div className="text-2xl font-bold">{alpha}°</div>
<div className="text-muted-foreground text-xs">
Alpha (Z)
</div>
</div>
<div className="rounded-lg border p-4">
<div className="text-2xl font-bold">{beta}°</div>
<div className="text-muted-foreground text-xs">
Beta (X)
</div>
</div>
<div className="rounded-lg border p-4">
<div className="text-2xl font-bold">{gamma}°</div>
<div className="text-muted-foreground text-xs">
Gamma (Y)
</div>
</div>
</div>
{error && (
<div className="text-destructive text-sm">{error.message}</div>
)}
<Button onClick={requestPermission} variant="outline">
<Smartphone className="mr-2 h-4 w-4" />
Request Permission (iOS)
</Button>
</div>
);
}
API Reference
Hook Signature
function useDeviceOrientation(): UseDeviceOrientationReturn;Return Value
| Property | Type | Description |
|---|---|---|
orientation | DeviceOrientationState | Object containing alpha, beta, gamma values |
error | Error | null | Error object if permission denied or unsupported |
requestPermission | () => Promise<boolean> | Request permission (iOS 13+). Returns true if granted |
isSupported | boolean | true if DeviceOrientation API is available |
DeviceOrientationState
interface DeviceOrientationState {
alpha: number | null; // Z axis (0 to 360)
beta: number | null; // X axis (-180 to 180) - Front/Back Tilt
gamma: number | null; // Y axis (-90 to 90) - Left/Right Tilt
absolute: boolean; // Whether values are absolute (Earth coords)
}Hook Source Code
import { useState, useCallback, useEffect } from "react";
/**
* Orientation data return type
*/
export interface DeviceOrientationState {
/** The correlation of the device's Z axis to the Earth's Z axis (0 to 360) */
alpha: number | null;
/** The inclination value (forward vs backward) (-180 to 180) */
beta: number | null;
/** The inclination value (left vs right) (-90 to 90) */
gamma: number | null;
/** Whether the data provided is absolute */
absolute: boolean;
}
/**
* Return type for the useDeviceOrientation hook
*/
export interface UseDeviceOrientationReturn {
/** Current orientation state */
orientation: DeviceOrientationState;
/** Error state if any */
error: Error | null;
/** Request permission (required for iOS 13+) */
requestPermission: () => Promise<boolean>;
/** Whether the API is supported */
isSupported: boolean;
}
// Add iOS-specific types
interface DeviceOrientationEventiOS extends DeviceOrientationEvent {
requestPermission?: () => Promise<"granted" | "denied">;
}
/**
* A React hook that provides access to the device's physical orientation.
* Handles permission requests for iOS 13+ devices.
*
* @returns UseDeviceOrientationReturn object with orientation data and controls
*/
export function useDeviceOrientation(): UseDeviceOrientationReturn {
const [orientation, setOrientation] = useState<DeviceOrientationState>({
alpha: null,
beta: null,
gamma: null,
absolute: false,
});
const [error, setError] = useState<Error | null>(null);
// Check support
const isSupported =
typeof window !== "undefined" && "DeviceOrientationEvent" in window;
// Handle orientation change
const handleOrientation = useCallback((event: DeviceOrientationEvent) => {
setOrientation({
alpha: event.alpha,
beta: event.beta,
gamma: event.gamma,
absolute: event.absolute,
});
}, []);
// Request permission (iOS 13+)
const requestPermission = useCallback(async (): Promise<boolean> => {
if (!isSupported) {
setError(new Error("DeviceOrientationEvent is not supported"));
return false;
}
try {
// Check for iOS 13+ permission API
const EventCast =
DeviceOrientationEvent as unknown as DeviceOrientationEventiOS;
if (typeof EventCast.requestPermission === "function") {
const state = await EventCast.requestPermission();
if (state === "granted") {
window.addEventListener(
"deviceorientation",
handleOrientation,
);
return true;
} else {
setError(new Error("Permission denied"));
return false;
}
}
// Non-iOS 13+ devices don't need permission
// Or if permission was already granted previously
return true;
} catch (err) {
const error =
err instanceof Error
? err
: new Error("Failed to request permission");
setError(error);
return false;
}
}, [isSupported, handleOrientation]);
useEffect(() => {
if (!isSupported) return;
// On mount, try to add listener (works if no permission needed or already granted)
// For iOS 13+, this might do nothing until requestPermission is called
window.addEventListener("deviceorientation", handleOrientation);
return () => {
window.removeEventListener("deviceorientation", handleOrientation);
};
}, [isSupported, handleOrientation]);
return {
orientation,
error,
requestPermission,
isSupported,
};
}
export default useDeviceOrientation;
useBattery
A React hook for monitoring device battery status. Provides real-time updates on charging state, level, and charging/discharging times via the Battery Status API.
useGeolocation
A React hook for accessing the Geolocation API. Retrieves current coordinates, handles permission states, and tracks position changes in real-time.