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