@atlaskit/react-ufo 5.4.0 → 5.4.1

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
@@ -1,18 +1,22 @@
1
1
  ---
2
2
  title: React-UFO - Instrumentation API Reference
3
- description: 'Complete API reference for @atlaskit/react-ufo performance instrumentation'
3
+ description: Complete API reference for @atlaskit/react-ufo performance instrumentation
4
4
  platform: platform
5
5
  product: ufo
6
6
  category: devguide
7
7
  subcategory: react-ufo
8
- date: '2025-11-10'
8
+ date: '2026-02-27'
9
9
  ---
10
10
 
11
11
  # React UFO Instrumentation API Reference
12
12
 
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.
13
+ React UFO is a performance monitoring library for React applications at Atlassian. It measures user
14
+ experience metrics including Time to App Idle (TTAI), Visual Completion (VC90/VC100), First
15
+ Meaningful Paint (FMP), and interaction responsiveness metrics like Input Delay (ID) and Input to
16
+ Next Paint (INP).
17
+
18
+ For a deep dive into how React UFO calculates these metrics, see
19
+ [An overview of how React UFO calculates frontend performance metrics](https://hello.atlassian.net/wiki/spaces/APD/pages/5046492124).
16
20
 
17
21
  ## Getting Started
18
22
 
@@ -20,6 +24,23 @@ Interactive (TTI), Time to Visual Complete (TTVC), and custom interaction metric
20
24
  yarn add @atlaskit/react-ufo
21
25
  ```
22
26
 
27
+ ## Key Metrics
28
+
29
+ | Metric | Description |
30
+ | ------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------- |
31
+ | **TTAI** (Time to App Idle) | Time from interaction start until the next paint after all UFO holds become inactive and DOM mutations finish |
32
+ | **VC90 / VC100** (Visual Completion) | Time until 90% / 100% of visible viewport mutations are complete |
33
+ | **FMP** (First Meaningful Paint) | Time for primary page content to become visible. Derived from BM3 instrumentation, SSR timing, or defaults to TTAI |
34
+ | **TTI** (Time to Interactive) | When the page is visible and ready for interaction. Calculated via BM3 manual instrumentation, earliest Apdex `stopTime`, or defaults to TTAI |
35
+ | **INP** (Input to Next Paint) | Time for browser to render the next frame after receiving user input |
36
+ | **ID** (Input Delay) | Time from the physical user action until first script execution |
37
+ | **Response** | Time to next active React UFO hold rendered on the page. If no hold is active, equals Result (TTAI) |
38
+ | **Result** | Time to next render when no React UFO hold is active. Equivalent to TTAI |
39
+ | **IVC100** (Interaction Visually Complete) | Time until last mutation visible in viewport for press interactions |
40
+ | **TBT** (Total Blocking Time) | Sum of blocking time from long tasks (>50ms) between FCP and TTI |
41
+ | **CLS** (Cumulative Layout Shift) | Largest burst of layout shift scores during the interaction window |
42
+ | **LCP** (Largest Contentful Paint) | Render time of the largest image or text block |
43
+
23
44
  ## API Reference
24
45
 
25
46
  ### Interaction Initializers
@@ -28,41 +49,53 @@ yarn add @atlaskit/react-ufo
28
49
  - [traceUFOPress](#traceufopress)
29
50
  - [traceUFOTransition](#traceufotransition)
30
51
  - [traceUFOInteraction](#traceufointeraction)
31
- - [traceUFORedirect](#traceuforedirect)
52
+ - [traceUFOHover](#traceufohover)
53
+ - [traceUFORedirect](#traceufodirect)
32
54
 
33
55
  ### React Components
34
56
 
35
57
  - [UFOSegment](#ufosegment)
58
+ - [UFOThirdPartySegment](#ufothirdpartysegment)
36
59
  - [UFOLabel](#ufolabel)
37
60
  - [UFOLoadHold](#ufoloadhold)
61
+ - [UFOInteractionIgnore](#ufointeractionignore)
38
62
  - [Suspense](#suspense)
39
63
  - [UFOCustomData](#ufocustomdata)
40
- - [UFOCustomCohortData](#ufocustomcohortdata) ⚠️ Experimental
64
+ - [UFOCustomCohortData](#ufocustomcohortdata)
65
+ - [UFOCustomMark](#ufocustommark)
66
+ - [Placeholder](#placeholder)
41
67
 
42
68
  ### React Hooks
43
69
 
44
70
  - [usePressTracing](#usepresstracing)
45
- - [useUFOTypingPerformanceTracing](#useufotypingperformancetracing) ⚠️ Experimental
71
+ - [useUFOTypingPerformanceTracing](#useufotypingperformancetracing)
46
72
  - [useUFOTransitionCompleter](#useufotransitioncompleter)
47
73
  - [useUFOReportError](#useuforeporterror)
74
+ - [useReportTerminalError](#usereportterminaerror)
48
75
 
49
76
  ### Utility Functions
50
77
 
51
78
  - [addUFOCustomData](#addufocustomdata)
52
- - [addUFOCustomCohortData](#addufocustomcohortdata) ⚠️ Experimental
79
+ - [addUFOCustomCohortData](#addufocustomcohortdata)
80
+ - [addUFOCustomMark](#addufocustommark-1)
81
+ - [addCustomSpans](#addcustomspans)
82
+ - [addBM3TimingsToUFO](#addbm3timingstoufo)
53
83
  - [addFeatureFlagAccessed](#addfeatureflagaccessed)
54
84
  - [setInteractionError](#setinteractionerror)
85
+ - [setTerminalError](#setterminalerror)
86
+ - [sinkTerminalErrorHandler](#sinkterminalerrorhandler)
55
87
  - [getUFORouteName](#getuforoutename)
88
+ - [updatePageloadName](#updatepageloadname)
56
89
 
57
90
  ### Component Integration
58
91
 
59
- - [interactionName Property](#interactionname)
92
+ - [interactionName Property](#interactionname-property)
60
93
 
61
94
  ### Advanced APIs
62
95
 
63
96
  - [SSR Configuration](#ssr-configuration)
64
- - [VC (Visual Completion) Observer](#vc-observer)
65
- - [Third-Party Segments](#third-party-segments)
97
+ - [Configuration](#configuration)
98
+ - [Experience Trace ID Context](#experience-trace-id-context)
66
99
 
67
100
  ---
68
101
 
@@ -70,8 +103,14 @@ yarn add @atlaskit/react-ufo
70
103
 
71
104
  ### traceUFOPageLoad
72
105
 
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.
106
+ Initializes measurement of page load performance for a specific route or page. Should be called
107
+ early in the page lifecycle to capture accurate timing data. Uses sampling rates configured in your
108
+ UFO settings.
109
+
110
+ Only one page load interaction can be active at a time. If called without a `ufoName` while no
111
+ interaction is active, it creates a new interaction with a hold until a name is provided. If called
112
+ with a `ufoName` while an unnamed page load interaction is active, it updates the name and releases
113
+ the hold.
75
114
 
76
115
  **Import:**
77
116
 
@@ -79,57 +118,64 @@ be called early in the page lifecycle to capture accurate timing data.
79
118
  import traceUFOPageLoad from '@atlaskit/react-ufo/trace-pageload';
80
119
  ```
81
120
 
82
- **Usage:**
121
+ **Signature:**
83
122
 
84
123
  ```typescript
85
- traceUFOPageLoad('issue-view');
124
+ function traceUFOPageLoad(
125
+ ufoName?: string | null | undefined,
126
+ routeName?: string | null | undefined, // defaults to ufoName
127
+ ): void;
86
128
  ```
87
129
 
88
130
  **Parameters:**
89
131
 
90
- - `ufoName` (string): The UFO interaction name used for sampling and event identification
132
+ - `ufoName` (string, optional): The UFO interaction name used for sampling and event identification
133
+ - `routeName` (string, optional): The route name for context. Defaults to `ufoName` if not provided
91
134
 
92
135
  **Example:**
93
136
 
94
137
  ```typescript
95
- // In your route component
96
- import { useEffect } from 'react';
97
138
  import traceUFOPageLoad from '@atlaskit/react-ufo/trace-pageload';
98
139
 
99
- function IssueView() {
100
- useEffect(() => {
101
- traceUFOPageLoad('issue-view');
102
- }, []);
140
+ // Simple usage
141
+ traceUFOPageLoad('issue-view');
103
142
 
104
- return <div>Issue content...</div>;
105
- }
143
+ // With separate route name
144
+ traceUFOPageLoad('issue-view', 'jira-issue-route');
145
+ ```
146
+
147
+ **Also exports:**
148
+
149
+ ```typescript
150
+ import { updatePageloadName } from '@atlaskit/react-ufo/trace-pageload';
106
151
  ```
107
152
 
108
- **Note:** The function uses sampling rates configured in your UFO settings to control which page
109
- loads are measured.
153
+ See [updatePageloadName](#updatepageloadname) for details.
110
154
 
111
155
  ### traceUFOPress
112
156
 
113
157
  Measures user interaction performance from a press/click event until all loading is complete. Use
114
158
  this when `usePressTracing` hook or the `interactionName` property on components isn't suitable.
115
159
 
160
+ Only one interaction can be active at a time. Starting a new press interaction aborts any currently
161
+ active interaction. Aborted interactions do **not** report metrics to prevent data skewing.
162
+
116
163
  **Import:**
117
164
 
118
165
  ```typescript
119
166
  import traceUFOPress from '@atlaskit/react-ufo/trace-press';
120
167
  ```
121
168
 
122
- **Usage:**
169
+ **Signature:**
123
170
 
124
171
  ```typescript
125
- traceUFOPress('button-click');
126
- traceUFOPress('menu-open', performance.now()); // with custom timestamp
172
+ function traceUFOPress(name: string, timestamp?: number): void;
127
173
  ```
128
174
 
129
175
  **Parameters:**
130
176
 
131
177
  - `name` (string): Unique identifier for the interaction
132
- - `timestamp` (number, optional): Custom timestamp, defaults to `performance.now()`
178
+ - `timestamp` (number, optional): Custom timestamp. Defaults to `performance.now()`
133
179
 
134
180
  **Example:**
135
181
 
@@ -137,13 +183,12 @@ traceUFOPress('menu-open', performance.now()); // with custom timestamp
137
183
  import traceUFOPress from '@atlaskit/react-ufo/trace-press';
138
184
 
139
185
  function CustomButton() {
140
- const handleClick = () => {
141
- traceUFOPress('custom-action');
142
- // Trigger your action that may cause loading states
143
- performAsyncAction();
144
- };
186
+ const handleClick = () => {
187
+ traceUFOPress('custom-action');
188
+ performAsyncAction();
189
+ };
145
190
 
146
- return <button onClick={handleClick}>Custom Action</button>;
191
+ return <button onClick={handleClick}>Custom Action</button>;
147
192
  }
148
193
  ```
149
194
 
@@ -159,8 +204,7 @@ function CustomButton() {
159
204
  ### traceUFOTransition
160
205
 
161
206
  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.
207
+ single-page application. Aborts all active interactions and starts measuring the new transition.
164
208
 
165
209
  **Import:**
166
210
 
@@ -168,40 +212,45 @@ transition.
168
212
  import traceUFOTransition from '@atlaskit/react-ufo/trace-transition';
169
213
  ```
170
214
 
171
- **Usage:**
215
+ **Signature:**
172
216
 
173
217
  ```typescript
174
- traceUFOTransition('board-to-backlog');
218
+ function traceUFOTransition(
219
+ ufoName: string | null | undefined,
220
+ routeName?: string | null | undefined, // defaults to ufoName
221
+ ): void;
175
222
  ```
176
223
 
177
224
  **Parameters:**
178
225
 
179
226
  - `ufoName` (string): The UFO name for the transition
227
+ - `routeName` (string, optional): The route name for context. Defaults to `ufoName`
180
228
 
181
229
  **Example:**
182
230
 
183
231
  ```typescript
184
232
  import { useRouter } from '@atlassian/react-resource-router';
185
233
  import traceUFOTransition from '@atlaskit/react-ufo/trace-transition';
234
+ import getUFORouteName from '@atlaskit/react-ufo/route-name';
186
235
 
187
236
  function NavigationHandler() {
188
- const [routerState] = useRouter();
237
+ const [routerState] = useRouter();
189
238
 
190
- useEffect(() => {
191
- const routeName = getUFORouteName(routerState.route);
192
- if (routeName) {
193
- traceUFOTransition(routeName);
194
- }
195
- }, [routerState]);
239
+ useEffect(() => {
240
+ const routeName = getUFORouteName(routerState.route);
241
+ if (routeName) {
242
+ traceUFOTransition(routeName);
243
+ }
244
+ }, [routerState]);
196
245
 
197
- return <div>Page content...</div>;
246
+ return <div>Page content...</div>;
198
247
  }
199
248
  ```
200
249
 
201
250
  ### traceUFOInteraction
202
251
 
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.
252
+ Measures performance of user interactions based on browser events. Automatically detects the
253
+ interaction type (press or hover) from the event and applies appropriate timing.
205
254
 
206
255
  **Import:**
207
256
 
@@ -209,10 +258,10 @@ detects the interaction type from the event and applies appropriate timing.
209
258
  import traceUFOInteraction from '@atlaskit/react-ufo/trace-interaction';
210
259
  ```
211
260
 
212
- **Usage:**
261
+ **Signature:**
213
262
 
214
263
  ```typescript
215
- traceUFOInteraction('dropdown-open', event);
264
+ function traceUFOInteraction(name: string, event: Event | UIEvent): void;
216
265
  ```
217
266
 
218
267
  **Parameters:**
@@ -222,11 +271,13 @@ traceUFOInteraction('dropdown-open', event);
222
271
 
223
272
  **Supported Event Types:**
224
273
 
225
- - `click`
226
- - `dblclick`
227
- - `mousedown`
228
- - `mouseenter`
229
- - `mouseover`
274
+ | Event Type | Maps to Interaction Type |
275
+ | ------------ | ------------------------ |
276
+ | `click` | `press` |
277
+ | `dblclick` | `press` |
278
+ | `mousedown` | `press` |
279
+ | `mouseenter` | `hover` |
280
+ | `mouseover` | `hover` |
230
281
 
231
282
  **Example:**
232
283
 
@@ -234,39 +285,78 @@ traceUFOInteraction('dropdown-open', event);
234
285
  import traceUFOInteraction from '@atlaskit/react-ufo/trace-interaction';
235
286
 
236
287
  function InteractiveComponent() {
237
- const handleClick = (event: React.MouseEvent) => {
238
- traceUFOInteraction('component-action', event.nativeEvent);
239
-
240
- // Your interaction logic
241
- setShowModal(true);
242
- };
288
+ const handleClick = (event: React.MouseEvent) => {
289
+ traceUFOInteraction('component-action', event.nativeEvent);
290
+ setShowModal(true);
291
+ };
243
292
 
244
- return <div onClick={handleClick}>Click me</div>;
293
+ return <div onClick={handleClick}>Click me</div>;
245
294
  }
246
295
  ```
247
296
 
248
297
  **Notes:**
249
298
 
250
299
  - Only trusted events (user-initiated) are processed
251
- - Unsupported event types are ignored
300
+ - Unsupported event types are silently ignored
252
301
  - Event timestamp is automatically extracted from the event object
253
302
 
303
+ ### traceUFOHover
304
+
305
+ Measures performance of hover interactions. Use this for tracking hover-triggered loading states
306
+ such as popups, tooltips, or preview cards.
307
+
308
+ **Import:**
309
+
310
+ ```typescript
311
+ import traceUFOHover from '@atlaskit/react-ufo/trace-hover';
312
+ ```
313
+
314
+ **Signature:**
315
+
316
+ ```typescript
317
+ function traceUFOHover(name: string, timestamp?: number): void;
318
+ ```
319
+
320
+ **Parameters:**
321
+
322
+ - `name` (string): Unique identifier for the hover interaction
323
+ - `timestamp` (number, optional): Custom timestamp. Defaults to `performance.now()`
324
+
325
+ **Example:**
326
+
327
+ ```typescript
328
+ import traceUFOHover from '@atlaskit/react-ufo/trace-hover';
329
+
330
+ function HoverCard() {
331
+ const handleMouseEnter = () => {
332
+ traceUFOHover('user-profile-hover');
333
+ loadUserProfile();
334
+ };
335
+
336
+ return <div onMouseEnter={handleMouseEnter}>Hover for details</div>;
337
+ }
338
+ ```
339
+
254
340
  ### traceUFORedirect
255
341
 
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`.
342
+ Tracks navigation redirects between routes, providing detailed route transition information. This is
343
+ typically used with routing libraries like `@atlassian/react-resource-router`.
258
344
 
259
345
  **Import:**
260
346
 
261
347
  ```typescript
262
348
  import traceUFORedirect from '@atlaskit/react-ufo/trace-redirect';
263
- import getUFORouteName from '@atlaskit/react-ufo/route-name';
264
349
  ```
265
350
 
266
- **Usage:**
351
+ **Signature:**
267
352
 
268
353
  ```typescript
269
- traceUFORedirect(fromUfoName, nextUfoName, nextRouteName, timestamp);
354
+ function traceUFORedirect(
355
+ fromUfoName: string,
356
+ nextUfoName: string,
357
+ nextRouteName: string,
358
+ time: number,
359
+ ): void;
270
360
  ```
271
361
 
272
362
  **Parameters:**
@@ -276,7 +366,7 @@ traceUFORedirect(fromUfoName, nextUfoName, nextRouteName, timestamp);
276
366
  - `nextRouteName` (string): Target route name from route definition
277
367
  - `time` (number): High-precision timestamp from `performance.now()`
278
368
 
279
- **Example with React Resource Router:**
369
+ **Example:**
280
370
 
281
371
  ```typescript
282
372
  import { matchRoute, useRouter } from '@atlassian/react-resource-router';
@@ -288,7 +378,6 @@ function NavigationTracker() {
288
378
 
289
379
  const handleNavigation = (nextLocation) => {
290
380
  const currentRouteName = getUFORouteName(routerState.route);
291
-
292
381
  const nextMatchObject = matchRoute(routes, nextLocation.pathname, nextLocation.search);
293
382
  const { route: nextRoute } = nextMatchObject;
294
383
  const nextRouteUfoName = getUFORouteName(nextRoute);
@@ -296,16 +385,10 @@ function NavigationTracker() {
296
385
  traceUFORedirect(currentRouteName, nextRouteUfoName, nextRoute.name, performance.now());
297
386
  };
298
387
 
299
- return null; // This is typically a side-effect component
388
+ return null;
300
389
  }
301
390
  ```
302
391
 
303
- **Use Cases:**
304
-
305
- - Single-page application route changes
306
- - Cross-product navigation
307
- - Complex navigation patterns requiring detailed tracking
308
-
309
392
  ---
310
393
 
311
394
  ## React Components
@@ -321,41 +404,37 @@ segment generates its own performance payload, allowing granular analysis of dif
321
404
  import UFOSegment from '@atlaskit/react-ufo/segment';
322
405
  ```
323
406
 
324
- **Basic Usage:**
325
-
326
- ```typescript
327
- <UFOSegment name="sidebar">
328
- <SidebarComponent />
329
- </UFOSegment>
330
- ```
331
-
332
- **Parameters:**
407
+ **Props:**
333
408
 
334
- - `name` (string): Unique identifier for this page segment
335
- - `children` (ReactNode): Components to be measured within this segment
409
+ | Prop | Type | Required | Description |
410
+ | ---------- | -------------------------------- | -------- | --------------------------------------------------- |
411
+ | `name` | `string` | Yes | Unique identifier for this page segment |
412
+ | `children` | `ReactNode` | Yes | Components to be measured within this segment |
413
+ | `mode` | `'list' \| 'single'` | No | Rendering mode for the segment |
414
+ | `type` | `'first-party' \| 'third-party'` | No | Segment ownership type. Defaults to `'first-party'` |
336
415
 
337
- **Real-world Example:**
416
+ **Example:**
338
417
 
339
418
  ```typescript
340
419
  import UFOSegment from '@atlaskit/react-ufo/segment';
341
420
 
342
421
  function IssuePage() {
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
- );
422
+ return (
423
+ <div>
424
+ <UFOSegment name="issue-header">
425
+ <IssueHeader />
426
+ </UFOSegment>
427
+
428
+ <UFOSegment name="issue-content">
429
+ <IssueDescription />
430
+ <IssueComments />
431
+ </UFOSegment>
432
+
433
+ <UFOSegment name="issue-sidebar">
434
+ <IssueSidebar />
435
+ </UFOSegment>
436
+ </div>
437
+ );
359
438
  }
360
439
  ```
361
440
 
@@ -371,26 +450,47 @@ function IssuePage() {
371
450
 
372
451
  - Use distinct, meaningful names for each segment
373
452
  - Represent logical page sections that could theoretically render independently
374
- - Avoid generic names like "content" or "main" - be specific
453
+ - Avoid generic names like "content" or "main" be specific
375
454
  - Segments with identical names are aggregated in analytics
376
455
  - Nesting is supported but use judiciously
377
456
 
378
- **Third-Party Extensions:**
457
+ ### UFOThirdPartySegment
458
+
459
+ A specialized segment for tracking external or plugin content separately from first-party code.
460
+ Wraps `UFOSegment` with `type="third-party"`.
461
+
462
+ **Import:**
463
+
464
+ ```typescript
465
+ import { UFOThirdPartySegment } from '@atlaskit/react-ufo/segment';
466
+ ```
467
+
468
+ **Props:**
469
+
470
+ Same as `UFOSegment` except `type` is automatically set to `'third-party'`.
471
+
472
+ **Example:**
379
473
 
380
474
  ```typescript
381
475
  import { UFOThirdPartySegment } from '@atlaskit/react-ufo/segment';
382
476
 
383
- // For external/plugin content that should be tracked separately
384
477
  <UFOThirdPartySegment name="confluence-macro-calendar">
385
- <CalendarMacro />
386
- </UFOThirdPartySegment>;
478
+ <CalendarMacro />
479
+ </UFOThirdPartySegment>
387
480
  ```
388
481
 
482
+ **Use Cases:**
483
+
484
+ - External service integrations
485
+ - Third-party plugins or widgets
486
+ - Partner-provided components
487
+ - Any content not directly controlled by your team
488
+
389
489
  ### UFOLabel
390
490
 
391
491
  A React context provider used to annotate sections of your component tree with descriptive names.
392
492
  Unlike segments, labels don't generate separate events but provide context for debugging and
393
- analysis.
493
+ analysis in VC revision data.
394
494
 
395
495
  **Import:**
396
496
 
@@ -398,13 +498,12 @@ analysis.
398
498
  import UFOLabel from '@atlaskit/react-ufo/label';
399
499
  ```
400
500
 
401
- **Usage:**
501
+ **Props:**
402
502
 
403
- ```typescript
404
- <UFOLabel name="welcome_banner">
405
- <Text>Hello folks</Text>
406
- </UFOLabel>
407
- ```
503
+ | Prop | Type | Required | Description |
504
+ | ---------- | ----------- | -------- | -------------------------------------- |
505
+ | `name` | `string` | Yes | Descriptive name for this section |
506
+ | `children` | `ReactNode` | Yes | Components within this labeled section |
408
507
 
409
508
  **Example:**
410
509
 
@@ -412,32 +511,33 @@ import UFOLabel from '@atlaskit/react-ufo/label';
412
511
  import UFOLabel from '@atlaskit/react-ufo/label';
413
512
 
414
513
  function CommentThread() {
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
- );
514
+ return (
515
+ <UFOLabel name="comment_thread">
516
+ <div>
517
+ <UFOLabel name="comment_author">
518
+ <UserAvatar user={author} />
519
+ </UFOLabel>
520
+ <UFOLabel name="comment_content">
521
+ <CommentBody content={comment.body} />
522
+ </UFOLabel>
523
+ </div>
524
+ </UFOLabel>
525
+ );
427
526
  }
428
527
  ```
429
528
 
430
529
  **Guidelines:**
431
530
 
432
- - Use static strings only - no dynamic values
531
+ - Use static strings only no dynamic values
433
532
  - Focus on being distinct rather than semantically perfect
434
- - No need to label every component - use judiciously
533
+ - No need to label every component use judiciously
435
534
  - Helps with debugging performance issues in specific UI areas
436
535
 
437
536
  ### UFOLoadHold
438
537
 
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.
538
+ Identifies loading elements and prevents interaction completion until they are no longer active.
539
+ This component is crucial for accurate TTAI measurements an interaction's TTAI is determined by
540
+ the point at which the last hold is released.
441
541
 
442
542
  **Import:**
443
543
 
@@ -445,17 +545,26 @@ the user. This component is crucial for accurate TTI (Time to Interactive) measu
445
545
  import UFOLoadHold from '@atlaskit/react-ufo/load-hold';
446
546
  ```
447
547
 
548
+ **Props:**
549
+
550
+ | Prop | Type | Required | Default | Description |
551
+ | -------------- | ----------- | -------- | ----------- | ------------------------------------------------------------------------------- |
552
+ | `name` | `string` | Yes | — | Unique identifier for this loading state |
553
+ | `hold` | `boolean` | No | `undefined` | Controls whether the hold is active. When omitted, hold is active while mounted |
554
+ | `children` | `ReactNode` | No | — | Components wrapped by the hold |
555
+ | `experimental` | `boolean` | No | `undefined` | Enables experimental hold behavior |
556
+
448
557
  **Usage Patterns:**
449
558
 
450
- **1. Wrapping loading elements:**
559
+ **1. Wrapping loading elements (hold while mounted):**
451
560
 
452
561
  ```typescript
453
562
  if (isLoading) {
454
- return (
455
- <UFOLoadHold name="card-loading">
456
- <Skeleton />
457
- </UFOLoadHold>
458
- );
563
+ return (
564
+ <UFOLoadHold name="card-loading">
565
+ <Skeleton />
566
+ </UFOLoadHold>
567
+ );
459
568
  }
460
569
  ```
461
570
 
@@ -463,18 +572,18 @@ if (isLoading) {
463
572
 
464
573
  ```typescript
465
574
  return (
466
- <>
467
- <Skeleton />
468
- <UFOLoadHold name="card-loading" />
469
- </>
575
+ <>
576
+ <Skeleton />
577
+ <UFOLoadHold name="card-loading" />
578
+ </>
470
579
  );
471
580
  ```
472
581
 
473
- **3. Conditional wrapping:**
582
+ **3. Conditional hold via prop:**
474
583
 
475
584
  ```typescript
476
585
  <UFOLoadHold name="card" hold={isLoading}>
477
- <Card />
586
+ <Card />
478
587
  </UFOLoadHold>
479
588
  ```
480
589
 
@@ -482,58 +591,69 @@ return (
482
591
 
483
592
  ```typescript
484
593
  return (
485
- <>
486
- <Card />
487
- <UFOLoadHold name="card" hold={isLoading} />
488
- </>
594
+ <>
595
+ <Card />
596
+ <UFOLoadHold name="card" hold={isLoading} />
597
+ </>
489
598
  );
490
599
  ```
491
600
 
492
- **Parameters:**
601
+ **Notes:**
602
+
603
+ - Only instrument loading elements once if they're reused
604
+ - Multiple holds with the same name are fine
605
+ - Holds automatically release when the component unmounts
606
+ - Holds that are added after the interaction is already complete (after TTAI) are called "late
607
+ holds" and are tracked separately
493
608
 
494
- - `name` (string): Unique identifier for this loading state
495
- - `hold` (boolean, optional): Controls whether the hold is active
496
- - `children` (ReactNode, optional): Components wrapped by the hold
609
+ ### UFOInteractionIgnore
497
610
 
498
- **Real-world Examples:**
611
+ Prevents a subtree from holding up an interaction. Use this when a component loads late but isn't
612
+ considered a breach of your SLO.
613
+
614
+ **Import:**
499
615
 
500
616
  ```typescript
501
- import UFOLoadHold from '@atlaskit/react-ufo/load-hold';
617
+ import UFOInteractionIgnore from '@atlaskit/react-ufo/interaction-ignore';
618
+ ```
502
619
 
503
- // Loading skeleton
504
- function IssueCard({ issue }) {
505
- if (!issue) {
506
- return (
507
- <UFOLoadHold name="issue-card-loading">
508
- <IssueCardSkeleton />
509
- </UFOLoadHold>
510
- );
511
- }
620
+ **Props:**
512
621
 
513
- return <IssueCardContent issue={issue} />;
514
- }
622
+ | Prop | Type | Required | Default | Description |
623
+ | ---------- | ----------------------- | -------- | ------- | --------------------------------------- |
624
+ | `children` | `ReactNode` | No | — | Components within the ignored subtree |
625
+ | `ignore` | `boolean` | No | `true` | Whether to ignore holds in this subtree |
626
+ | `reason` | `'third-party-element'` | No | — | Reason for ignoring holds |
515
627
 
516
- // Conditional loading
517
- function CommentsSection({ loading, comments }) {
518
- return (
519
- <div>
520
- <UFOLoadHold name="comments-loading" hold={loading} />
521
- {loading ? <CommentsSkeleton /> : <CommentsList comments={comments} />}
522
- </div>
523
- );
524
- }
525
- ```
628
+ **Example:**
526
629
 
527
- **Notes:**
630
+ ```typescript
631
+ import UFOInteractionIgnore from '@atlaskit/react-ufo/interaction-ignore';
528
632
 
529
- - Only instrument loading elements once if they're reused
530
- - Multiple holds with the same name are fine
531
- - Holds automatically release when component unmounts
633
+ function PageLayout() {
634
+ return (
635
+ <App>
636
+ <Main />
637
+ <Sidebar>
638
+ <UFOInteractionIgnore>
639
+ <InsightsButton />
640
+ </UFOInteractionIgnore>
641
+ </Sidebar>
642
+ </App>
643
+ );
644
+ }
645
+
646
+ // Conditional usage
647
+ <UFOInteractionIgnore ignore={!isCriticalContent}>
648
+ <OptionalWidget />
649
+ </UFOInteractionIgnore>
650
+ ```
532
651
 
533
652
  ### Suspense
534
653
 
535
- An enhanced Suspense boundary that provides UFO instrumentation. Use this as a drop-in replacement
536
- for React's `Suspense` component.
654
+ An enhanced React Suspense boundary that integrates with UFO instrumentation. Wraps React's
655
+ `Suspense` component and adds a `UFOLoadHold` around the fallback, so the interaction is held until
656
+ the suspended component resolves.
537
657
 
538
658
  **Import:**
539
659
 
@@ -541,19 +661,13 @@ for React's `Suspense` component.
541
661
  import Suspense from '@atlaskit/react-ufo/suspense';
542
662
  ```
543
663
 
544
- **Usage:**
545
-
546
- ```typescript
547
- <Suspense name="lazy-component" fallback={<Skeleton />}>
548
- <LazyComponent />
549
- </Suspense>
550
- ```
551
-
552
- **Parameters:**
664
+ **Props:**
553
665
 
554
- - `name` (string): Unique identifier for this suspense boundary
555
- - `fallback` (ReactNode): Component to show while suspended
556
- - `children` (ReactNode): Components that might suspend
666
+ | Prop | Type | Required | Description |
667
+ | ----------------- | ----------- | -------- | -------------------------------------------------------------------- |
668
+ | `interactionName` | `string` | Yes | Unique identifier for this suspense boundary (used as the hold name) |
669
+ | `fallback` | `ReactNode` | Yes | Component to show while suspended |
670
+ | `children` | `ReactNode` | Yes | Components that might suspend |
557
671
 
558
672
  **Example:**
559
673
 
@@ -563,18 +677,18 @@ import Suspense from '@atlaskit/react-ufo/suspense';
563
677
  const LazyIssueView = lazy(() => import('./IssueView'));
564
678
 
565
679
  function App() {
566
- return (
567
- <Suspense name="issue-view-lazy" fallback={<IssueViewSkeleton />}>
568
- <LazyIssueView />
569
- </Suspense>
570
- );
680
+ return (
681
+ <Suspense interactionName="issue-view-lazy" fallback={<IssueViewSkeleton />}>
682
+ <LazyIssueView />
683
+ </Suspense>
684
+ );
571
685
  }
572
686
  ```
573
687
 
574
688
  ### UFOCustomData
575
689
 
576
690
  Adds custom metadata to the current UFO interaction. This data appears in analytics events with a
577
- "custom:" prefix.
691
+ `custom:` prefix.
578
692
 
579
693
  **Import:**
580
694
 
@@ -582,11 +696,11 @@ Adds custom metadata to the current UFO interaction. This data appears in analyt
582
696
  import UFOCustomData from '@atlaskit/react-ufo/custom-data';
583
697
  ```
584
698
 
585
- **Usage:**
699
+ **Props:**
586
700
 
587
- ```typescript
588
- <UFOCustomData data={{ viewType: 'list', itemCount: 42 }} />
589
- ```
701
+ | Prop | Type | Required | Description |
702
+ | ------ | ------------------------- | -------- | ------------------------------ |
703
+ | `data` | `Record<string, unknown>` | Yes | Key-value pairs of custom data |
590
704
 
591
705
  **Example:**
592
706
 
@@ -594,18 +708,12 @@ import UFOCustomData from '@atlaskit/react-ufo/custom-data';
594
708
  import UFOCustomData from '@atlaskit/react-ufo/custom-data';
595
709
 
596
710
  function IssueList({ issues, viewType }) {
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
- );
711
+ return (
712
+ <div>
713
+ <UFOCustomData data={{ viewType, issueCount: issues.length }} />
714
+ <IssueTable issues={issues} />
715
+ </div>
716
+ );
609
717
  }
610
718
  ```
611
719
 
@@ -614,16 +722,13 @@ function IssueList({ issues, viewType }) {
614
722
  ```javascript
615
723
  {
616
724
  'custom:viewType': 'list',
617
- 'custom:issueCount': 42,
618
- 'custom:hasFilters': true
725
+ 'custom:issueCount': 42
619
726
  }
620
727
  ```
621
728
 
622
- ### UFOCustomCohortData ⚠️ Experimental
729
+ ### UFOCustomCohortData
623
730
 
624
- **Note: This is an experimental feature that may change or be removed in future versions.**
625
-
626
- Adds cohort-specific custom data to UFO interactions. This is used for A/B testing and feature flag
731
+ Adds cohort-specific custom data to UFO interactions. Used for A/B testing and feature flag
627
732
  correlation.
628
733
 
629
734
  **Import:**
@@ -632,27 +737,103 @@ correlation.
632
737
  import UFOCustomCohortData from '@atlaskit/react-ufo/custom-cohort-data';
633
738
  ```
634
739
 
635
- **Usage:**
740
+ **Props:**
636
741
 
637
- ```typescript
638
- <UFOCustomCohortData dataKey="experiment_variant" value="control" />
639
- ```
742
+ | Prop | Type | Required | Description |
743
+ | --------- | -------------------------------------------------- | -------- | ----------------- |
744
+ | `dataKey` | `string` | Yes | Cohort data key |
745
+ | `value` | `string \| number \| boolean \| null \| undefined` | Yes | Cohort data value |
640
746
 
641
747
  **Example:**
642
748
 
643
749
  ```typescript
644
750
  import UFOCustomCohortData from '@atlaskit/react-ufo/custom-cohort-data';
645
- import { fg } from '@atlassian/jira-feature-gating';
751
+ import { fg } from '@atlaskit/platform-feature-flags';
646
752
 
647
753
  function ExperimentalFeature() {
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
- );
754
+ const variant = fg('new_issue_view') ? 'treatment' : 'control';
755
+
756
+ return (
757
+ <div>
758
+ <UFOCustomCohortData dataKey="new_issue_view_variant" value={variant} />
759
+ {variant === 'treatment' ? <NewIssueView /> : <OldIssueView />}
760
+ </div>
761
+ );
762
+ }
763
+ ```
764
+
765
+ ### UFOCustomMark
766
+
767
+ Records a custom performance mark at a specific point in time during the current interaction.
768
+
769
+ **Import:**
770
+
771
+ ```typescript
772
+ import UFOCustomMark from '@atlaskit/react-ufo/custom-mark';
773
+ ```
774
+
775
+ **Props:**
776
+
777
+ | Prop | Type | Required | Description |
778
+ | ----------- | -------- | -------- | ------------------------------------------------- |
779
+ | `name` | `string` | Yes | Name of the mark |
780
+ | `timestamp` | `number` | No | Custom timestamp. Defaults to `performance.now()` |
781
+
782
+ **Example:**
783
+
784
+ ```typescript
785
+ import UFOCustomMark from '@atlaskit/react-ufo/custom-mark';
786
+
787
+ function DataTable({ data }) {
788
+ return (
789
+ <div>
790
+ <UFOCustomMark name="data-table-render-start" />
791
+ <Table data={data} />
792
+ </div>
793
+ );
794
+ }
795
+ ```
796
+
797
+ **Also available:** `UFOCustomMarks` component for setting multiple marks at once:
798
+
799
+ ```typescript
800
+ import { UFOCustomMarks } from '@atlaskit/react-ufo/custom-mark';
801
+
802
+ <UFOCustomMarks data={{ 'mark-a': timestamp1, 'mark-b': timestamp2 }} />
803
+ ```
804
+
805
+ ### Placeholder
806
+
807
+ A UFO-instrumented wrapper around lazy-loaded components using Suspense. Provides hold tracking for
808
+ lazy-loaded content.
809
+
810
+ **Import:**
811
+
812
+ ```typescript
813
+ import Placeholder from '@atlaskit/react-ufo/placeholder';
814
+ ```
815
+
816
+ **Props:**
817
+
818
+ | Prop | Type | Required | Description |
819
+ | ---------- | ----------- | -------- | -------------------------------------- |
820
+ | `name` | `string` | Yes | Unique identifier for this placeholder |
821
+ | `children` | `ReactNode` | No | Lazy-loaded components |
822
+ | `fallback` | `ReactNode` | No | Component to show while loading |
823
+
824
+ **Example:**
825
+
826
+ ```typescript
827
+ import Placeholder from '@atlaskit/react-ufo/placeholder';
828
+
829
+ const LazyPanel = lazy(() => import('./Panel'));
830
+
831
+ function App() {
832
+ return (
833
+ <Placeholder name="panel-placeholder" fallback={<PanelSkeleton />}>
834
+ <LazyPanel />
835
+ </Placeholder>
836
+ );
656
837
  }
657
838
  ```
658
839
 
@@ -671,12 +852,20 @@ that need interaction tracking.
671
852
  import usePressTracing from '@atlaskit/react-ufo/use-press-tracing';
672
853
  ```
673
854
 
674
- **Usage:**
855
+ **Signature:**
675
856
 
676
857
  ```typescript
677
- const traceInteraction = usePressTracing('custom-button-click');
858
+ function usePressTracing(name: string): (timeStamp?: number) => void;
678
859
  ```
679
860
 
861
+ **Parameters:**
862
+
863
+ - `name` (string): The UFO interaction name
864
+
865
+ **Returns:**
866
+
867
+ - A function to call when the interaction starts. Accepts an optional `timeStamp` parameter.
868
+
680
869
  **Example:**
681
870
 
682
871
  ```typescript
@@ -684,34 +873,23 @@ import { useCallback } from 'react';
684
873
  import usePressTracing from '@atlaskit/react-ufo/use-press-tracing';
685
874
 
686
875
  function CustomButton({ onAction }) {
687
- const traceInteraction = usePressTracing('custom-action');
876
+ const traceInteraction = usePressTracing('custom-action');
688
877
 
689
- const handleClick = useCallback(
690
- (event) => {
691
- traceInteraction(event.timeStamp);
692
- onAction();
693
- },
694
- [traceInteraction, onAction],
695
- );
878
+ const handleClick = useCallback(
879
+ (event) => {
880
+ traceInteraction(event.timeStamp);
881
+ onAction();
882
+ },
883
+ [traceInteraction, onAction],
884
+ );
696
885
 
697
- return <button onClick={handleClick}>Custom Action</button>;
886
+ return <button onClick={handleClick}>Custom Action</button>;
698
887
  }
699
888
  ```
700
889
 
701
- **Parameters:**
702
-
703
- - `name` (string): The UFO interaction name
704
-
705
- **Returns:**
706
-
707
- - `traceInteraction` (function): Function to call when interaction starts
708
- - `timeStamp` (number, optional): Custom timestamp
709
-
710
890
  **Note:** Results are sent as `inline-result` type events.
711
891
 
712
- ### useUFOTypingPerformanceTracing ⚠️ Experimental
713
-
714
- **Note: This is an experimental feature that may change or be removed in future versions.**
892
+ ### useUFOTypingPerformanceTracing
715
893
 
716
894
  Measures typing latency in input fields to track user experience during text input.
717
895
 
@@ -721,21 +899,29 @@ Measures typing latency in input fields to track user experience during text inp
721
899
  import useUFOTypingPerformanceTracing from '@atlaskit/react-ufo/typing-performance-tracing';
722
900
  ```
723
901
 
724
- **Usage:**
902
+ **Signature:**
725
903
 
726
904
  ```typescript
727
- const typingRef = useUFOTypingPerformanceTracing<HTMLInputElement>('text-input-typing');
905
+ function useUFOTypingPerformanceTracing<T extends HTMLElement>(name: string): React.RefObject<T>;
728
906
  ```
729
907
 
908
+ **Parameters:**
909
+
910
+ - `name` (string): Identifier for the typing performance trace
911
+
912
+ **Returns:**
913
+
914
+ - A React ref to attach to the input element
915
+
730
916
  **Example:**
731
917
 
732
918
  ```typescript
733
919
  import useUFOTypingPerformanceTracing from '@atlaskit/react-ufo/typing-performance-tracing';
734
920
 
735
921
  function CommentEditor() {
736
- const typingRef = useUFOTypingPerformanceTracing<HTMLTextAreaElement>('comment-editor-typing');
922
+ const typingRef = useUFOTypingPerformanceTracing<HTMLTextAreaElement>('comment-editor-typing');
737
923
 
738
- return <textarea ref={typingRef} placeholder="Write a comment..." />;
924
+ return <textarea ref={typingRef} placeholder="Write a comment..." />;
739
925
  }
740
926
  ```
741
927
 
@@ -756,7 +942,7 @@ function CommentEditor() {
756
942
  ### useUFOTransitionCompleter
757
943
 
758
944
  Automatically completes UFO transitions when the component mounts. Use this in destination route
759
- components.
945
+ components to signal that the transition is complete.
760
946
 
761
947
  **Import:**
762
948
 
@@ -764,38 +950,27 @@ components.
764
950
  import { useUFOTransitionCompleter } from '@atlaskit/react-ufo/trace-transition';
765
951
  ```
766
952
 
767
- **Usage:**
768
-
769
- ```typescript
770
- function MyPageComponent() {
771
- useUFOTransitionCompleter();
772
-
773
- return <div>Page content</div>;
774
- }
775
- ```
776
-
777
953
  **Example:**
778
954
 
779
955
  ```typescript
780
956
  import { useUFOTransitionCompleter } from '@atlaskit/react-ufo/trace-transition';
781
957
 
782
958
  function IssueViewPage() {
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
- );
959
+ useUFOTransitionCompleter();
960
+
961
+ return (
962
+ <div>
963
+ <IssueHeader />
964
+ <IssueContent />
965
+ </div>
966
+ );
792
967
  }
793
968
  ```
794
969
 
795
970
  ### useUFOReportError
796
971
 
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.
972
+ A hook that provides a function to report errors within UFO interactions. Errors are automatically
973
+ associated with the currently active UFO interaction.
799
974
 
800
975
  **Import:**
801
976
 
@@ -803,26 +978,15 @@ and track errors that occur during performance-critical user flows.
803
978
  import { useUFOReportError } from '@atlaskit/react-ufo/report-error';
804
979
  ```
805
980
 
806
- **Usage:**
807
-
808
- ```typescript
809
- const reportError = useUFOReportError();
810
- ```
811
-
812
- **Parameters:**
813
-
814
- - None
815
-
816
981
  **Returns:**
817
982
 
818
- - `reportError` (function): Function to report errors to the current UFO interaction
819
- - `error` (object): Error information with the following properties:
820
- - `name` (string): Error name/type
821
- - `errorMessage` (string): Error description
822
- - `errorStack` (string, optional): Error stack trace
823
- - `forcedError` (boolean, optional): Whether this was intentionally triggered
824
- - `errorHash` (string, optional): Custom error identifier
825
- - `errorStatusCode` (number, optional): HTTP status code if applicable
983
+ - A function that accepts an error object with:
984
+ - `name` (string): Error name/type
985
+ - `errorMessage` (string): Error description
986
+ - `errorStack` (string, optional): Error stack trace
987
+ - `forcedError` (boolean, optional): Whether this was intentionally triggered
988
+ - `errorHash` (string, optional): Custom error identifier
989
+ - `errorStatusCode` (number, optional): HTTP status code if applicable
826
990
 
827
991
  **Example:**
828
992
 
@@ -843,7 +1007,7 @@ function DataLoader({ issueId }) {
843
1007
  errorStack: error.stack,
844
1008
  errorStatusCode: error.status,
845
1009
  });
846
- throw error; // Re-throw for component error handling
1010
+ throw error;
847
1011
  }
848
1012
  };
849
1013
 
@@ -851,35 +1015,47 @@ function DataLoader({ issueId }) {
851
1015
  }
852
1016
  ```
853
1017
 
854
- **Advanced Example with Context:**
1018
+ ### useReportTerminalError
855
1019
 
856
- ```typescript
857
- import { useUFOReportError } from '@atlaskit/react-ufo/report-error';
1020
+ A hook that reports a terminal error to UFO when the error is first set. Terminal errors represent
1021
+ critical failures that prevent the user from completing their task — typically rendered via error
1022
+ boundaries.
858
1023
 
859
- function SearchComponent() {
860
- const reportError = useUFOReportError();
1024
+ **Import:**
861
1025
 
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
- };
1026
+ ```typescript
1027
+ import { useReportTerminalError } from '@atlaskit/react-ufo/set-terminal-error';
1028
+ ```
872
1029
 
873
- return <div>{/* Search UI */}</div>;
874
- }
1030
+ **Signature:**
1031
+
1032
+ ```typescript
1033
+ function useReportTerminalError(
1034
+ error: Error | null | undefined,
1035
+ additionalAttributes?: TerminalErrorAdditionalAttributes,
1036
+ ): void;
875
1037
  ```
876
1038
 
877
- **Notes:**
1039
+ **Parameters:**
1040
+
1041
+ - `error` (Error | null | undefined): The error to report. Only reported once when first set.
1042
+ - `additionalAttributes` (optional): See [setTerminalError](#setterminalerror) for details.
1043
+
1044
+ **Example:**
878
1045
 
879
- - Errors are automatically associated with the currently active UFO interaction
880
- - If no interaction is active, errors are added to all active interactions
881
- - Use meaningful error names for better categorization in analytics
882
- - Include stack traces when possible for debugging
1046
+ ```typescript
1047
+ import { useReportTerminalError } from '@atlaskit/react-ufo/set-terminal-error';
1048
+
1049
+ function ErrorBoundaryFallback({ error }) {
1050
+ useReportTerminalError(error, {
1051
+ errorBoundaryId: 'issue-view',
1052
+ fallbackType: 'page',
1053
+ teamName: 'my-team',
1054
+ });
1055
+
1056
+ return <ErrorPage />;
1057
+ }
1058
+ ```
883
1059
 
884
1060
  ---
885
1061
 
@@ -895,10 +1071,10 @@ Programmatically adds custom data to the current UFO interaction without using a
895
1071
  import { addUFOCustomData } from '@atlaskit/react-ufo/custom-data';
896
1072
  ```
897
1073
 
898
- **Usage:**
1074
+ **Signature:**
899
1075
 
900
1076
  ```typescript
901
- addUFOCustomData({ key: 'value', count: 42 });
1077
+ function addUFOCustomData(data: Record<string, unknown>): void;
902
1078
  ```
903
1079
 
904
1080
  **Example:**
@@ -917,9 +1093,7 @@ function performSearch(query) {
917
1093
  }
918
1094
  ```
919
1095
 
920
- ### addUFOCustomCohortData ⚠️ Experimental
921
-
922
- **Note: This is an experimental feature that may change or be removed in future versions.**
1096
+ ### addUFOCustomCohortData
923
1097
 
924
1098
  Programmatically adds cohort data for A/B testing and feature flag tracking.
925
1099
 
@@ -929,17 +1103,20 @@ Programmatically adds cohort data for A/B testing and feature flag tracking.
929
1103
  import { addUFOCustomCohortData } from '@atlaskit/react-ufo/custom-cohort-data';
930
1104
  ```
931
1105
 
932
- **Usage:**
1106
+ **Signature:**
933
1107
 
934
1108
  ```typescript
935
- addUFOCustomCohortData('feature_flag_key', true);
1109
+ function addUFOCustomCohortData(
1110
+ key: string,
1111
+ value: number | boolean | string | null | undefined,
1112
+ ): void;
936
1113
  ```
937
1114
 
938
1115
  **Example:**
939
1116
 
940
1117
  ```typescript
941
1118
  import { addUFOCustomCohortData } from '@atlaskit/react-ufo/custom-cohort-data';
942
- import { fg } from '@atlassian/jira-feature-gating';
1119
+ import { fg } from '@atlaskit/platform-feature-flags';
943
1120
 
944
1121
  function initializeExperiment() {
945
1122
  const isNewDesignEnabled = fg('new_design_system');
@@ -949,6 +1126,99 @@ function initializeExperiment() {
949
1126
  }
950
1127
  ```
951
1128
 
1129
+ ### addUFOCustomMark
1130
+
1131
+ Programmatically records a custom performance mark at a specific point in time.
1132
+
1133
+ **Import:**
1134
+
1135
+ ```typescript
1136
+ import { addUFOCustomMark } from '@atlaskit/react-ufo/custom-mark';
1137
+ ```
1138
+
1139
+ **Signature:**
1140
+
1141
+ ```typescript
1142
+ function addUFOCustomMark(name: string, timestamp?: number): void;
1143
+ ```
1144
+
1145
+ **Parameters:**
1146
+
1147
+ - `name` (string): Name of the mark
1148
+ - `timestamp` (number, optional): Custom timestamp. Defaults to `performance.now()`
1149
+
1150
+ **Example:**
1151
+
1152
+ ```typescript
1153
+ import { addUFOCustomMark } from '@atlaskit/react-ufo/custom-mark';
1154
+
1155
+ function onDataFetched() {
1156
+ addUFOCustomMark('data-fetch-complete');
1157
+ }
1158
+ ```
1159
+
1160
+ ### addCustomSpans
1161
+
1162
+ Records a custom performance span (a time range with a start and end) to all active interactions.
1163
+
1164
+ **Import:**
1165
+
1166
+ ```typescript
1167
+ import { addCustomSpans } from '@atlaskit/react-ufo/custom-spans';
1168
+ ```
1169
+
1170
+ **Signature:**
1171
+
1172
+ ```typescript
1173
+ function addCustomSpans(
1174
+ name: string,
1175
+ start: number,
1176
+ end?: number, // defaults to performance.now()
1177
+ size?: number, // defaults to 0
1178
+ ): void;
1179
+ ```
1180
+
1181
+ **Example:**
1182
+
1183
+ ```typescript
1184
+ import { addCustomSpans } from '@atlaskit/react-ufo/custom-spans';
1185
+
1186
+ async function fetchData() {
1187
+ const start = performance.now();
1188
+ const data = await api.getData();
1189
+ addCustomSpans('api-fetch', start, performance.now(), data.length);
1190
+ return data;
1191
+ }
1192
+ ```
1193
+
1194
+ ### addBM3TimingsToUFO
1195
+
1196
+ Converts BM3 (Browser Metrics 3) timing marks into UFO custom timings. Useful for migrating from BM3
1197
+ instrumentation to UFO.
1198
+
1199
+ **Import:**
1200
+
1201
+ ```typescript
1202
+ import { addBM3TimingsToUFO } from '@atlaskit/react-ufo/custom-timings';
1203
+ ```
1204
+
1205
+ **Signature:**
1206
+
1207
+ ```typescript
1208
+ function addBM3TimingsToUFO(
1209
+ marks?: { [key: string]: number },
1210
+ timingsConfig?: Array<{ key: string; startMark?: string; endMark?: string }>,
1211
+ ): void;
1212
+ ```
1213
+
1214
+ **Also available as a component:**
1215
+
1216
+ ```typescript
1217
+ import { UFOBM3TimingsToUFO } from '@atlaskit/react-ufo/custom-timings';
1218
+
1219
+ <UFOBM3TimingsToUFO marks={marks} timings={timingsConfig} />
1220
+ ```
1221
+
952
1222
  ### addFeatureFlagAccessed
953
1223
 
954
1224
  Records feature flag usage for correlation with performance metrics.
@@ -959,26 +1229,20 @@ Records feature flag usage for correlation with performance metrics.
959
1229
  import { addFeatureFlagAccessed } from '@atlaskit/react-ufo/feature-flags-accessed';
960
1230
  ```
961
1231
 
962
- **Usage:**
1232
+ **Signature:**
963
1233
 
964
1234
  ```typescript
965
- addFeatureFlagAccessed('feature_flag_name', flagValue);
1235
+ function addFeatureFlagAccessed(featureFlagName: string, featureFlagValue: FeatureFlagValue): void;
966
1236
  ```
967
1237
 
968
1238
  **Example:**
969
1239
 
970
1240
  ```typescript
971
1241
  import { addFeatureFlagAccessed } from '@atlaskit/react-ufo/feature-flags-accessed';
972
- import { fg } from '@atlassian/jira-feature-gating';
973
1242
 
974
1243
  function checkFeatureFlag(flagKey) {
975
1244
  const flagValue = fg(flagKey);
976
-
977
- // Record this flag access for performance correlation
978
- if (shouldRecordFeatureFlagForUFO(flagKey)) {
979
- addFeatureFlagAccessed(flagKey, flagValue);
980
- }
981
-
1245
+ addFeatureFlagAccessed(flagKey, flagValue);
982
1246
  return flagValue;
983
1247
  }
984
1248
  ```
@@ -987,7 +1251,7 @@ function checkFeatureFlag(flagKey) {
987
1251
 
988
1252
  ### setInteractionError
989
1253
 
990
- Manually records an error for the current UFO interaction.
1254
+ Manually records an error for a specific named interaction.
991
1255
 
992
1256
  **Import:**
993
1257
 
@@ -995,13 +1259,13 @@ Manually records an error for the current UFO interaction.
995
1259
  import { setInteractionError } from '@atlaskit/react-ufo/set-interaction-error';
996
1260
  ```
997
1261
 
998
- **Usage:**
1262
+ **Signature:**
999
1263
 
1000
1264
  ```typescript
1001
- setInteractionError('interaction-name', {
1002
- errorMessage: 'Something went wrong',
1003
- name: 'CustomError',
1004
- });
1265
+ function setInteractionError(
1266
+ interactionName: string,
1267
+ error: { errorMessage: string; name: string },
1268
+ ): void;
1005
1269
  ```
1006
1270
 
1007
1271
  **Example:**
@@ -1022,9 +1286,84 @@ function loadIssueData(issueId) {
1022
1286
  }
1023
1287
  ```
1024
1288
 
1289
+ ### setTerminalError
1290
+
1291
+ Reports a terminal (critical) error to UFO. Terminal errors represent failures that prevent the user
1292
+ from completing their task, such as errors caught by error boundaries.
1293
+
1294
+ **Import:**
1295
+
1296
+ ```typescript
1297
+ import { setTerminalError } from '@atlaskit/react-ufo/set-terminal-error';
1298
+ ```
1299
+
1300
+ **Signature:**
1301
+
1302
+ ```typescript
1303
+ function setTerminalError(
1304
+ error: Error,
1305
+ additionalAttributes?: TerminalErrorAdditionalAttributes,
1306
+ labelStack?: LabelStack,
1307
+ ): void;
1308
+ ```
1309
+
1310
+ **`TerminalErrorAdditionalAttributes`:**
1311
+
1312
+ | Property | Type | Description |
1313
+ | ---------------------- | ------------------------------ | ---------------------------------------------------------------- |
1314
+ | `teamName` | `string` | Team responsible for the component |
1315
+ | `packageName` | `string` | Package where the error occurred |
1316
+ | `errorBoundaryId` | `string` | Identifier of the error boundary |
1317
+ | `errorHash` | `string` | Custom error identifier for grouping |
1318
+ | `traceId` | `string` | Trace ID for distributed tracing |
1319
+ | `fallbackType` | `'page' \| 'flag' \| 'custom'` | Type of fallback rendered |
1320
+ | `statusCode` | `number` | HTTP status code if applicable |
1321
+ | `isClientNetworkError` | `boolean` | Whether this is a client network error (excluded from reporting) |
1322
+
1323
+ **Example:**
1324
+
1325
+ ```typescript
1326
+ import { setTerminalError } from '@atlaskit/react-ufo/set-terminal-error';
1327
+
1328
+ class AppErrorBoundary extends React.Component {
1329
+ componentDidCatch(error) {
1330
+ setTerminalError(error, {
1331
+ errorBoundaryId: 'app-root',
1332
+ fallbackType: 'page',
1333
+ teamName: 'my-team',
1334
+ packageName: '@atlassian/my-app',
1335
+ });
1336
+ }
1337
+ }
1338
+ ```
1339
+
1340
+ ### sinkTerminalErrorHandler
1341
+
1342
+ Registers a handler function that is called when terminal errors are reported via
1343
+ `setTerminalError`. This is used to configure how terminal error data is consumed (e.g., sent to
1344
+ analytics).
1345
+
1346
+ **Import:**
1347
+
1348
+ ```typescript
1349
+ import { sinkTerminalErrorHandler } from '@atlaskit/react-ufo/set-terminal-error';
1350
+ ```
1351
+
1352
+ **Signature:**
1353
+
1354
+ ```typescript
1355
+ function sinkTerminalErrorHandler(
1356
+ fn: (errorData: TerminalErrorData, context: TerminalErrorContext) => void | Promise<void>,
1357
+ ): void;
1358
+ ```
1359
+
1360
+ The `TerminalErrorContext` provides information about the interaction state when the error occurred,
1361
+ including the active and previous interaction names, IDs, and types.
1362
+
1025
1363
  ### getUFORouteName
1026
1364
 
1027
- Extracts the UFO name from a route configuration object.
1365
+ Extracts the UFO name from a route configuration object. Returns `ufoName` if defined, otherwise
1366
+ falls back to `name`.
1028
1367
 
1029
1368
  **Import:**
1030
1369
 
@@ -1032,10 +1371,10 @@ Extracts the UFO name from a route configuration object.
1032
1371
  import getUFORouteName from '@atlaskit/react-ufo/route-name';
1033
1372
  ```
1034
1373
 
1035
- **Usage:**
1374
+ **Signature:**
1036
1375
 
1037
1376
  ```typescript
1038
- const ufoName = getUFORouteName(route);
1377
+ function getUFORouteName(route: { ufoName?: string; name: string }): string;
1039
1378
  ```
1040
1379
 
1041
1380
  **Example:**
@@ -1058,6 +1397,42 @@ function RouteAnalytics() {
1058
1397
  }
1059
1398
  ```
1060
1399
 
1400
+ ### updatePageloadName
1401
+
1402
+ Updates the UFO name of an active page load or transition interaction. Useful when the route name
1403
+ isn't known at the time `traceUFOPageLoad` is first called.
1404
+
1405
+ **Import:**
1406
+
1407
+ ```typescript
1408
+ import { updatePageloadName } from '@atlaskit/react-ufo/trace-pageload';
1409
+ ```
1410
+
1411
+ **Signature:**
1412
+
1413
+ ```typescript
1414
+ function updatePageloadName(
1415
+ ufoName: string,
1416
+ routeName?: string | null | undefined, // defaults to ufoName
1417
+ ): void;
1418
+ ```
1419
+
1420
+ **Example:**
1421
+
1422
+ ```typescript
1423
+ import { updatePageloadName } from '@atlaskit/react-ufo/trace-pageload';
1424
+
1425
+ function RouteResolver({ routeName }) {
1426
+ useEffect(() => {
1427
+ if (routeName) {
1428
+ updatePageloadName(routeName);
1429
+ }
1430
+ }, [routeName]);
1431
+
1432
+ return null;
1433
+ }
1434
+ ```
1435
+
1061
1436
  ---
1062
1437
 
1063
1438
  ## Component Integration
@@ -1067,52 +1442,36 @@ function RouteAnalytics() {
1067
1442
  Many Atlassian Design System components support an `interactionName` prop that automatically adds
1068
1443
  UFO tracking.
1069
1444
 
1070
- **Supported Components:**
1071
-
1072
1445
  **@atlaskit/button:**
1073
1446
 
1074
1447
  ```typescript
1075
1448
  import Button from '@atlaskit/button';
1076
1449
 
1077
- <Button interactionName="save-issue">Save</Button>;
1450
+ // Triggers a UFO press interaction on click
1451
+ <Button interactionName="save-issue" onClick={handleSave}>
1452
+ Save
1453
+ </Button>
1078
1454
  ```
1079
1455
 
1456
+ When no `interactionName` is provided, the button defaults to `unknown`. Unknown interactions are
1457
+ tracked for volume purposes but are **not** aggregated into metrics.
1458
+
1080
1459
  **@atlaskit/spinner:**
1081
1460
 
1082
1461
  ```typescript
1083
1462
  import Spinner from '@atlaskit/spinner';
1084
1463
 
1085
- <Spinner interactionName="loading-comments" size="medium" />;
1086
- ```
1087
-
1088
- **Examples:**
1089
-
1090
- ```typescript
1091
- // Button with interaction tracking
1092
- <Button interactionName="create-issue" onClick={handleCreateIssue}>
1093
- Create Issue
1094
- </Button>;
1095
-
1096
- // Spinner that holds interaction until unmounted
1097
- {
1098
- isLoading && <Spinner interactionName="issue-loading" size="large" />;
1099
- }
1464
+ // Adds a UFO hold while the spinner is mounted
1465
+ <Spinner interactionName="loading-comments" size="medium" />
1100
1466
  ```
1101
1467
 
1102
- **Notes:**
1103
-
1104
- - Button: Triggers a UFO press interaction on click
1105
- - Spinner: Adds a UFO hold while the spinner is mounted
1106
- - Use descriptive, unique names for each interaction
1107
-
1108
1468
  ---
1109
1469
 
1110
1470
  ## Advanced APIs
1111
1471
 
1112
1472
  ### SSR Configuration
1113
1473
 
1114
- Configure UFO for Server-Side Rendering scenarios. This will produce a more accurate FMP Topline
1115
- Metric in Glance.
1474
+ Configure UFO for Server-Side Rendering scenarios. This produces a more accurate FMP metric.
1116
1475
 
1117
1476
  **Import:**
1118
1477
 
@@ -1120,22 +1479,21 @@ Metric in Glance.
1120
1479
  import { configure } from '@atlaskit/react-ufo/ssr';
1121
1480
  ```
1122
1481
 
1123
- **Usage:**
1482
+ **Signature:**
1124
1483
 
1125
1484
  ```typescript
1126
- configure({
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
- }),
1138
- });
1485
+ type SSRConfig = {
1486
+ getDoneMark: () => number | null;
1487
+ getFeatureFlags: () => SSRFeatureFlags | null;
1488
+ getTimings?: () => ReportedTimings | null;
1489
+ getSsrPhaseSuccess?: () => {
1490
+ prefetch?: boolean;
1491
+ earlyFlush?: boolean;
1492
+ done?: boolean;
1493
+ };
1494
+ };
1495
+
1496
+ function configure(ssrConfig: SSRConfig): void;
1139
1497
  ```
1140
1498
 
1141
1499
  **Configuration Options:**
@@ -1143,32 +1501,71 @@ configure({
1143
1501
  - `getDoneMark()`: Returns timestamp when SSR rendering completed
1144
1502
  - `getFeatureFlags()`: Returns feature flags used during SSR
1145
1503
  - `getTimings()`: Returns detailed SSR timing breakdown
1146
- - `getSsrPhaseSuccess()`: Returns success status for SSR phases
1504
+ - `getSsrPhaseSuccess()`: Returns success status for each SSR phase
1505
+
1506
+ **Additional SSR exports:**
1147
1507
 
1148
- ### Third-Party Segments
1508
+ ```typescript
1509
+ import {
1510
+ getSSRTimings,
1511
+ getSSRSuccess,
1512
+ getSSRPhaseSuccess,
1513
+ getSSRDoneTime,
1514
+ getSSRFeatureFlags,
1515
+ } from '@atlaskit/react-ufo/ssr';
1516
+ ```
1517
+
1518
+ ### Configuration
1149
1519
 
1150
- Special segment type for tracking external or plugin content separately from first-party code.
1520
+ Set the global UFO configuration for your product.
1151
1521
 
1152
1522
  **Import:**
1153
1523
 
1154
1524
  ```typescript
1155
- import { UFOThirdPartySegment } from '@atlaskit/react-ufo/segment';
1525
+ import { setUFOConfig, getConfig, isUFOEnabled } from '@atlaskit/react-ufo/config';
1156
1526
  ```
1157
1527
 
1158
- **Usage:**
1528
+ **Key configuration fields:**
1529
+
1530
+ | Field | Type | Description |
1531
+ | ---------------------------------------------- | ------------------------ | ----------------------------------------------------------- |
1532
+ | `enabled` | `boolean` | Whether UFO is enabled |
1533
+ | `product` | `string` | Product identifier (e.g., `'jira'`, `'confluence'`) |
1534
+ | `region` | `string` | Deployment region |
1535
+ | `rates` | `Rates` | Sampling rates for different interaction types |
1536
+ | `interactionTimeout` | `Record<string, number>` | Custom timeouts per interaction |
1537
+ | `minorInteractions` | `string[]` | Interactions classified as minor |
1538
+ | `doNotAbortActivePressInteraction` | `string[]` | Press interactions that should not be aborted |
1539
+ | `doNotAbortActivePressInteractionOnTransition` | `string[]` | Press interactions that should not be aborted on transition |
1540
+ | `finishInteractionOnTransition` | `string[]` | Interactions to finish (not abort) on transition |
1541
+ | `vc` | `object` | Visual Completion observer configuration |
1542
+ | `terminalErrors` | `object` | Terminal error reporting configuration |
1543
+
1544
+ ### Experience Trace ID Context
1545
+
1546
+ Manage distributed tracing context for UFO interactions. Enables correlation of frontend performance
1547
+ data with backend traces.
1548
+
1549
+ **Import:**
1159
1550
 
1160
1551
  ```typescript
1161
- <UFOThirdPartySegment name="external-calendar-widget">
1162
- <CalendarWidget />
1163
- </UFOThirdPartySegment>
1552
+ import {
1553
+ getActiveTrace,
1554
+ setActiveTrace,
1555
+ clearActiveTrace,
1556
+ getActiveTraceHttpRequestHeaders,
1557
+ getActiveTraceAsQueryParams,
1558
+ } from '@atlaskit/react-ufo/experience-trace-id-context';
1164
1559
  ```
1165
1560
 
1166
- **Use Cases:**
1561
+ **Key Functions:**
1167
1562
 
1168
- - External service integrations
1169
- - Third-party plugins or widgets
1170
- - Partner-provided components
1171
- - Any content not directly controlled by your team
1563
+ - `getActiveTrace()`: Returns the current active trace context
1564
+ - `setActiveTrace(traceId, spanId, type)`: Sets the active trace
1565
+ - `clearActiveTrace()`: Clears the active trace
1566
+ - `getActiveTraceHttpRequestHeaders(url?)`: Returns `X-B3-TraceId` and `X-B3-SpanId` headers for
1567
+ HTTP requests
1568
+ - `getActiveTraceAsQueryParams(url?)`: Returns trace context as query parameters
1172
1569
 
1173
1570
  ---
1174
1571
 
@@ -1183,29 +1580,37 @@ import { UFOThirdPartySegment } from '@atlaskit/react-ufo/segment';
1183
1580
 
1184
1581
  ### Performance Considerations
1185
1582
 
1186
- - Don't over-instrument - focus on key user journeys
1583
+ - Don't over-instrument focus on key user journeys
1187
1584
  - Use segments for logical page sections (like header, content, sidebar)
1188
1585
  - Be selective with custom data to avoid large payloads
1189
1586
  - Prefer component-level holds over page-level holds for granular tracking
1190
- - Use meaningful, descriptive names that help identify performance bottlenecks
1191
- - Consider using UFOThirdPartySegment for external content that may impact performance
1587
+ - Use `UFOThirdPartySegment` for external content that may impact performance
1588
+ - Use `UFOInteractionIgnore` for non-critical components that load late
1192
1589
 
1193
- ### Debugging
1590
+ ### Interaction Lifecycle
1194
1591
 
1195
- - Use browser dev tools to inspect UFO events
1196
- - Check network tab for UFO payload requests (look for analytics events)
1197
- - Use UFO labels to identify problematic code areas
1198
- - Monitor UFO events in your application's analytics dashboard
1199
- - Enable verbose logging in development to see interaction lifecycle
1200
- - Use meaningful names for segments and holds to quickly identify issues
1201
- - Test with feature flags to ensure performance tracking works across variants
1202
- - Pay attention to "holds" that never release - they prevent interactions from completing
1592
+ 1. An interaction starts via `traceUFOPageLoad`, `traceUFOTransition`, `traceUFOPress`, or
1593
+ `traceUFOHover`
1594
+ 2. `UFOLoadHold` components register active holds that prevent completion
1595
+ 3. The interaction completes (TTAI) at the next paint after all holds are released
1596
+ 4. VC metrics are calculated based on viewport mutations during the interaction window
1597
+ 5. Only one interaction can be active at a time new interactions abort previous ones
1203
1598
 
1204
- ### Migration Guide
1599
+ ### Feature Gating
1205
1600
 
1206
- - Use `interactionName` props on ADS components when possible
1207
- - Migrate from manual event tracking to UFO components
1208
- - Consider SSR implications when adding new instrumentation
1601
+ All changes to React UFO should use feature gates:
1602
+
1603
+ ```typescript
1604
+ import { fg } from '@atlaskit/platform-feature-flags';
1605
+
1606
+ if (fg('my_feature_gate')) {
1607
+ // New behavior
1608
+ } else {
1609
+ // Existing behavior
1610
+ }
1611
+ ```
1612
+
1613
+ Register feature gates in `package.json` under `platform-feature-flags`.
1209
1614
 
1210
1615
  ---
1211
1616
 
@@ -1215,15 +1620,15 @@ import { UFOThirdPartySegment } from '@atlaskit/react-ufo/segment';
1215
1620
 
1216
1621
  ```typescript
1217
1622
  function MyPage() {
1218
- useEffect(() => {
1219
- traceUFOPageLoad('my-page');
1220
- }, []);
1221
-
1222
- return (
1223
- <UFOSegment name="main-content">
1224
- <PageContent />
1225
- </UFOSegment>
1226
- );
1623
+ useEffect(() => {
1624
+ traceUFOPageLoad('my-page');
1625
+ }, []);
1626
+
1627
+ return (
1628
+ <UFOSegment name="main-content">
1629
+ <PageContent />
1630
+ </UFOSegment>
1631
+ );
1227
1632
  }
1228
1633
  ```
1229
1634
 
@@ -1231,19 +1636,19 @@ function MyPage() {
1231
1636
 
1232
1637
  ```typescript
1233
1638
  function DataComponent() {
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
- );
1639
+ const [loading, setLoading] = useState(true);
1640
+
1641
+ return (
1642
+ <UFOSegment name="data-section">
1643
+ {loading ? (
1644
+ <UFOLoadHold name="data-loading">
1645
+ <Skeleton />
1646
+ </UFOLoadHold>
1647
+ ) : (
1648
+ <DataTable />
1649
+ )}
1650
+ </UFOSegment>
1651
+ );
1247
1652
  }
1248
1653
  ```
1249
1654
 
@@ -1251,19 +1656,37 @@ function DataComponent() {
1251
1656
 
1252
1657
  ```typescript
1253
1658
  function NavigationWrapper() {
1254
- const [routerState] = useRouter();
1659
+ const [routerState] = useRouter();
1255
1660
 
1256
- useEffect(() => {
1257
- const ufoName = getUFORouteName(routerState.route);
1258
- if (ufoName) {
1259
- traceUFOTransition(ufoName);
1260
- }
1261
- }, [routerState]);
1661
+ useEffect(() => {
1662
+ const ufoName = getUFORouteName(routerState.route);
1663
+ if (ufoName) {
1664
+ traceUFOTransition(ufoName);
1665
+ }
1666
+ }, [routerState]);
1262
1667
 
1263
- return <Router />;
1668
+ return <Router />;
1264
1669
  }
1265
1670
  ```
1266
1671
 
1672
+ ### Error Boundary with Terminal Error Reporting
1673
+
1674
+ ```typescript
1675
+ import { useReportTerminalError } from '@atlaskit/react-ufo/set-terminal-error';
1676
+
1677
+ function ErrorBoundaryFallback({ error }) {
1678
+ useReportTerminalError(error, {
1679
+ errorBoundaryId: 'main-content',
1680
+ fallbackType: 'page',
1681
+ teamName: 'my-team',
1682
+ });
1683
+
1684
+ return <ErrorPage message="Something went wrong" />;
1685
+ }
1686
+ ```
1687
+
1688
+ ---
1689
+
1267
1690
  ## Troubleshooting
1268
1691
 
1269
1692
  ### Common Issues
@@ -1273,21 +1696,41 @@ function NavigationWrapper() {
1273
1696
  - Check for holds that are never released (components that mount `UFOLoadHold` but never unmount)
1274
1697
  - Verify all async operations complete properly
1275
1698
  - Ensure error boundaries don't prevent holds from releasing
1699
+ - Use `UFOInteractionIgnore` for non-critical components that load late
1276
1700
 
1277
1701
  **Missing Analytics Events**
1278
1702
 
1279
- - Verify UFO configuration is properly set up in your application
1703
+ - Verify UFO configuration is properly set up via `setUFOConfig`
1280
1704
  - Check that sampling rates are configured for your interaction names
1281
1705
  - Ensure you're calling trace functions early in the component lifecycle
1282
1706
 
1707
+ **Aborted Interactions**
1708
+
1709
+ - Only one interaction can be active at a time
1710
+ - A new press interaction aborts the previous interaction
1711
+ - Aborted interactions do **not** report metrics to prevent data skewing
1712
+ - Check `doNotAbortActivePressInteraction` config if your interaction is unexpectedly aborted
1713
+
1283
1714
  **Performance Impact**
1284
1715
 
1285
- - Avoid over-instrumentation - focus on critical user journeys
1716
+ - Avoid over-instrumentation focus on critical user journeys
1286
1717
  - Use feature flags to control UFO instrumentation in production
1287
1718
  - Monitor payload sizes if adding extensive custom data
1288
1719
 
1289
1720
  **SSR Compatibility**
1290
1721
 
1291
1722
  - Use the SSR configuration APIs for server-side rendered applications
1292
- - Ensure UFO components handle server-side rendering gracefully
1723
+ - Confluence uses `window.performance.mark("CFP-63.ssr-ttr")` for SSR timing via `getDoneMark`
1293
1724
  - Test SSR placeholder behavior with your specific setup
1725
+
1726
+ ---
1727
+
1728
+ ## Additional Resources
1729
+
1730
+ - [React UFO Overview](https://hello.atlassian.net/wiki/spaces/UFO/pages/2305847386/react-ufo+UFO+v2)
1731
+ - [How React UFO calculates metrics](https://hello.atlassian.net/wiki/spaces/APD/pages/5046492124)
1732
+ - [React UFO Contribution Guidelines](https://hello.atlassian.net/wiki/spaces/APD/pages/4717459313)
1733
+ - [Press Interactions Overview](https://hello.atlassian.net/wiki/spaces/APD/pages/5770720615)
1734
+ - [Interactivity Metrics](https://hello.atlassian.net/wiki/spaces/APD/pages/5026355203)
1735
+ - [React UFO for Platform Components](https://hello.atlassian.net/wiki/spaces/APD/pages/6170852473)
1736
+ - [React UFO: A Deeper Understanding](https://hello.atlassian.net/wiki/spaces/UFO/blog/2022/12/16/2280380649/react-UFO+A+deeper+understanding+of+performance)