ForkSnowflake (Arctic)Snowflake (Arctic)published Oct 8, 2025seen Jun 26

Snowflake-Labs/ttvc

forked from dropbox/ttvc

Open original ↗

Captured source

source ↗
published Oct 8, 2025seen Jun 26captured 2whttp 200method plain

Snowflake-Labs/ttvc

Description: Measure Visually Complete metrics in real time

License: Apache-2.0

Stars: 1

Forks: 0

Open issues: 3

Created: 2025-10-08T05:38:27Z

Pushed: 2025-11-04T23:57:14Z

Default branch: main

Fork: yes

Parent repository: dropbox/ttvc

Archived: no

README:

ttvc

  • [Overview](#overview)
  • [Get started](#get-started)
  • [Usage](#usage)
  • [Basic usage](#basic-usage)
  • [Report metrics to a collection endpoint](#report-metrics-to-a-collection-endpoint)
  • [Record a PerformanceTimeline entry](#record-a-performancetimeline-entry)
  • [Client-side navigation with React Router](#client-side-navigation-with-react-router)
  • [Attributing TTVC measurement cancellations](#attributing-ttvc-measurement-cancellations)
  • [API](#api)
  • [Types](#types)
  • [Functions](#functions)
  • [Browser Support](#browser-support)
  • [How does it work?](#how-does-it-work)
  • [Developing](#developing)
  • [Building](#building)
  • [Testing](#testing)
  • [Releasing](#releasing)

Overview

ttvc provides an in-browser implementation of the VisuallyComplete metric suitable for field data collection (real-user monitoring).

Visually Complete measures the moment in time when users perceive that all the visual elements of a page have completely loaded. Once the page has reached visually complete, nothing else should change in the viewport without the user’s input.

Get started

This library is available from npm. Add it to your project using the npm or yarn package managers.

$ npm install @dropbox/ttvc
$ yarn add @dropbox/ttvc

Usage

Basic usage

import {init, onTTVC} from '@dropbox/ttvc';

// Call this as early in pageload as possible to setup instrumentation.
init({
debug: false,
idleTimeout: 2000,
networkTimeout: 0,
});

// Reports the last visible change for each navigation that
// occurs during the life of this document.
const unsubscribe = onTTVC((measurement) => {
console.log('TTVC:', measurement.duration);
});

Report metrics to a collection endpoint

import {init, onTTVC} from '@dropbox/ttvc';

init();

let measurements = [];

// capture measurements in client
onTTVC((measurement) => {
measurements.append(measurement);
});

// flush data to server when page is hidden
document.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'hidden') {
navigator.sendBeacon('/log', JSON.stringify(measurements));
measurements = [];
}
});

Record a PerformanceTimeline entry

Capture a span using the Performance Timeline API.

NOTE: Setting arbitrary start and end times with performance.measure relies on the User Timing Level 3 specification. This is not yet adopted by all major browsers.

import {init, onTTVC} from '@dropbox/ttvc';

init();

onTTVC(({start, end, duration, detail}: Metric) => {
window.performance.measure('TTVC', {
start,
end,
duration,
detail,
});
});

Client-side navigation with React Router

@dropbox/ttvc supports measuring client-side navigations!

What counts as navigation may be different in each application, but as long as you signal that a navigation has begun, this library can figure out the rest.

To trigger a new navigation measurement, call start() or dispatch a "locationchange" event on the window object.

// analytics.js
import {init, onTTVC} from '@dropbox/ttvc';

init();

onTTVC((measurement) => {
console.log('TTVC:', measurement.duration);
});
// app.js
import {start} from '@dropbox/ttvc';
import React, {useEffect} from 'react';
import ReactDOM from 'react-dom';
import {BrowserRouter, useLocation} from 'react-router-dom';

ReactDOM.render(


,
document.getElementById('root')
);

const App = () => {
const location = useLocation();

useEffect(() => {
// Option 1: If you have access to the ttvc library, import it and
// call start().
start();

// Option 2: Dispatch a custom 'locationchange' event. TTVC subscribes to
// this and will call start() for you.
window.dispatchEvent(new Event('locationchange'));
}, [location]);

return (

Welcome to React Router!


} />
{/* ... more routes */}


);
};

Attributing TTVC measurement cancellations

In certain cases, @dropbox/ttvc might discard the measurement before it is captured.

This can happen if a user interacts with or navigates away from the page, or the page is put in the background before it has reached a state ot visual completeness.

This is done to obtain a higher confidence of the measurement's accuracy, as interaction with a page can cause it to change in ways that invalidate the measurement.

However, @dropbox/ttvc provides a way to monitor these cancellations and attribute them to a specific cause. A second callback function provided to onTTVC will be called when the measurement is cancelled.

import {init, onTTVC} from '@dropbox/ttvc';

init();

onTTVC(
(measurement) => {
console.log('TTVC measurement captured:', measurement.duration);
},
(error) => {
console.log('TTVC measurement cancelled:', error.cancellationReason);
}
);

API

Types

Metric

export type Metric = {
// time since timeOrigin that the navigation was triggered
// (this will be 0 for the initial pageload)
start: number;

// time since timeOrigin that ttvc was marked for the current navigation
end: number;

// the difference between start and end; this is the value of "TTVC"
duration: number;

// additional metadata related to the current navigation
detail: {
// if ttvc ignored a stalled network request, this value will be true
didNetworkTimeOut: boolean;

// the most recent visible update
// (this can be either a mutation or a load event target, whichever
// occurred last)
lastVisibleChange?: HTMLElement | TimestampedMutationRecord;

// describes how the navigation being measured was initiated
// NOTE: this extends the navigation type values defined in the W3 spec;
// "script" is usually reported as "navigation" by the browser, but we
// report that distinctly
// @see https://developer.mozilla.org/en-US/docs/Web/API/PerformanceNavigationTiming/type
navigationType: // Navigation started by clicking a link, by entering the
// URL in the browser's address bar, or by form submission.
| 'navigate'
// Navigation is through the browser's reload operation.
| 'reload'
// Navigation is through the browser's history traversal operation.
| 'back_forward'
// Navigation is initiated...

Excerpt shown — open the source for the full document.