0
# Browser APIs
1
2
Components for interacting with various browser APIs like clipboard, geolocation, fullscreen, and media features.
3
4
## Capabilities
5
6
### UseClipboard Component
7
8
Provides clipboard read and write functionality with reactive state.
9
10
```typescript { .api }
11
/**
12
* Component that provides clipboard read/write functionality
13
* @example
14
* <UseClipboard v-slot="{ text, copy, isSupported }">
15
* <div>
16
* <p>Clipboard: {{ text }}</p>
17
* <button @click="copy('Hello!')">Copy Text</button>
18
* </div>
19
* </UseClipboard>
20
*/
21
interface UseClipboardProps {
22
/** Text source to read from @default undefined */
23
source?: MaybeRefOrGetter<string>;
24
/** Whether to read clipboard content immediately @default false */
25
read?: boolean;
26
/** Legacy mode using document.execCommand @default false */
27
legacy?: boolean;
28
/** Copy options */
29
copiedDuring?: number;
30
}
31
32
/** Slot data exposed by UseClipboard component */
33
interface UseClipboardReturn {
34
/** Whether clipboard API is supported */
35
isSupported: Ref<boolean>;
36
/** Current clipboard text content */
37
text: Ref<string>;
38
/** Whether clipboard was recently copied */
39
copied: Ref<boolean>;
40
/** Copy text to clipboard */
41
copy: (text?: string) => Promise<void>;
42
}
43
```
44
45
**Usage Examples:**
46
47
```vue
48
<template>
49
<!-- Basic clipboard usage -->
50
<UseClipboard v-slot="{ text, copy, copied, isSupported }">
51
<div v-if="isSupported" class="clipboard-demo">
52
<h3>Clipboard Demo</h3>
53
<p>Current clipboard: {{ text || 'Empty' }}</p>
54
<div class="clipboard-actions">
55
<button @click="copy('Hello World!')" :disabled="copied">
56
{{ copied ? 'Copied!' : 'Copy Hello World' }}
57
</button>
58
<button @click="copy(customText)">
59
Copy Custom Text
60
</button>
61
</div>
62
<input v-model="customText" placeholder="Enter text to copy" />
63
</div>
64
<div v-else>Clipboard API not supported</div>
65
</UseClipboard>
66
67
<!-- Auto-read clipboard -->
68
<UseClipboard :read="true" v-slot="{ text, copy }">
69
<div class="auto-clipboard">
70
<p>Auto-synced clipboard: {{ text }}</p>
71
<button @click="copy(`Timestamp: ${Date.now()}`)">
72
Copy Timestamp
73
</button>
74
</div>
75
</UseClipboard>
76
77
<!-- Copy from source -->
78
<UseClipboard :source="sourceText" v-slot="{ copy, copied }">
79
<button @click="copy()" :class="{ copied }">
80
{{ copied ? 'Copied!' : 'Copy Source Text' }}
81
</button>
82
</UseClipboard>
83
</template>
84
85
<script setup>
86
import { ref } from 'vue';
87
import { UseClipboard } from '@vueuse/components';
88
89
const customText = ref('');
90
const sourceText = ref('This is source text to copy');
91
</script>
92
93
<style>
94
.clipboard-actions {
95
margin: 10px 0;
96
}
97
98
.clipboard-actions button {
99
margin-right: 10px;
100
padding: 8px 16px;
101
}
102
103
.copied {
104
background-color: #4caf50;
105
color: white;
106
}
107
</style>
108
```
109
110
### UseGeolocation Component
111
112
Provides geolocation data from the browser's Geolocation API.
113
114
```typescript { .api }
115
/**
116
* Component that provides geolocation data
117
* @example
118
* <UseGeolocation v-slot="{ coords, error, resume, pause }">
119
* <div>Location: {{ coords.latitude }}, {{ coords.longitude }}</div>
120
* </UseGeolocation>
121
*/
122
interface UseGeolocationProps {
123
/** High accuracy mode @default true */
124
enableHighAccuracy?: boolean;
125
/** Maximum age of cached position (ms) @default 30000 */
126
maximumAge?: number;
127
/** Timeout for position request (ms) @default 27000 */
128
timeout?: number;
129
/** Start watching immediately @default true */
130
immediate?: boolean;
131
}
132
133
/** Slot data exposed by UseGeolocation component */
134
interface UseGeolocationReturn {
135
/** Whether geolocation is supported */
136
isSupported: Ref<boolean>;
137
/** Current position coordinates */
138
coords: Ref<GeolocationCoordinates>;
139
/** Location timestamp */
140
locatedAt: Ref<number | null>;
141
/** Geolocation error */
142
error: Ref<GeolocationPositionError | null>;
143
/** Start/resume location tracking */
144
resume: () => void;
145
/** Pause location tracking */
146
pause: () => void;
147
}
148
149
interface GeolocationCoordinates {
150
/** Latitude in decimal degrees */
151
latitude: number;
152
/** Longitude in decimal degrees */
153
longitude: number;
154
/** Altitude in meters above sea level */
155
altitude: number | null;
156
/** Accuracy of lat/lng in meters */
157
accuracy: number;
158
/** Accuracy of altitude in meters */
159
altitudeAccuracy: number | null;
160
/** Direction of travel in degrees */
161
heading: number | null;
162
/** Speed in meters per second */
163
speed: number | null;
164
}
165
166
interface GeolocationPositionError {
167
code: number;
168
message: string;
169
}
170
```
171
172
**Usage Examples:**
173
174
```vue
175
<template>
176
<!-- Basic geolocation -->
177
<UseGeolocation v-slot="{ coords, error, isSupported, locatedAt }">
178
<div v-if="isSupported" class="geolocation-info">
179
<h3>Your Location</h3>
180
<div v-if="error" class="error">
181
Error: {{ error.message }}
182
</div>
183
<div v-else-if="coords.latitude">
184
<p>π {{ coords.latitude.toFixed(6) }}, {{ coords.longitude.toFixed(6) }}</p>
185
<p>π Accuracy: Β±{{ Math.round(coords.accuracy) }}m</p>
186
<p v-if="coords.altitude">ποΈ Altitude: {{ Math.round(coords.altitude) }}m</p>
187
<p v-if="coords.speed">π Speed: {{ (coords.speed * 3.6).toFixed(1) }} km/h</p>
188
<p v-if="coords.heading">π§ Heading: {{ Math.round(coords.heading) }}Β°</p>
189
<p>β° Updated: {{ new Date(locatedAt).toLocaleTimeString() }}</p>
190
</div>
191
<div v-else class="loading">
192
π‘ Getting your location...
193
</div>
194
</div>
195
<div v-else>Geolocation not supported</div>
196
</UseGeolocation>
197
198
<!-- Controlled geolocation -->
199
<UseGeolocation
200
:immediate="false"
201
:enable-high-accuracy="false"
202
v-slot="{ coords, resume, pause, error }"
203
>
204
<div class="controlled-geo">
205
<div class="geo-controls">
206
<button @click="resume">Start Tracking</button>
207
<button @click="pause">Stop Tracking</button>
208
</div>
209
<div v-if="coords.latitude" class="coordinates">
210
Low accuracy location: {{ coords.latitude.toFixed(4) }}, {{ coords.longitude.toFixed(4) }}
211
</div>
212
<div v-if="error" class="error">{{ error.message }}</div>
213
</div>
214
</UseGeolocation>
215
</template>
216
217
<script setup>
218
import { UseGeolocation } from '@vueuse/components';
219
</script>
220
221
<style>
222
.error {
223
color: #f44336;
224
padding: 10px;
225
background: #ffebee;
226
border-radius: 4px;
227
}
228
229
.loading {
230
color: #2196f3;
231
font-style: italic;
232
}
233
234
.geo-controls button {
235
margin-right: 10px;
236
padding: 8px 16px;
237
}
238
</style>
239
```
240
241
### UseFullscreen Component
242
243
Manages fullscreen mode for elements with the Fullscreen API.
244
245
```typescript { .api }
246
/**
247
* Component that manages fullscreen mode
248
* @example
249
* <UseFullscreen v-slot="{ isFullscreen, enter, exit, toggle }">
250
* <div>
251
* <button @click="toggle">{{ isFullscreen ? 'Exit' : 'Enter' }} Fullscreen</button>
252
* </div>
253
* </UseFullscreen>
254
*/
255
interface UseFullscreenProps extends RenderableComponent {
256
/** Auto-exit fullscreen when component unmounts @default false */
257
autoExit?: boolean;
258
}
259
260
/** Slot data exposed by UseFullscreen component */
261
interface UseFullscreenReturn {
262
/** Whether fullscreen API is supported */
263
isSupported: Ref<boolean>;
264
/** Whether element is in fullscreen */
265
isFullscreen: Ref<boolean>;
266
/** Enter fullscreen mode */
267
enter: () => Promise<void>;
268
/** Exit fullscreen mode */
269
exit: () => Promise<void>;
270
/** Toggle fullscreen mode */
271
toggle: () => Promise<void>;
272
}
273
```
274
275
**Usage Examples:**
276
277
```vue
278
<template>
279
<!-- Basic fullscreen -->
280
<UseFullscreen v-slot="{ isFullscreen, toggle, isSupported }">
281
<div v-if="isSupported" class="fullscreen-demo">
282
<div class="content" :class="{ 'fullscreen-content': isFullscreen }">
283
<h3>{{ isFullscreen ? 'π― Fullscreen Mode!' : 'π± Normal Mode' }}</h3>
284
<p>This content can go fullscreen</p>
285
<button @click="toggle" class="fullscreen-btn">
286
{{ isFullscreen ? 'Exit Fullscreen' : 'Go Fullscreen' }}
287
</button>
288
</div>
289
</div>
290
<div v-else>Fullscreen API not supported</div>
291
</UseFullscreen>
292
293
<!-- Video fullscreen -->
294
<UseFullscreen v-slot="{ isFullscreen, enter, exit }">
295
<div class="video-container">
296
<video
297
ref="videoRef"
298
controls
299
width="400"
300
:style="{ width: isFullscreen ? '100vw' : '400px' }"
301
>
302
<source src="/sample-video.mp4" type="video/mp4">
303
</video>
304
<div class="video-controls">
305
<button @click="enter">πΊ Fullscreen Video</button>
306
<button @click="exit" :disabled="!isFullscreen">β©οΈ Exit</button>
307
</div>
308
</div>
309
</UseFullscreen>
310
311
<!-- Auto-exit fullscreen -->
312
<UseFullscreen :auto-exit="true" v-slot="{ isFullscreen, toggle }">
313
<div class="auto-exit-demo">
314
<p>Auto-exits fullscreen when component unmounts</p>
315
<button @click="toggle">Toggle Fullscreen</button>
316
<p>Status: {{ isFullscreen ? 'Fullscreen' : 'Windowed' }}</p>
317
</div>
318
</UseFullscreen>
319
</template>
320
321
<script setup>
322
import { ref } from 'vue';
323
import { UseFullscreen } from '@vueuse/components';
324
325
const videoRef = ref();
326
</script>
327
328
<style>
329
.fullscreen-demo {
330
border: 2px solid #ccc;
331
border-radius: 8px;
332
overflow: hidden;
333
}
334
335
.content {
336
padding: 20px;
337
background: #f9f9f9;
338
min-height: 200px;
339
display: flex;
340
flex-direction: column;
341
align-items: center;
342
justify-content: center;
343
}
344
345
.fullscreen-content {
346
background: #1976d2;
347
color: white;
348
min-height: 100vh;
349
}
350
351
.fullscreen-btn {
352
padding: 12px 24px;
353
font-size: 16px;
354
border: none;
355
border-radius: 6px;
356
background: #2196f3;
357
color: white;
358
cursor: pointer;
359
}
360
361
.video-controls {
362
padding: 10px;
363
display: flex;
364
gap: 10px;
365
}
366
</style>
367
```
368
369
### UseEyeDropper Component
370
371
Provides color picking functionality using the EyeDropper API.
372
373
```typescript { .api }
374
/**
375
* Component that provides color picker functionality
376
* @example
377
* <UseEyeDropper v-slot="{ isSupported, sRGBHex, open }">
378
* <div>
379
* <button @click="open">Pick Color</button>
380
* <div>Color: {{ sRGBHex }}</div>
381
* </div>
382
* </UseEyeDropper>
383
*/
384
interface UseEyeDropperProps {
385
// No props - uses browser EyeDropper API directly
386
}
387
388
/** Slot data exposed by UseEyeDropper component */
389
interface UseEyeDropperReturn {
390
/** Whether EyeDropper API is supported */
391
isSupported: Ref<boolean>;
392
/** Selected color in sRGB hex format */
393
sRGBHex: Ref<string>;
394
/** Open color picker */
395
open: (options?: EyeDropperOpenOptions) => Promise<void>;
396
}
397
398
interface EyeDropperOpenOptions {
399
signal?: AbortSignal;
400
}
401
```
402
403
**Usage Examples:**
404
405
```vue
406
<template>
407
<!-- Basic color picker -->
408
<UseEyeDropper v-slot="{ isSupported, sRGBHex, open }">
409
<div v-if="isSupported" class="color-picker">
410
<h3>Color Picker</h3>
411
<div class="color-display">
412
<div
413
class="color-swatch"
414
:style="{ backgroundColor: sRGBHex || '#transparent' }"
415
></div>
416
<span class="color-value">{{ sRGBHex || 'No color selected' }}</span>
417
</div>
418
<button @click="open" class="pick-button">
419
π¨ Pick Color from Screen
420
</button>
421
</div>
422
<div v-else>EyeDropper API not supported</div>
423
</UseEyeDropper>
424
425
<!-- Color palette builder -->
426
<UseEyeDropper v-slot="{ isSupported, sRGBHex, open }">
427
<div v-if="isSupported" class="palette-builder">
428
<h3>Build Color Palette</h3>
429
<div class="palette">
430
<div
431
v-for="(color, index) in palette"
432
:key="index"
433
class="palette-color"
434
:style="{ backgroundColor: color }"
435
:title="color"
436
></div>
437
</div>
438
<div class="palette-actions">
439
<button @click="addColor" :disabled="!sRGBHex">
440
Add Current Color
441
</button>
442
<button @click="open">Pick New Color</button>
443
<button @click="clearPalette">Clear Palette</button>
444
</div>
445
<p>Current: {{ sRGBHex || 'Pick a color' }}</p>
446
</div>
447
</UseEyeDropper>
448
</template>
449
450
<script setup>
451
import { ref, watch } from 'vue';
452
import { UseEyeDropper } from '@vueuse/components';
453
454
const palette = ref([]);
455
456
const addColor = () => {
457
// This would need access to sRGBHex from the slot
458
// In practice, you'd use a composable or different pattern
459
};
460
461
const clearPalette = () => {
462
palette.value = [];
463
};
464
</script>
465
466
<style>
467
.color-display {
468
display: flex;
469
align-items: center;
470
gap: 10px;
471
margin: 15px 0;
472
}
473
474
.color-swatch {
475
width: 50px;
476
height: 50px;
477
border: 2px solid #ccc;
478
border-radius: 6px;
479
box-shadow: inset 0 0 10px rgba(0,0,0,0.1);
480
}
481
482
.pick-button {
483
padding: 10px 20px;
484
background: #ff5722;
485
color: white;
486
border: none;
487
border-radius: 6px;
488
cursor: pointer;
489
font-size: 16px;
490
}
491
492
.palette {
493
display: flex;
494
flex-wrap: wrap;
495
gap: 5px;
496
margin: 15px 0;
497
min-height: 60px;
498
border: 2px dashed #ccc;
499
border-radius: 6px;
500
padding: 10px;
501
}
502
503
.palette-color {
504
width: 40px;
505
height: 40px;
506
border-radius: 4px;
507
border: 1px solid #ddd;
508
}
509
510
.palette-actions {
511
display: flex;
512
gap: 10px;
513
}
514
515
.palette-actions button {
516
padding: 8px 16px;
517
border: 1px solid #ccc;
518
border-radius: 4px;
519
cursor: pointer;
520
}
521
</style>
522
```
523
524
### UseBrowserLocation Component
525
526
Tracks browser location and URL information.
527
528
```typescript { .api }
529
/**
530
* Component that tracks browser location/URL information
531
* @example
532
* <UseBrowserLocation v-slot="{ url, origin, pathname, search }">
533
* <div>Current path: {{ pathname }}{{ search }}</div>
534
* </UseBrowserLocation>
535
*/
536
interface UseBrowserLocationProps {
537
// No props - uses browser location API directly
538
}
539
540
/** Slot data exposed by UseBrowserLocation component */
541
interface BrowserLocationState {
542
/** Reactive trigger ref for updates */
543
trigger: Ref<string>;
544
/** Complete URL */
545
url: ComputedRef<string>;
546
/** URL origin */
547
origin: ComputedRef<string>;
548
/** URL protocol */
549
protocol: ComputedRef<string>;
550
/** URL host */
551
host: ComputedRef<string>;
552
/** URL hostname */
553
hostname: ComputedRef<string>;
554
/** URL port */
555
port: ComputedRef<string>;
556
/** URL pathname */
557
pathname: ComputedRef<string>;
558
/** URL search params */
559
search: ComputedRef<string>;
560
/** URL hash */
561
hash: ComputedRef<string>;
562
}
563
```
564
565
**Usage Examples:**
566
567
```vue
568
<template>
569
<!-- Basic location info -->
570
<UseBrowserLocation v-slot="{ url, pathname, search, hash, origin }">
571
<div class="location-info">
572
<h3>Browser Location</h3>
573
<div class="url-parts">
574
<p><strong>Full URL:</strong> {{ url }}</p>
575
<p><strong>Origin:</strong> {{ origin }}</p>
576
<p><strong>Path:</strong> {{ pathname }}</p>
577
<p><strong>Search:</strong> {{ search || '(none)' }}</p>
578
<p><strong>Hash:</strong> {{ hash || '(none)' }}</p>
579
</div>
580
</div>
581
</UseBrowserLocation>
582
583
<!-- URL analyzer -->
584
<UseBrowserLocation v-slot="{ protocol, host, port, pathname, search }">
585
<div class="url-analyzer">
586
<h3>URL Analysis</h3>
587
<table class="url-table">
588
<tr><td>Protocol:</td><td>{{ protocol }}</td></tr>
589
<tr><td>Host:</td><td>{{ host }}</td></tr>
590
<tr><td>Port:</td><td>{{ port || '(default)' }}</td></tr>
591
<tr><td>Path:</td><td>{{ pathname }}</td></tr>
592
<tr><td>Query:</td><td>{{ search || '(none)' }}</td></tr>
593
</table>
594
</div>
595
</UseBrowserLocation>
596
</template>
597
598
<script setup>
599
import { UseBrowserLocation } from '@vueuse/components';
600
</script>
601
602
<style>
603
.location-info, .url-analyzer {
604
border: 1px solid #ddd;
605
border-radius: 6px;
606
padding: 15px;
607
margin: 15px 0;
608
}
609
610
.url-table {
611
width: 100%;
612
border-collapse: collapse;
613
}
614
615
.url-table td {
616
padding: 8px;
617
border-bottom: 1px solid #eee;
618
}
619
620
.url-table td:first-child {
621
font-weight: bold;
622
width: 100px;
623
}
624
</style>
625
```
626
627
## Type Definitions
628
629
```typescript { .api }
630
/** Common types used across browser API components */
631
type MaybeRefOrGetter<T> = T | Ref<T> | (() => T);
632
633
interface RenderableComponent {
634
/** The element that the component should be rendered as @default 'div' */
635
as?: object | string;
636
}
637
638
/** Clipboard API types */
639
interface ClipboardItem {
640
readonly types: string[];
641
getType(type: string): Promise<Blob>;
642
}
643
644
/** Geolocation API types */
645
interface GeolocationPosition {
646
coords: GeolocationCoordinates;
647
timestamp: number;
648
}
649
650
interface GeolocationPositionError {
651
readonly code: number;
652
readonly message: string;
653
readonly PERMISSION_DENIED: number;
654
readonly POSITION_UNAVAILABLE: number;
655
readonly TIMEOUT: number;
656
}
657
658
/** Fullscreen API types */
659
interface FullscreenOptions {
660
navigationUI?: 'auto' | 'show' | 'hide';
661
}
662
663
/** EyeDropper API types */
664
interface EyeDropper {
665
open(options?: EyeDropperOpenOptions): Promise<ColorSelectionResult>;
666
}
667
668
interface ColorSelectionResult {
669
sRGBHex: string;
670
}
671
```