@atlaskit/react-ufo 4.15.10 → 4.15.12

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