@atlaskit/react-ufo 4.15.6 → 4.15.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (25) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/api-reference.md +1188 -0
  3. package/dist/cjs/create-post-interaction-log-payload/get-late-mutations.js +2 -3
  4. package/dist/cjs/vc/vc-observer/observers/ssr-placeholders/index.js +28 -0
  5. package/dist/cjs/vc/vc-observer-new/index.js +1 -1
  6. package/dist/cjs/vc/vc-observer-new/metric-calculator/abstract-base-vc-calculator.js +11 -7
  7. package/dist/cjs/vc/vc-observer-new/raw-data-handler/index.js +1 -1
  8. package/dist/cjs/vc/vc-observer-new/viewport-observer/index.js +2 -2
  9. package/dist/es2019/create-post-interaction-log-payload/get-late-mutations.js +2 -3
  10. package/dist/es2019/vc/vc-observer/observers/ssr-placeholders/index.js +24 -0
  11. package/dist/es2019/vc/vc-observer-new/index.js +1 -1
  12. package/dist/es2019/vc/vc-observer-new/metric-calculator/abstract-base-vc-calculator.js +11 -7
  13. package/dist/es2019/vc/vc-observer-new/raw-data-handler/index.js +1 -1
  14. package/dist/es2019/vc/vc-observer-new/viewport-observer/index.js +2 -2
  15. package/dist/esm/create-post-interaction-log-payload/get-late-mutations.js +2 -3
  16. package/dist/esm/vc/vc-observer/observers/ssr-placeholders/index.js +28 -0
  17. package/dist/esm/vc/vc-observer-new/index.js +1 -1
  18. package/dist/esm/vc/vc-observer-new/metric-calculator/abstract-base-vc-calculator.js +11 -7
  19. package/dist/esm/vc/vc-observer-new/raw-data-handler/index.js +1 -1
  20. package/dist/esm/vc/vc-observer-new/viewport-observer/index.js +2 -2
  21. package/dist/types/common/vc/types.d.ts +1 -1
  22. package/dist/types/vc/vc-observer/observers/ssr-placeholders/index.d.ts +2 -0
  23. package/dist/types-ts4.5/common/vc/types.d.ts +1 -1
  24. package/dist/types-ts4.5/vc/vc-observer/observers/ssr-placeholders/index.d.ts +2 -0
  25. package/package.json +2 -2
@@ -0,0 +1,1188 @@
1
+ ---
2
+ title: React-UFO - Instrumentation API Reference
3
+ description: 'Complete API reference for @atlaskit/react-ufo performance instrumentation'
4
+ platform: platform
5
+ product: ufo
6
+ category: devguide
7
+ subcategory: react-ufo
8
+ date: '2025-11-10'
9
+ ---
10
+
11
+ # React UFO Instrumentation API Reference
12
+
13
+ React UFO provides performance instrumentation components and utilities to measure user experience metrics across Atlassian products. This library captures key performance indicators like Time to Interactive (TTI), Time to Visual Complete (TTVC), and custom interaction metrics.
14
+
15
+ ## Getting Started
16
+
17
+ ```bash
18
+ yarn add @atlaskit/react-ufo
19
+ ```
20
+
21
+ ## API Reference
22
+
23
+ ### Interaction Initializers
24
+
25
+ - [traceUFOPageLoad](#traceufopageload)
26
+ - [traceUFOPress](#traceufopress)
27
+ - [traceUFOTransition](#traceufotransition)
28
+ - [traceUFOInteraction](#traceufointeraction)
29
+ - [traceUFORedirect](#traceuforedirect)
30
+
31
+ ### React Components
32
+
33
+ - [UFOSegment](#ufosegment)
34
+ - [UFOLabel](#ufolabel)
35
+ - [UFOLoadHold](#ufoloadhold)
36
+ - [Suspense](#suspense)
37
+ - [UFOCustomData](#ufocustomdata)
38
+ - [UFOCustomCohortData](#ufocustomcohortdata) ⚠️ Experimental
39
+
40
+ ### React Hooks
41
+
42
+ - [usePressTracing](#usepresstracing)
43
+ - [useUFOTypingPerformanceTracing](#useufotypingperformancetracing) ⚠️ Experimental
44
+ - [useUFOTransitionCompleter](#useufotransitioncompleter)
45
+ - [useUFOReportError](#useuforeporterror)
46
+
47
+ ### Utility Functions
48
+
49
+ - [addUFOCustomData](#addufocustomdata)
50
+ - [addUFOCustomCohortData](#addufocustomcohortdata) ⚠️ Experimental
51
+ - [addFeatureFlagAccessed](#addfeatureflagaccessed)
52
+ - [setInteractionError](#setinteractionerror)
53
+ - [getUFORouteName](#getuforoutename)
54
+
55
+ ### Component Integration
56
+
57
+ - [interactionName Property](#interactionname)
58
+
59
+ ### Advanced APIs
60
+
61
+ - [SSR Configuration](#ssr-configuration)
62
+ - [VC (Visual Completion) Observer](#vc-observer)
63
+ - [Third-Party Segments](#third-party-segments)
64
+
65
+ ---
66
+
67
+ ## Interaction Initializers
68
+
69
+ ### traceUFOPageLoad
70
+
71
+ Initializes measurement of page load performance for a specific route or page. This function should be called early in the page lifecycle to capture accurate timing data.
72
+
73
+ **Import:**
74
+ ```typescript
75
+ import traceUFOPageLoad from '@atlaskit/react-ufo/trace-pageload';
76
+ ```
77
+
78
+ **Usage:**
79
+ ```typescript
80
+ traceUFOPageLoad('issue-view');
81
+ ```
82
+
83
+ **Parameters:**
84
+ - `ufoName` (string): The UFO interaction name used for sampling and event identification
85
+
86
+ **Example:**
87
+ ```typescript
88
+ // In your route component
89
+ import { useEffect } from 'react';
90
+ import traceUFOPageLoad from '@atlaskit/react-ufo/trace-pageload';
91
+
92
+ function IssueView() {
93
+ useEffect(() => {
94
+ traceUFOPageLoad('issue-view');
95
+ }, []);
96
+
97
+ return <div>Issue content...</div>;
98
+ }
99
+ ```
100
+
101
+ **Note:** The function uses sampling rates configured in your UFO settings to control which page loads are measured.
102
+
103
+ ### traceUFOPress
104
+
105
+ Measures user interaction performance from a press/click event until all loading is complete. Use this when `usePressTracing` hook or the `interactionName` property on components isn't suitable.
106
+
107
+ **Import:**
108
+ ```typescript
109
+ import traceUFOPress from '@atlaskit/react-ufo/trace-press';
110
+ ```
111
+
112
+ **Usage:**
113
+ ```typescript
114
+ traceUFOPress('button-click');
115
+ traceUFOPress('menu-open', performance.now()); // with custom timestamp
116
+ ```
117
+
118
+ **Parameters:**
119
+ - `name` (string): Unique identifier for the interaction
120
+ - `timestamp` (number, optional): Custom timestamp, defaults to `performance.now()`
121
+
122
+ **Example:**
123
+ ```typescript
124
+ import traceUFOPress from '@atlaskit/react-ufo/trace-press';
125
+
126
+ function CustomButton() {
127
+ const handleClick = () => {
128
+ traceUFOPress('custom-action');
129
+ // Trigger your action that may cause loading states
130
+ performAsyncAction();
131
+ };
132
+
133
+ return <button onClick={handleClick}>Custom Action</button>;
134
+ }
135
+ ```
136
+
137
+ **When to use:**
138
+ - Custom components that don't support `interactionName`
139
+ - Complex interaction patterns
140
+ - Manual event handling scenarios
141
+
142
+ **Note:** Results are sent as `inline-result` type events. Prefer `usePressTracing` or `interactionName` when possible.
143
+
144
+ ### traceUFOTransition
145
+
146
+ Measures navigation transition performance between routes or major state changes within a single-page application. This function aborts active interactions and starts measuring the new transition.
147
+
148
+ **Import:**
149
+ ```typescript
150
+ import traceUFOTransition from '@atlaskit/react-ufo/trace-transition';
151
+ ```
152
+
153
+ **Usage:**
154
+ ```typescript
155
+ traceUFOTransition('board-to-backlog');
156
+ ```
157
+
158
+ **Parameters:**
159
+ - `ufoName` (string): The UFO name for the transition
160
+
161
+ **Example:**
162
+ ```typescript
163
+ import { useRouter } from '@atlassian/react-resource-router';
164
+ import traceUFOTransition from '@atlaskit/react-ufo/trace-transition';
165
+
166
+ function NavigationHandler() {
167
+ const [routerState] = useRouter();
168
+
169
+ useEffect(() => {
170
+ const routeName = getUFORouteName(routerState.route);
171
+ if (routeName) {
172
+ traceUFOTransition(routeName);
173
+ }
174
+ }, [routerState]);
175
+
176
+ return <div>Page content...</div>;
177
+ }
178
+ ```
179
+
180
+ ### traceUFOInteraction
181
+
182
+ Measures performance of user interactions based on browser events. This function automatically detects the interaction type from the event and applies appropriate timing.
183
+
184
+ **Import:**
185
+ ```typescript
186
+ import traceUFOInteraction from '@atlaskit/react-ufo/trace-interaction';
187
+ ```
188
+
189
+ **Usage:**
190
+ ```typescript
191
+ traceUFOInteraction('dropdown-open', event);
192
+ ```
193
+
194
+ **Parameters:**
195
+ - `name` (string): Unique identifier for the interaction
196
+ - `event` (Event | UIEvent): Browser event object (must be trusted)
197
+
198
+ **Supported Event Types:**
199
+ - `click`
200
+ - `dblclick`
201
+ - `mousedown`
202
+ - `mouseenter`
203
+ - `mouseover`
204
+
205
+ **Example:**
206
+ ```typescript
207
+ import traceUFOInteraction from '@atlaskit/react-ufo/trace-interaction';
208
+
209
+ function InteractiveComponent() {
210
+ const handleClick = (event: React.MouseEvent) => {
211
+ traceUFOInteraction('component-action', event.nativeEvent);
212
+
213
+ // Your interaction logic
214
+ setShowModal(true);
215
+ };
216
+
217
+ return (
218
+ <div onClick={handleClick}>
219
+ Click me
220
+ </div>
221
+ );
222
+ }
223
+ ```
224
+
225
+ **Notes:**
226
+ - Only trusted events (user-initiated) are processed
227
+ - Unsupported event types are ignored
228
+ - Event timestamp is automatically extracted from the event object
229
+
230
+ ### traceUFORedirect
231
+
232
+ Specialized function for tracking navigation between routes, providing detailed route transition information. This is typically used with routing libraries like `@atlassian/react-resource-router`.
233
+
234
+ **Import:**
235
+ ```typescript
236
+ import traceUFORedirect from '@atlaskit/react-ufo/trace-redirect';
237
+ import getUFORouteName from '@atlaskit/react-ufo/route-name';
238
+ ```
239
+
240
+ **Usage:**
241
+ ```typescript
242
+ traceUFORedirect(fromUfoName, nextUfoName, nextRouteName, timestamp);
243
+ ```
244
+
245
+ **Parameters:**
246
+ - `fromUfoName` (string): Current route UFO name
247
+ - `nextUfoName` (string): Target route UFO name from route config
248
+ - `nextRouteName` (string): Target route name from route definition
249
+ - `time` (number): High-precision timestamp from `performance.now()`
250
+
251
+ **Example with React Resource Router:**
252
+ ```typescript
253
+ import { matchRoute, useRouter } from '@atlassian/react-resource-router';
254
+ import traceUFORedirect from '@atlaskit/react-ufo/trace-redirect';
255
+ import getUFORouteName from '@atlaskit/react-ufo/route-name';
256
+
257
+ function NavigationTracker() {
258
+ const [routerState] = useRouter();
259
+
260
+ const handleNavigation = (nextLocation) => {
261
+ const currentRouteName = getUFORouteName(routerState.route);
262
+
263
+ const nextMatchObject = matchRoute(routes, nextLocation.pathname, nextLocation.search);
264
+ const { route: nextRoute } = nextMatchObject;
265
+ const nextRouteUfoName = getUFORouteName(nextRoute);
266
+
267
+ traceUFORedirect(
268
+ currentRouteName,
269
+ nextRouteUfoName,
270
+ nextRoute.name,
271
+ performance.now()
272
+ );
273
+ };
274
+
275
+ return null; // This is typically a side-effect component
276
+ }
277
+ ```
278
+
279
+ **Use Cases:**
280
+ - Single-page application route changes
281
+ - Cross-product navigation
282
+ - Complex navigation patterns requiring detailed tracking
283
+
284
+ ---
285
+
286
+ ## React Components
287
+
288
+ ### UFOSegment
289
+
290
+ A React context provider that defines distinct, measurable sections of your application. Each segment generates its own performance payload, allowing granular analysis of different page areas.
291
+
292
+ **Import:**
293
+ ```typescript
294
+ import UFOSegment from '@atlaskit/react-ufo/segment';
295
+ ```
296
+
297
+ **Basic Usage:**
298
+ ```typescript
299
+ <UFOSegment name="sidebar">
300
+ <SidebarComponent />
301
+ </UFOSegment>
302
+ ```
303
+
304
+ **Parameters:**
305
+ - `name` (string): Unique identifier for this page segment
306
+ - `children` (ReactNode): Components to be measured within this segment
307
+
308
+ **Real-world Example:**
309
+ ```typescript
310
+ import UFOSegment from '@atlaskit/react-ufo/segment';
311
+
312
+ function IssuePage() {
313
+ return (
314
+ <div>
315
+ <UFOSegment name="issue-header">
316
+ <IssueHeader />
317
+ </UFOSegment>
318
+
319
+ <UFOSegment name="issue-content">
320
+ <IssueDescription />
321
+ <IssueComments />
322
+ </UFOSegment>
323
+
324
+ <UFOSegment name="issue-sidebar">
325
+ <IssueSidebar />
326
+ </UFOSegment>
327
+ </div>
328
+ );
329
+ }
330
+ ```
331
+
332
+ **Event Generation:**
333
+ For a page load with UFO name `issue-view`, segments generate:
334
+ - Main interaction: `jira.fe.page-load.issue-view`
335
+ - Segment events:
336
+ - `jira.fe.page-segment-load.issue-header`
337
+ - `jira.fe.page-segment-load.issue-content`
338
+ - `jira.fe.page-segment-load.issue-sidebar`
339
+
340
+ **Best Practices:**
341
+ - Use distinct, meaningful names for each segment
342
+ - Represent logical page sections that could theoretically render independently
343
+ - Avoid generic names like "content" or "main" - be specific
344
+ - Segments with identical names are aggregated in analytics
345
+ - Nesting is supported but use judiciously
346
+
347
+ **Third-Party Extensions:**
348
+ ```typescript
349
+ import { UFOThirdPartySegment } from '@atlaskit/react-ufo/segment';
350
+
351
+ // For external/plugin content that should be tracked separately
352
+ <UFOThirdPartySegment name="confluence-macro-calendar">
353
+ <CalendarMacro />
354
+ </UFOThirdPartySegment>
355
+ ```
356
+
357
+ ### UFOLabel
358
+
359
+ A React context provider used to annotate sections of your component tree with descriptive names. Unlike segments, labels don't generate separate events but provide context for debugging and analysis.
360
+
361
+ **Import:**
362
+ ```typescript
363
+ import UFOLabel from '@atlaskit/react-ufo/label';
364
+ ```
365
+
366
+ **Usage:**
367
+ ```typescript
368
+ <UFOLabel name="welcome_banner">
369
+ <Text>Hello folks</Text>
370
+ </UFOLabel>
371
+ ```
372
+
373
+ **Example:**
374
+ ```typescript
375
+ import UFOLabel from '@atlaskit/react-ufo/label';
376
+
377
+ function CommentThread() {
378
+ return (
379
+ <UFOLabel name="comment_thread">
380
+ <div>
381
+ <UFOLabel name="comment_author">
382
+ <UserAvatar user={author} />
383
+ </UFOLabel>
384
+ <UFOLabel name="comment_content">
385
+ <CommentBody content={comment.body} />
386
+ </UFOLabel>
387
+ </div>
388
+ </UFOLabel>
389
+ );
390
+ }
391
+ ```
392
+
393
+ **Guidelines:**
394
+ - Use static strings only - no dynamic values
395
+ - Focus on being distinct rather than semantically perfect
396
+ - No need to label every component - use judiciously
397
+ - Helps with debugging performance issues in specific UI areas
398
+
399
+ ### UFOLoadHold
400
+
401
+ Identifies loading elements and prevents interaction completion until they are no longer visible to the user. This component is crucial for accurate TTI (Time to Interactive) measurements.
402
+
403
+ **Import:**
404
+ ```typescript
405
+ import UFOLoadHold from '@atlaskit/react-ufo/load-hold';
406
+ ```
407
+
408
+ **Usage Patterns:**
409
+
410
+ **1. Wrapping loading elements:**
411
+ ```typescript
412
+ if (isLoading) {
413
+ return (
414
+ <UFOLoadHold name="card-loading">
415
+ <Skeleton />
416
+ </UFOLoadHold>
417
+ );
418
+ }
419
+ ```
420
+
421
+ **2. As a sibling component:**
422
+ ```typescript
423
+ return (
424
+ <>
425
+ <Skeleton />
426
+ <UFOLoadHold name="card-loading" />
427
+ </>
428
+ );
429
+ ```
430
+
431
+ **3. Conditional wrapping:**
432
+ ```typescript
433
+ <UFOLoadHold name="card" hold={isLoading}>
434
+ <Card />
435
+ </UFOLoadHold>
436
+ ```
437
+
438
+ **4. Conditional sibling:**
439
+ ```typescript
440
+ return (
441
+ <>
442
+ <Card />
443
+ <UFOLoadHold name="card" hold={isLoading} />
444
+ </>
445
+ );
446
+ ```
447
+
448
+ **Parameters:**
449
+ - `name` (string): Unique identifier for this loading state
450
+ - `hold` (boolean, optional): Controls whether the hold is active
451
+ - `children` (ReactNode, optional): Components wrapped by the hold
452
+
453
+ **Real-world Examples:**
454
+ ```typescript
455
+ import UFOLoadHold from '@atlaskit/react-ufo/load-hold';
456
+
457
+ // Loading skeleton
458
+ function IssueCard({ issue }) {
459
+ if (!issue) {
460
+ return (
461
+ <UFOLoadHold name="issue-card-loading">
462
+ <IssueCardSkeleton />
463
+ </UFOLoadHold>
464
+ );
465
+ }
466
+
467
+ return <IssueCardContent issue={issue} />;
468
+ }
469
+
470
+ // Conditional loading
471
+ function CommentsSection({ loading, comments }) {
472
+ return (
473
+ <div>
474
+ <UFOLoadHold name="comments-loading" hold={loading} />
475
+ {loading ? <CommentsSkeleton /> : <CommentsList comments={comments} />}
476
+ </div>
477
+ );
478
+ }
479
+ ```
480
+
481
+ **Notes:**
482
+ - Only instrument loading elements once if they're reused
483
+ - Multiple holds with the same name are fine
484
+ - Holds automatically release when component unmounts
485
+
486
+ ### Suspense
487
+
488
+ An enhanced Suspense boundary that provides UFO instrumentation. Use this as a drop-in replacement for React's `Suspense` component.
489
+
490
+ **Import:**
491
+ ```typescript
492
+ import Suspense from '@atlaskit/react-ufo/suspense';
493
+ ```
494
+
495
+ **Usage:**
496
+ ```typescript
497
+ <Suspense name="lazy-component" fallback={<Skeleton />}>
498
+ <LazyComponent />
499
+ </Suspense>
500
+ ```
501
+
502
+ **Parameters:**
503
+ - `name` (string): Unique identifier for this suspense boundary
504
+ - `fallback` (ReactNode): Component to show while suspended
505
+ - `children` (ReactNode): Components that might suspend
506
+
507
+ **Example:**
508
+ ```typescript
509
+ import Suspense from '@atlaskit/react-ufo/suspense';
510
+
511
+ const LazyIssueView = lazy(() => import('./IssueView'));
512
+
513
+ function App() {
514
+ return (
515
+ <Suspense
516
+ name="issue-view-lazy"
517
+ fallback={<IssueViewSkeleton />}
518
+ >
519
+ <LazyIssueView />
520
+ </Suspense>
521
+ );
522
+ }
523
+ ```
524
+
525
+ ### UFOCustomData
526
+
527
+ Adds custom metadata to the current UFO interaction. This data appears in analytics events with a "custom:" prefix.
528
+
529
+ **Import:**
530
+ ```typescript
531
+ import UFOCustomData from '@atlaskit/react-ufo/custom-data';
532
+ ```
533
+
534
+ **Usage:**
535
+ ```typescript
536
+ <UFOCustomData data={{ viewType: 'list', itemCount: 42 }} />
537
+ ```
538
+
539
+ **Example:**
540
+ ```typescript
541
+ import UFOCustomData from '@atlaskit/react-ufo/custom-data';
542
+
543
+ function IssueList({ issues, viewType }) {
544
+ const customData = {
545
+ viewType,
546
+ issueCount: issues.length,
547
+ hasFilters: filters.length > 0
548
+ };
549
+
550
+ return (
551
+ <div>
552
+ <UFOCustomData data={customData} />
553
+ <IssueTable issues={issues} />
554
+ </div>
555
+ );
556
+ }
557
+ ```
558
+
559
+ **Output in analytics:**
560
+ ```javascript
561
+ {
562
+ 'custom:viewType': 'list',
563
+ 'custom:issueCount': 42,
564
+ 'custom:hasFilters': true
565
+ }
566
+ ```
567
+
568
+ ### UFOCustomCohortData ⚠️ Experimental
569
+
570
+ **Note: This is an experimental feature that may change or be removed in future versions.**
571
+
572
+ Adds cohort-specific custom data to UFO interactions. This is used for A/B testing and feature flag correlation.
573
+
574
+ **Import:**
575
+ ```typescript
576
+ import UFOCustomCohortData from '@atlaskit/react-ufo/custom-cohort-data';
577
+ ```
578
+
579
+ **Usage:**
580
+ ```typescript
581
+ <UFOCustomCohortData dataKey="experiment_variant" value="control" />
582
+ ```
583
+
584
+ **Example:**
585
+ ```typescript
586
+ import UFOCustomCohortData from '@atlaskit/react-ufo/custom-cohort-data';
587
+ import { fg } from '@atlassian/jira-feature-gating';
588
+
589
+ function ExperimentalFeature() {
590
+ const variant = fg('new_issue_view') ? 'treatment' : 'control';
591
+
592
+ return (
593
+ <div>
594
+ <UFOCustomCohortData dataKey="new_issue_view_variant" value={variant} />
595
+ {variant === 'treatment' ? <NewIssueView /> : <OldIssueView />}
596
+ </div>
597
+ );
598
+ }
599
+ ```
600
+
601
+ ---
602
+
603
+ ## React Hooks
604
+
605
+ ### usePressTracing
606
+
607
+ A hook that provides a function to trace user press interactions. Use this for custom components that need interaction tracking.
608
+
609
+ **Import:**
610
+ ```typescript
611
+ import usePressTracing from '@atlaskit/react-ufo/use-press-tracing';
612
+ ```
613
+
614
+ **Usage:**
615
+ ```typescript
616
+ const traceInteraction = usePressTracing('custom-button-click');
617
+ ```
618
+
619
+ **Example:**
620
+ ```typescript
621
+ import { useCallback } from 'react';
622
+ import usePressTracing from '@atlaskit/react-ufo/use-press-tracing';
623
+
624
+ function CustomButton({ onAction }) {
625
+ const traceInteraction = usePressTracing('custom-action');
626
+
627
+ const handleClick = useCallback((event) => {
628
+ traceInteraction(event.timeStamp);
629
+ onAction();
630
+ }, [traceInteraction, onAction]);
631
+
632
+ return <button onClick={handleClick}>Custom Action</button>;
633
+ }
634
+ ```
635
+
636
+ **Parameters:**
637
+ - `name` (string): The UFO interaction name
638
+
639
+ **Returns:**
640
+ - `traceInteraction` (function): Function to call when interaction starts
641
+ - `timeStamp` (number, optional): Custom timestamp
642
+
643
+ **Note:** Results are sent as `inline-result` type events.
644
+
645
+ ### useUFOTypingPerformanceTracing ⚠️ Experimental
646
+
647
+ **Note: This is an experimental feature that may change or be removed in future versions.**
648
+
649
+ Measures typing latency in input fields to track user experience during text input.
650
+
651
+ **Import:**
652
+ ```typescript
653
+ import useUFOTypingPerformanceTracing from '@atlaskit/react-ufo/typing-performance-tracing';
654
+ ```
655
+
656
+ **Usage:**
657
+ ```typescript
658
+ const typingRef = useUFOTypingPerformanceTracing<HTMLInputElement>('text-input-typing');
659
+ ```
660
+
661
+ **Example:**
662
+ ```typescript
663
+ import useUFOTypingPerformanceTracing from '@atlaskit/react-ufo/typing-performance-tracing';
664
+
665
+ function CommentEditor() {
666
+ const typingRef = useUFOTypingPerformanceTracing<HTMLTextAreaElement>(
667
+ 'comment-editor-typing'
668
+ );
669
+
670
+ return (
671
+ <textarea
672
+ ref={typingRef}
673
+ placeholder="Write a comment..."
674
+ />
675
+ );
676
+ }
677
+ ```
678
+
679
+ **Metrics Captured:**
680
+ - Minimum typing latency
681
+ - Maximum typing latency
682
+ - Average typing latency
683
+ - Key press count
684
+ - Count of key presses under 50ms
685
+
686
+ **Notes:**
687
+ - Starts measuring on first keypress
688
+ - Sends metrics after 2 seconds of inactivity
689
+ - Only character-producing keys are measured (not backspace, etc.)
690
+
691
+ ### useUFOTransitionCompleter
692
+
693
+ Automatically completes UFO transitions when the component mounts. Use this in destination route components.
694
+
695
+ **Import:**
696
+ ```typescript
697
+ import { useUFOTransitionCompleter } from '@atlaskit/react-ufo/trace-transition';
698
+ ```
699
+
700
+ **Usage:**
701
+ ```typescript
702
+ function MyPageComponent() {
703
+ useUFOTransitionCompleter();
704
+
705
+ return <div>Page content</div>;
706
+ }
707
+ ```
708
+
709
+ **Example:**
710
+ ```typescript
711
+ import { useUFOTransitionCompleter } from '@atlaskit/react-ufo/trace-transition';
712
+
713
+ function IssueViewPage() {
714
+ // This will complete any active transition when the page renders
715
+ useUFOTransitionCompleter();
716
+
717
+ return (
718
+ <div>
719
+ <IssueHeader />
720
+ <IssueContent />
721
+ </div>
722
+ );
723
+ }
724
+ ```
725
+
726
+ ### useUFOReportError
727
+
728
+ A hook that provides a function to report errors within UFO interactions. This allows you to capture and track errors that occur during performance-critical user flows.
729
+
730
+ **Import:**
731
+ ```typescript
732
+ import { useUFOReportError } from '@atlaskit/react-ufo/report-error';
733
+ ```
734
+
735
+ **Usage:**
736
+ ```typescript
737
+ const reportError = useUFOReportError();
738
+ ```
739
+
740
+ **Parameters:**
741
+ - None
742
+
743
+ **Returns:**
744
+ - `reportError` (function): Function to report errors to the current UFO interaction
745
+ - `error` (object): Error information with the following properties:
746
+ - `name` (string): Error name/type
747
+ - `errorMessage` (string): Error description
748
+ - `errorStack` (string, optional): Error stack trace
749
+ - `forcedError` (boolean, optional): Whether this was intentionally triggered
750
+ - `errorHash` (string, optional): Custom error identifier
751
+ - `errorStatusCode` (number, optional): HTTP status code if applicable
752
+
753
+ **Example:**
754
+ ```typescript
755
+ import { useUFOReportError } from '@atlaskit/react-ufo/report-error';
756
+
757
+ function DataLoader({ issueId }) {
758
+ const reportError = useUFOReportError();
759
+
760
+ const loadIssueData = async () => {
761
+ try {
762
+ const issue = await fetchIssue(issueId);
763
+ return issue;
764
+ } catch (error) {
765
+ reportError({
766
+ name: 'IssueLoadError',
767
+ errorMessage: error.message,
768
+ errorStack: error.stack,
769
+ errorStatusCode: error.status
770
+ });
771
+ throw error; // Re-throw for component error handling
772
+ }
773
+ };
774
+
775
+ // Component logic...
776
+ }
777
+ ```
778
+
779
+ **Advanced Example with Context:**
780
+ ```typescript
781
+ import { useUFOReportError } from '@atlaskit/react-ufo/report-error';
782
+
783
+ function SearchComponent() {
784
+ const reportError = useUFOReportError();
785
+
786
+ const handleSearchError = (error, searchContext) => {
787
+ reportError({
788
+ name: 'SearchAPIError',
789
+ errorMessage: `Search failed: ${error.message}`,
790
+ errorStack: error.stack,
791
+ errorStatusCode: error.response?.status,
792
+ // Custom error hash for grouping similar errors
793
+ errorHash: `search-${searchContext.query}-${error.type}`
794
+ });
795
+ };
796
+
797
+ return (
798
+ <div>
799
+ {/* Search UI */}
800
+ </div>
801
+ );
802
+ }
803
+ ```
804
+
805
+ **Notes:**
806
+ - Errors are automatically associated with the currently active UFO interaction
807
+ - If no interaction is active, errors are added to all active interactions
808
+ - Use meaningful error names for better categorization in analytics
809
+ - Include stack traces when possible for debugging
810
+
811
+ ---
812
+
813
+ ## Utility Functions
814
+
815
+ ### addUFOCustomData
816
+
817
+ Programmatically adds custom data to the current UFO interaction without using a component.
818
+
819
+ **Import:**
820
+ ```typescript
821
+ import { addUFOCustomData } from '@atlaskit/react-ufo/custom-data';
822
+ ```
823
+
824
+ **Usage:**
825
+ ```typescript
826
+ addUFOCustomData({ key: 'value', count: 42 });
827
+ ```
828
+
829
+ **Example:**
830
+ ```typescript
831
+ import { addUFOCustomData } from '@atlaskit/react-ufo/custom-data';
832
+
833
+ function performSearch(query) {
834
+ addUFOCustomData({
835
+ searchQuery: query,
836
+ searchType: 'advanced',
837
+ hasFilters: filters.length > 0
838
+ });
839
+
840
+ return searchAPI(query);
841
+ }
842
+ ```
843
+
844
+ ### addUFOCustomCohortData ⚠️ Experimental
845
+
846
+ **Note: This is an experimental feature that may change or be removed in future versions.**
847
+
848
+ Programmatically adds cohort data for A/B testing and feature flag tracking.
849
+
850
+ **Import:**
851
+ ```typescript
852
+ import { addUFOCustomCohortData } from '@atlaskit/react-ufo/custom-cohort-data';
853
+ ```
854
+
855
+ **Usage:**
856
+ ```typescript
857
+ addUFOCustomCohortData('feature_flag_key', true);
858
+ ```
859
+
860
+ **Example:**
861
+ ```typescript
862
+ import { addUFOCustomCohortData } from '@atlaskit/react-ufo/custom-cohort-data';
863
+ import { fg } from '@atlassian/jira-feature-gating';
864
+
865
+ function initializeExperiment() {
866
+ const isNewDesignEnabled = fg('new_design_system');
867
+
868
+ addUFOCustomCohortData('design_system_version', isNewDesignEnabled ? 'v2' : 'v1');
869
+ addUFOCustomCohortData('user_segment', getUserSegment());
870
+ }
871
+ ```
872
+
873
+ ### addFeatureFlagAccessed
874
+
875
+ Records feature flag usage for correlation with performance metrics.
876
+
877
+ **Import:**
878
+ ```typescript
879
+ import { addFeatureFlagAccessed } from '@atlaskit/react-ufo/feature-flags-accessed';
880
+ ```
881
+
882
+ **Usage:**
883
+ ```typescript
884
+ addFeatureFlagAccessed('feature_flag_name', flagValue);
885
+ ```
886
+
887
+ **Example:**
888
+ ```typescript
889
+ import { addFeatureFlagAccessed } from '@atlaskit/react-ufo/feature-flags-accessed';
890
+ import { fg } from '@atlassian/jira-feature-gating';
891
+
892
+ function checkFeatureFlag(flagKey) {
893
+ const flagValue = fg(flagKey);
894
+
895
+ // Record this flag access for performance correlation
896
+ if (shouldRecordFeatureFlagForUFO(flagKey)) {
897
+ addFeatureFlagAccessed(flagKey, flagValue);
898
+ }
899
+
900
+ return flagValue;
901
+ }
902
+ ```
903
+
904
+ **Note:** Use cautiously as recording all feature flags can significantly increase payload size.
905
+
906
+ ### setInteractionError
907
+
908
+ Manually records an error for the current UFO interaction.
909
+
910
+ **Import:**
911
+ ```typescript
912
+ import { setInteractionError } from '@atlaskit/react-ufo/set-interaction-error';
913
+ ```
914
+
915
+ **Usage:**
916
+ ```typescript
917
+ setInteractionError('interaction-name', {
918
+ errorMessage: 'Something went wrong',
919
+ name: 'CustomError'
920
+ });
921
+ ```
922
+
923
+ **Example:**
924
+ ```typescript
925
+ import { setInteractionError } from '@atlaskit/react-ufo/set-interaction-error';
926
+
927
+ function loadIssueData(issueId) {
928
+ try {
929
+ return loadIssue(issueId);
930
+ } catch (error) {
931
+ setInteractionError('issue-view', {
932
+ errorMessage: error.message,
933
+ name: 'IssueLoadError'
934
+ });
935
+ throw error;
936
+ }
937
+ }
938
+ ```
939
+
940
+ ### getUFORouteName
941
+
942
+ Extracts the UFO name from a route configuration object.
943
+
944
+ **Import:**
945
+ ```typescript
946
+ import getUFORouteName from '@atlaskit/react-ufo/route-name';
947
+ ```
948
+
949
+ **Usage:**
950
+ ```typescript
951
+ const ufoName = getUFORouteName(route);
952
+ ```
953
+
954
+ **Example:**
955
+ ```typescript
956
+ import getUFORouteName from '@atlaskit/react-ufo/route-name';
957
+ import { useRouter } from '@atlassian/react-resource-router';
958
+
959
+ function RouteAnalytics() {
960
+ const [routerState] = useRouter();
961
+ const ufoName = getUFORouteName(routerState.route);
962
+
963
+ useEffect(() => {
964
+ if (ufoName) {
965
+ console.log('Current UFO route:', ufoName);
966
+ }
967
+ }, [ufoName]);
968
+
969
+ return null;
970
+ }
971
+ ```
972
+
973
+ ---
974
+
975
+ ## Component Integration
976
+
977
+ ### interactionName Property
978
+
979
+ Many Atlassian Design System components support an `interactionName` prop that automatically adds UFO tracking.
980
+
981
+ **Supported Components:**
982
+
983
+ **@atlaskit/button:**
984
+ ```typescript
985
+ import Button from '@atlaskit/button';
986
+
987
+ <Button interactionName="save-issue">Save</Button>
988
+ ```
989
+
990
+ **@atlaskit/spinner:**
991
+ ```typescript
992
+ import Spinner from '@atlaskit/spinner';
993
+
994
+ <Spinner interactionName="loading-comments" size="medium" />
995
+ ```
996
+
997
+ **Examples:**
998
+ ```typescript
999
+ // Button with interaction tracking
1000
+ <Button
1001
+ interactionName="create-issue"
1002
+ onClick={handleCreateIssue}
1003
+ >
1004
+ Create Issue
1005
+ </Button>
1006
+
1007
+ // Spinner that holds interaction until unmounted
1008
+ {isLoading && (
1009
+ <Spinner
1010
+ interactionName="issue-loading"
1011
+ size="large"
1012
+ />
1013
+ )}
1014
+ ```
1015
+
1016
+ **Notes:**
1017
+ - Button: Triggers a UFO press interaction on click
1018
+ - Spinner: Adds a UFO hold while the spinner is mounted
1019
+ - Use descriptive, unique names for each interaction
1020
+
1021
+ ---
1022
+
1023
+ ## Advanced APIs
1024
+
1025
+ ### SSR Configuration
1026
+
1027
+ Configure UFO for Server-Side Rendering scenarios. This will produce a more accurate FMP Topline Metric in Glance.
1028
+
1029
+ **Import:**
1030
+ ```typescript
1031
+ import { configure } from '@atlaskit/react-ufo/ssr';
1032
+ ```
1033
+
1034
+ **Usage:**
1035
+ ```typescript
1036
+ configure({
1037
+ getDoneMark: () => performance.now(),
1038
+ getFeatureFlags: () => ({ flag1: true, flag2: false }),
1039
+ getTimings: () => ({
1040
+ total: { startTime: 0, duration: 1200 },
1041
+ render: { startTime: 100, duration: 800 }
1042
+ }),
1043
+ getSsrPhaseSuccess: () => ({
1044
+ prefetch: true,
1045
+ earlyFlush: true,
1046
+ done: true
1047
+ })
1048
+ });
1049
+ ```
1050
+
1051
+ **Configuration Options:**
1052
+ - `getDoneMark()`: Returns timestamp when SSR rendering completed
1053
+ - `getFeatureFlags()`: Returns feature flags used during SSR
1054
+ - `getTimings()`: Returns detailed SSR timing breakdown
1055
+ - `getSsrPhaseSuccess()`: Returns success status for SSR phases
1056
+
1057
+ ### Third-Party Segments
1058
+
1059
+ Special segment type for tracking external or plugin content separately from first-party code.
1060
+
1061
+ **Import:**
1062
+ ```typescript
1063
+ import { UFOThirdPartySegment } from '@atlaskit/react-ufo/segment';
1064
+ ```
1065
+
1066
+ **Usage:**
1067
+ ```typescript
1068
+ <UFOThirdPartySegment name="external-calendar-widget">
1069
+ <CalendarWidget />
1070
+ </UFOThirdPartySegment>
1071
+ ```
1072
+
1073
+ **Use Cases:**
1074
+ - External service integrations
1075
+ - Third-party plugins or widgets
1076
+ - Partner-provided components
1077
+ - Any content not directly controlled by your team
1078
+
1079
+ ---
1080
+
1081
+ ## Best Practices
1082
+
1083
+ ### Naming Conventions
1084
+ - Use descriptive, specific names: `issue-header` not `header`
1085
+ - Use kebab-case for consistency
1086
+ - Include context when helpful: `board-issue-card` vs `issue-card`
1087
+ - Avoid dynamic values in names
1088
+
1089
+ ### Performance Considerations
1090
+ - Don't over-instrument - focus on key user journeys
1091
+ - Use segments for logical page sections (like header, content, sidebar)
1092
+ - Be selective with custom data to avoid large payloads
1093
+ - Prefer component-level holds over page-level holds for granular tracking
1094
+ - Use meaningful, descriptive names that help identify performance bottlenecks
1095
+ - Consider using UFOThirdPartySegment for external content that may impact performance
1096
+
1097
+ ### Debugging
1098
+ - Use browser dev tools to inspect UFO events
1099
+ - Check network tab for UFO payload requests (look for analytics events)
1100
+ - Use UFO labels to identify problematic code areas
1101
+ - Monitor UFO events in your application's analytics dashboard
1102
+ - Enable verbose logging in development to see interaction lifecycle
1103
+ - Use meaningful names for segments and holds to quickly identify issues
1104
+ - Test with feature flags to ensure performance tracking works across variants
1105
+ - Pay attention to "holds" that never release - they prevent interactions from completing
1106
+
1107
+ ### Migration Guide
1108
+ - Use `interactionName` props on ADS components when possible
1109
+ - Migrate from manual event tracking to UFO components
1110
+ - Consider SSR implications when adding new instrumentation
1111
+
1112
+ ---
1113
+
1114
+ ## Common Patterns
1115
+
1116
+ ### Page Load Tracking
1117
+ ```typescript
1118
+ function MyPage() {
1119
+ useEffect(() => {
1120
+ traceUFOPageLoad('my-page');
1121
+ }, []);
1122
+
1123
+ return (
1124
+ <UFOSegment name="main-content">
1125
+ <PageContent />
1126
+ </UFOSegment>
1127
+ );
1128
+ }
1129
+ ```
1130
+
1131
+ ### Loading States
1132
+ ```typescript
1133
+ function DataComponent() {
1134
+ const [loading, setLoading] = useState(true);
1135
+
1136
+ return (
1137
+ <UFOSegment name="data-section">
1138
+ {loading ? (
1139
+ <UFOLoadHold name="data-loading">
1140
+ <Skeleton />
1141
+ </UFOLoadHold>
1142
+ ) : (
1143
+ <DataTable />
1144
+ )}
1145
+ </UFOSegment>
1146
+ );
1147
+ }
1148
+ ```
1149
+
1150
+ ### Navigation Tracking
1151
+ ```typescript
1152
+ function NavigationWrapper() {
1153
+ const [routerState] = useRouter();
1154
+
1155
+ useEffect(() => {
1156
+ const ufoName = getUFORouteName(routerState.route);
1157
+ if (ufoName) {
1158
+ traceUFOTransition(ufoName);
1159
+ }
1160
+ }, [routerState]);
1161
+
1162
+ return <Router />;
1163
+ }
1164
+ ```
1165
+
1166
+ ## Troubleshooting
1167
+
1168
+ ### Common Issues
1169
+
1170
+ **Interaction Never Completes**
1171
+ - Check for holds that are never released (components that mount `UFOLoadHold` but never unmount)
1172
+ - Verify all async operations complete properly
1173
+ - Ensure error boundaries don't prevent holds from releasing
1174
+
1175
+ **Missing Analytics Events**
1176
+ - Verify UFO configuration is properly set up in your application
1177
+ - Check that sampling rates are configured for your interaction names
1178
+ - Ensure you're calling trace functions early in the component lifecycle
1179
+
1180
+ **Performance Impact**
1181
+ - Avoid over-instrumentation - focus on critical user journeys
1182
+ - Use feature flags to control UFO instrumentation in production
1183
+ - Monitor payload sizes if adding extensive custom data
1184
+
1185
+ **SSR Compatibility**
1186
+ - Use the SSR configuration APIs for server-side rendered applications
1187
+ - Ensure UFO components handle server-side rendering gracefully
1188
+ - Test SSR placeholder behavior with your specific setup