useHooks.iov4.1.2
DocsBlogGitHub
Hooks
No hooks found in any category.

useBluetooth

browser

Installation

npx usehooks-cli@latest add use-bluetooth

Description

A comprehensive hook for interacting with Bluetooth Low Energy devices using the Web Bluetooth API. Provides device discovery, connection management, and GATT characteristic operations.

Parameters

NameTypeDefaultDescription
options?UseBluetoothOptions-Default options for device requests

Parameter Properties

options properties:

NameTypeDescription
filters?BluetoothLEScanFilter[]Filters to apply when scanning for devices
optionalServices?BluetoothServiceUUID[]Optional services to access on the device
acceptAllDevices?booleanWhether to accept all devices (used when no filters specified)

Return Type

UseBluetoothReturn
PropertyTypeDescription
deviceBluetoothDeviceInfo | nullInformation about the currently selected device
isSupportedbooleanWhether Web Bluetooth API is supported
isAvailableboolean | nullWhether Bluetooth is available on the device
isConnectingbooleanWhether a connection attempt is in progress
errorstring | nullError message if any operation failed
requestDevice(options?: BluetoothRequestDeviceOptions) => Promise<BluetoothDeviceInfo | null>Request access to a Bluetooth device
connect() => Promise<boolean>Connect to the selected device
disconnect() => Promise<void>Disconnect from the current device
readCharacteristic(serviceUuid: BluetoothServiceUUID, characteristicUuid: BluetoothServiceUUID) => Promise<DataView | null>Read a value from a GATT characteristic
writeCharacteristic(serviceUuid: BluetoothServiceUUID, characteristicUuid: BluetoothServiceUUID, value: BufferSource) => Promise<boolean>Write a value to a GATT characteristic
startNotifications(serviceUuid: BluetoothServiceUUID, characteristicUuid: BluetoothServiceUUID, callback: (value: DataView) => void) => Promise<boolean>Start receiving notifications from a characteristic
stopNotifications(serviceUuid: BluetoothServiceUUID, characteristicUuid: BluetoothServiceUUID) => Promise<boolean>Stop receiving notifications from a characteristic

Examples

Basic Device Connection

Connect to a Bluetooth device and check connection status

1import { useBluetooth } from '@usehooks/use-bluetooth'; 2 3function BluetoothConnector() { 4 const { 5 device, 6 isSupported, 7 isConnecting, 8 error, 9 requestDevice, 10 connect, 11 disconnect 12 } = useBluetooth(); 13 14 const handleRequestDevice = async () => { 15 await requestDevice({ 16 acceptAllDevices: true 17 }); 18 }; 19 20 const handleConnect = async () => { 21 const success = await connect(); 22 if (success) { 23 console.log('Connected successfully'); 24 } 25 }; 26 27 if (!isSupported) { 28 return <div>Web Bluetooth not supported</div>; 29 } 30 31 return ( 32 <div> 33 <button onClick={handleRequestDevice} disabled={isConnecting}> 34 Select Device 35 </button> 36 37 {device && ( 38 <div> 39 <p>Device: {device.name || 'Unknown'}</p> 40 <p>Status: {device.connected ? 'Connected' : 'Disconnected'}</p> 41 42 {!device.connected ? ( 43 <button onClick={handleConnect} disabled={isConnecting}> 44 {isConnecting ? 'Connecting...' : 'Connect'} 45 </button> 46 ) : ( 47 <button onClick={disconnect}>Disconnect</button> 48 )} 49 </div> 50 )} 51 52 {error && <p>Error: {error}</p>} 53 </div> 54 ); 55}

Heart Rate Monitor

Connect to a heart rate monitor and read heart rate data

1import { useBluetooth } from '@usehooks/use-bluetooth'; 2import { useState, useEffect } from 'react'; 3 4function HeartRateMonitor() { 5 const { 6 device, 7 requestDevice, 8 connect, 9 readCharacteristic, 10 startNotifications 11 } = useBluetooth(); 12 const [heartRate, setHeartRate] = useState<number | null>(null); 13 14 const connectToHeartRateMonitor = async () => { 15 // Request heart rate monitor 16 await requestDevice({ 17 filters: [{ services: ['heart_rate'] }] 18 }); 19 }; 20 21 useEffect(() => { 22 if (device?.connected) { 23 // Start heart rate notifications 24 startNotifications( 25 'heart_rate', 26 'heart_rate_measurement', 27 (value) => { 28 // Parse heart rate value (first byte after flags) 29 const rate = value.getUint8(1); 30 setHeartRate(rate); 31 } 32 ); 33 } 34 }, [device?.connected, startNotifications]); 35 36 return ( 37 <div> 38 <button onClick={connectToHeartRateMonitor}> 39 Connect Heart Rate Monitor 40 </button> 41 42 {device && ( 43 <div> 44 <p>Device: {device.name}</p> 45 {!device.connected ? ( 46 <button onClick={connect}>Connect</button> 47 ) : ( 48 <div> 49 <p>Status: Connected</p> 50 {heartRate && ( 51 <p>Heart Rate: {heartRate} BPM</p> 52 )} 53 </div> 54 )} 55 </div> 56 )} 57 </div> 58 ); 59}

Custom Service Communication

Read from and write to custom GATT characteristics

1import { useBluetooth } from '@usehooks/use-bluetooth'; 2import { useState } from 'react'; 3 4function CustomDeviceController() { 5 const { 6 device, 7 requestDevice, 8 connect, 9 readCharacteristic, 10 writeCharacteristic 11 } = useBluetooth(); 12 const [data, setData] = useState<string>(''); 13 const [inputValue, setInputValue] = useState(''); 14 15 const SERVICE_UUID = '12345678-1234-1234-1234-123456789abc'; 16 const READ_CHAR_UUID = '87654321-4321-4321-4321-cba987654321'; 17 const WRITE_CHAR_UUID = '11111111-2222-3333-4444-555555555555'; 18 19 const connectToDevice = async () => { 20 await requestDevice({ 21 filters: [{ services: [SERVICE_UUID] }], 22 optionalServices: [SERVICE_UUID] 23 }); 24 }; 25 26 const readData = async () => { 27 const value = await readCharacteristic(SERVICE_UUID, READ_CHAR_UUID); 28 if (value) { 29 const decoder = new TextDecoder(); 30 const text = decoder.decode(value); 31 setData(text); 32 } 33 }; 34 35 const writeData = async () => { 36 const encoder = new TextEncoder(); 37 const data = encoder.encode(inputValue); 38 const success = await writeCharacteristic(SERVICE_UUID, WRITE_CHAR_UUID, data); 39 if (success) { 40 console.log('Data written successfully'); 41 setInputValue(''); 42 } 43 }; 44 45 return ( 46 <div> 47 <button onClick={connectToDevice}>Select Device</button> 48 49 {device && !device.connected && ( 50 <button onClick={connect}>Connect</button> 51 )} 52 53 {device?.connected && ( 54 <div> 55 <button onClick={readData}>Read Data</button> 56 <p>Read Value: {data}</p> 57 58 <div> 59 <input 60 value={inputValue} 61 onChange={(e) => setInputValue(e.target.value)} 62 placeholder="Enter data to write" 63 /> 64 <button onClick={writeData}>Write Data</button> 65 </div> 66 </div> 67 )} 68 </div> 69 ); 70}

Dependencies

react

Notes

  • Only available in browsers that support Web Bluetooth API (Chrome 56+, Edge 79+)
  • Requires HTTPS in production environments
  • User gesture required to initiate device requests
  • Device permissions are persistent across sessions
  • Automatically handles disconnection events and cleanup
  • GATT characteristics are cached for performance

Implementation

1"use client"; 2 3import { useState, useCallback, useRef, useEffect } from "react"; 4 5// Extend Navigator interface for Web Bluetooth API 6declare global { 7 interface Navigator { 8 bluetooth?: { 9 requestDevice(options: BluetoothRequestDeviceOptions): Promise<{ 10 id: string; 11 name: string | undefined; 12 gatt?: { 13 connect(): Promise<any>; 14 connected: boolean; 15 }; 16 addEventListener( 17 type: string, 18 listener: EventListener, 19 options?: boolean | AddEventListenerOptions 20 ): void; 21 }>; 22 getAvailability(): Promise<boolean>; 23 }; 24 } 25} 26 27interface BluetoothRequestDeviceOptions { 28 filters?: BluetoothLEScanFilter[]; 29 optionalServices?: BluetoothServiceUUID[]; 30 acceptAllDevices?: boolean; 31} 32 33interface BluetoothLEScanFilter { 34 services?: BluetoothServiceUUID[]; 35 name?: string; 36 namePrefix?: string; 37 manufacturerData?: BluetoothManufacturerDataFilter[]; 38} 39 40interface BluetoothManufacturerDataFilter { 41 companyIdentifier: number; 42 dataPrefix?: BufferSource; 43 mask?: BufferSource; 44} 45 46type BluetoothServiceUUID = number | string; 47 48interface BluetoothDeviceInfo { 49 id: string; 50 name: string | undefined; 51 connected: boolean; 52} 53 54interface UseBluetoothOptions { 55 filters?: BluetoothLEScanFilter[]; 56 optionalServices?: BluetoothServiceUUID[]; 57 acceptAllDevices?: boolean; 58} 59 60interface UseBluetoothReturn { 61 device: BluetoothDeviceInfo | null; 62 isSupported: boolean; 63 isAvailable: boolean | null; 64 isConnecting: boolean; 65 error: string | null; 66 requestDevice: ( 67 options?: BluetoothRequestDeviceOptions 68 ) => Promise<BluetoothDeviceInfo | null>; 69 connect: () => Promise<boolean>; 70 disconnect: () => Promise<void>; 71 readCharacteristic: ( 72 serviceUuid: BluetoothServiceUUID, 73 characteristicUuid: BluetoothServiceUUID 74 ) => Promise<DataView | null>; 75 writeCharacteristic: ( 76 serviceUuid: BluetoothServiceUUID, 77 characteristicUuid: BluetoothServiceUUID, 78 value: BufferSource 79 ) => Promise<boolean>; 80 startNotifications: ( 81 serviceUuid: BluetoothServiceUUID, 82 characteristicUuid: BluetoothServiceUUID, 83 callback: (value: DataView) => void 84 ) => Promise<boolean>; 85 stopNotifications: ( 86 serviceUuid: BluetoothServiceUUID, 87 characteristicUuid: BluetoothServiceUUID 88 ) => Promise<boolean>; 89} 90 91export const useBluetooth = ( 92 options: UseBluetoothOptions = {} 93): UseBluetoothReturn => { 94 const [device, setDevice] = useState<BluetoothDeviceInfo | null>(null); 95 const [isAvailable, setIsAvailable] = useState<boolean | null>(null); 96 const [isConnecting, setIsConnecting] = useState(false); 97 const [error, setError] = useState<string | null>(null); 98 99 const deviceRef = useRef<{ 100 id: string; 101 name: string | undefined; 102 gatt?: { 103 connect(): Promise<any>; 104 connected: boolean; 105 }; 106 addEventListener( 107 type: string, 108 listener: EventListener, 109 options?: boolean | AddEventListenerOptions 110 ): void; 111 } | null>(null); 112 const serverRef = useRef<any | null>(null); 113 const characteristicsRef = useRef<Map<string, any>>(new Map()); 114 115 // Check if Web Bluetooth API is supported 116 const isSupported = 117 typeof navigator !== "undefined" && "bluetooth" in navigator; 118 119 // Check Bluetooth availability 120 useEffect(() => { 121 if (isSupported && navigator.bluetooth?.getAvailability) { 122 navigator.bluetooth 123 .getAvailability() 124 .then(setIsAvailable) 125 .catch(() => setIsAvailable(false)); 126 } else { 127 setIsAvailable(false); 128 } 129 }, [isSupported]); 130 131 // Request Bluetooth device 132 const requestDevice = useCallback( 133 async ( 134 requestOptions?: BluetoothRequestDeviceOptions 135 ): Promise<BluetoothDeviceInfo | null> => { 136 if (!isSupported || !navigator.bluetooth) { 137 setError("Web Bluetooth API is not supported"); 138 return null; 139 } 140 141 try { 142 setError(null); 143 const deviceOptions = requestOptions || { 144 filters: options.filters, 145 optionalServices: options.optionalServices, 146 acceptAllDevices: options.acceptAllDevices, 147 }; 148 149 // Ensure we have either filters or acceptAllDevices 150 if (!deviceOptions.filters && !deviceOptions.acceptAllDevices) { 151 deviceOptions.acceptAllDevices = true; 152 } 153 154 const bluetoothDevice = 155 await navigator.bluetooth.requestDevice(deviceOptions); 156 157 deviceRef.current = bluetoothDevice; 158 const deviceInfo: BluetoothDeviceInfo = { 159 id: bluetoothDevice.id, 160 name: bluetoothDevice.name, 161 connected: bluetoothDevice.gatt?.connected || false, 162 }; 163 164 setDevice(deviceInfo); 165 166 // Listen for disconnection 167 bluetoothDevice.addEventListener("gattserverdisconnected", () => { 168 setDevice((prev) => (prev ? { ...prev, connected: false } : null)); 169 serverRef.current = null; 170 characteristicsRef.current.clear(); 171 }); 172 173 return deviceInfo; 174 } catch (err) { 175 const errorMessage = 176 err instanceof Error ? err.message : "Failed to request device"; 177 setError(errorMessage); 178 return null; 179 } 180 }, 181 [isSupported, options] 182 ); 183 184 // Connect to device 185 const connect = useCallback(async (): Promise<boolean> => { 186 if (!deviceRef.current) { 187 setError("No device selected"); 188 return false; 189 } 190 191 try { 192 setIsConnecting(true); 193 setError(null); 194 195 const server = await deviceRef.current.gatt?.connect(); 196 if (server) { 197 serverRef.current = server; 198 setDevice((prev) => (prev ? { ...prev, connected: true } : null)); 199 return true; 200 } 201 202 setError("Failed to connect to GATT server"); 203 return false; 204 } catch (err) { 205 const errorMessage = 206 err instanceof Error ? err.message : "Failed to connect"; 207 setError(errorMessage); 208 return false; 209 } finally { 210 setIsConnecting(false); 211 } 212 }, []); 213 214 // Disconnect from device 215 const disconnect = useCallback(async (): Promise<void> => { 216 if (serverRef.current && serverRef.current.connected) { 217 serverRef.current.disconnect(); 218 } 219 serverRef.current = null; 220 characteristicsRef.current.clear(); 221 setDevice((prev) => (prev ? { ...prev, connected: false } : null)); 222 }, []); 223 224 // Get characteristic helper 225 const getCharacteristic = useCallback( 226 async ( 227 serviceUuid: BluetoothServiceUUID, 228 characteristicUuid: BluetoothServiceUUID 229 ): Promise<any | null> => { 230 if (!serverRef.current) { 231 setError("Not connected to device"); 232 return null; 233 } 234 235 const key = `${serviceUuid}-${characteristicUuid}`; 236 237 if (characteristicsRef.current.has(key)) { 238 return characteristicsRef.current.get(key)!; 239 } 240 241 try { 242 const service = await serverRef.current.getPrimaryService(serviceUuid); 243 const characteristic = 244 await service.getCharacteristic(characteristicUuid); 245 characteristicsRef.current.set(key, characteristic); 246 return characteristic; 247 } catch (err) { 248 const errorMessage = 249 err instanceof Error ? err.message : "Failed to get characteristic"; 250 setError(errorMessage); 251 return null; 252 } 253 }, 254 [] 255 ); 256 257 // Read characteristic value 258 const readCharacteristic = useCallback( 259 async ( 260 serviceUuid: BluetoothServiceUUID, 261 characteristicUuid: BluetoothServiceUUID 262 ): Promise<DataView | null> => { 263 const characteristic = await getCharacteristic( 264 serviceUuid, 265 characteristicUuid 266 ); 267 if (!characteristic) return null; 268 269 try { 270 setError(null); 271 const value = await characteristic.readValue(); 272 return value; 273 } catch (err) { 274 const errorMessage = 275 err instanceof Error ? err.message : "Failed to read characteristic"; 276 setError(errorMessage); 277 return null; 278 } 279 }, 280 [getCharacteristic] 281 ); 282 283 // Write characteristic value 284 const writeCharacteristic = useCallback( 285 async ( 286 serviceUuid: BluetoothServiceUUID, 287 characteristicUuid: BluetoothServiceUUID, 288 value: BufferSource 289 ): Promise<boolean> => { 290 const characteristic = await getCharacteristic( 291 serviceUuid, 292 characteristicUuid 293 ); 294 if (!characteristic) return false; 295 296 try { 297 setError(null); 298 await characteristic.writeValue(value); 299 return true; 300 } catch (err) { 301 const errorMessage = 302 err instanceof Error ? err.message : "Failed to write characteristic"; 303 setError(errorMessage); 304 return false; 305 } 306 }, 307 [getCharacteristic] 308 ); 309 310 // Start notifications 311 const startNotifications = useCallback( 312 async ( 313 serviceUuid: BluetoothServiceUUID, 314 characteristicUuid: BluetoothServiceUUID, 315 callback: (value: DataView) => void 316 ): Promise<boolean> => { 317 const characteristic = await getCharacteristic( 318 serviceUuid, 319 characteristicUuid 320 ); 321 if (!characteristic) return false; 322 323 try { 324 setError(null); 325 await characteristic.startNotifications(); 326 327 const handleNotification = (event: Event) => { 328 const target = event.target as { value?: DataView }; 329 if (target.value) { 330 callback(target.value); 331 } 332 }; 333 334 characteristic.addEventListener( 335 "characteristicvaluechanged", 336 handleNotification 337 ); 338 return true; 339 } catch (err) { 340 const errorMessage = 341 err instanceof Error ? err.message : "Failed to start notifications"; 342 setError(errorMessage); 343 return false; 344 } 345 }, 346 [getCharacteristic] 347 ); 348 349 // Stop notifications 350 const stopNotifications = useCallback( 351 async ( 352 serviceUuid: BluetoothServiceUUID, 353 characteristicUuid: BluetoothServiceUUID 354 ): Promise<boolean> => { 355 const characteristic = await getCharacteristic( 356 serviceUuid, 357 characteristicUuid 358 ); 359 if (!characteristic) return false; 360 361 try { 362 setError(null); 363 await characteristic.stopNotifications(); 364 return true; 365 } catch (err) { 366 const errorMessage = 367 err instanceof Error ? err.message : "Failed to stop notifications"; 368 setError(errorMessage); 369 return false; 370 } 371 }, 372 [getCharacteristic] 373 ); 374 375 // Cleanup on unmount 376 useEffect(() => { 377 return () => { 378 if (serverRef.current && serverRef.current.connected) { 379 serverRef.current.disconnect(); 380 } 381 }; 382 }, []); 383 384 return { 385 device, 386 isSupported, 387 isAvailable, 388 isConnecting, 389 error, 390 requestDevice, 391 connect, 392 disconnect, 393 readCharacteristic, 394 writeCharacteristic, 395 startNotifications, 396 stopNotifications, 397 }; 398}; 399