or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

additional-web-vitals.mdattribution.mdcore-web-vitals.mdindex.mdthresholds.md

thresholds.mddocs/

0

# Thresholds

1

2

Pre-defined threshold constants for determining metric ratings according to Web Vitals standards. These thresholds classify metric values as "good", "needs improvement", or "poor" based on real-world performance data and user experience research.

3

4

## Capabilities

5

6

### Threshold Constants

7

8

Each Web Vitals metric has associated threshold constants that define the boundaries between performance ratings.

9

10

```typescript { .api }

11

/**

12

* CLS thresholds: [0.1, 0.25]

13

* - ≤ 0.1: good

14

* - 0.1 - 0.25: needs improvement

15

* - > 0.25: poor

16

*/

17

const CLSThresholds: MetricRatingThresholds;

18

19

/**

20

* FCP thresholds: [1800, 3000] (milliseconds)

21

* - ≤ 1800ms: good

22

* - 1800ms - 3000ms: needs improvement

23

* - > 3000ms: poor

24

*/

25

const FCPThresholds: MetricRatingThresholds;

26

27

/**

28

* INP thresholds: [200, 500] (milliseconds)

29

* - ≤ 200ms: good

30

* - 200ms - 500ms: needs improvement

31

* - > 500ms: poor

32

*/

33

const INPThresholds: MetricRatingThresholds;

34

35

/**

36

* LCP thresholds: [2500, 4000] (milliseconds)

37

* - ≤ 2500ms: good

38

* - 2500ms - 4000ms: needs improvement

39

* - > 4000ms: poor

40

*/

41

const LCPThresholds: MetricRatingThresholds;

42

43

/**

44

* TTFB thresholds: [800, 1800] (milliseconds)

45

* - ≤ 800ms: good

46

* - 800ms - 1800ms: needs improvement

47

* - > 1800ms: poor

48

*/

49

const TTFBThresholds: MetricRatingThresholds;

50

51

type MetricRatingThresholds = [number, number];

52

```

53

54

## Usage Examples

55

56

### Import and Use Thresholds

57

58

```typescript

59

import {

60

CLSThresholds,

61

FCPThresholds,

62

INPThresholds,

63

LCPThresholds,

64

TTFBThresholds

65

} from "web-vitals";

66

67

// Check threshold values

68

console.log('CLS thresholds:', CLSThresholds); // [0.1, 0.25]

69

console.log('FCP thresholds:', FCPThresholds); // [1800, 3000]

70

console.log('INP thresholds:', INPThresholds); // [200, 500]

71

console.log('LCP thresholds:', LCPThresholds); // [2500, 4000]

72

console.log('TTFB thresholds:', TTFBThresholds); // [800, 1800]

73

```

74

75

### Manual Rating Calculation

76

77

Although the `Metric.rating` property automatically provides ratings, you can manually calculate ratings using the thresholds:

78

79

```typescript

80

import { LCPThresholds } from "web-vitals";

81

82

function calculateRating(value: number, thresholds: MetricRatingThresholds): string {

83

if (value <= thresholds[0]) {

84

return 'good';

85

} else if (value <= thresholds[1]) {

86

return 'needs-improvement';

87

} else {

88

return 'poor';

89

}

90

}

91

92

// Example usage

93

const lcpValue = 3200; // 3.2 seconds

94

const rating = calculateRating(lcpValue, LCPThresholds);

95

console.log('LCP rating:', rating); // 'needs-improvement'

96

```

97

98

### Custom Threshold Analysis

99

100

```typescript

101

import { onLCP, LCPThresholds } from "web-vitals";

102

103

onLCP((metric) => {

104

const [goodThreshold, poorThreshold] = LCPThresholds;

105

106

console.log(`LCP: ${metric.value}ms (${metric.rating})`);

107

108

if (metric.rating === 'poor') {

109

const improvement = metric.value - poorThreshold;

110

console.log(`Need to improve by ${improvement}ms to reach 'needs-improvement'`);

111

} else if (metric.rating === 'needs-improvement') {

112

const improvement = metric.value - goodThreshold;

113

console.log(`Need to improve by ${improvement}ms to reach 'good'`);

114

}

115

});

116

```

117

118

### Threshold-Based Monitoring

119

120

```typescript

121

import {

122

onCLS, onFCP, onINP, onLCP, onTTFB,

123

CLSThresholds, FCPThresholds, INPThresholds, LCPThresholds, TTFBThresholds

124

} from "web-vitals";

125

126

// Monitor metrics and alert on poor performance

127

const thresholds = {

128

CLS: CLSThresholds,

129

FCP: FCPThresholds,

130

INP: INPThresholds,

131

LCP: LCPThresholds,

132

TTFB: TTFBThresholds

133

};

134

135

function monitorMetric(metricName: string, metric: any) {

136

const threshold = thresholds[metricName as keyof typeof thresholds];

137

138

if (metric.rating === 'poor') {

139

console.warn(`⚠️ Poor ${metricName}: ${metric.value} (threshold: >${threshold[1]})`);

140

141

// Send alert to monitoring system

142

sendAlert({

143

metric: metricName,

144

value: metric.value,

145

rating: metric.rating,

146

threshold: threshold[1]

147

});

148

}

149

}

150

151

// Set up monitoring

152

onCLS(metric => monitorMetric('CLS', metric));

153

onFCP(metric => monitorMetric('FCP', metric));

154

onINP(metric => monitorMetric('INP', metric));

155

onLCP(metric => monitorMetric('LCP', metric));

156

onTTFB(metric => monitorMetric('TTFB', metric));

157

```

158

159

## Rating System Details

160

161

### Rating Categories

162

163

Each metric value falls into one of three categories:

164

165

- **Good**: Represents a great user experience

166

- **Needs Improvement**: Indicates room for optimization

167

- **Poor**: Suggests significant performance issues

168

169

### Threshold Methodology

170

171

The threshold values are based on:

172

173

1. **Real-world data** from Chrome User Experience Report (CrUX)

174

2. **User experience research** correlating performance with user satisfaction

175

3. **75th percentile** targets for good performance

176

4. **Statistical analysis** of millions of web pages

177

178

### Metric-Specific Considerations

179

180

#### CLS (Cumulative Layout Shift)

181

- **Unit**: Unitless score (0 = no shifts, higher = more shifts)

182

- **Good**: ≤ 0.1 (minimal unexpected layout shifts)

183

- **Poor**: > 0.25 (significant visual instability)

184

185

#### FCP (First Contentful Paint)

186

- **Unit**: Milliseconds from navigation start

187

- **Good**: ≤ 1.8s (fast content appearance)

188

- **Poor**: > 3s (slow content appearance)

189

190

#### INP (Interaction to Next Paint)

191

- **Unit**: Milliseconds of interaction latency

192

- **Good**: ≤ 200ms (imperceptible delay)

193

- **Poor**: > 500ms (noticeable delay)

194

195

#### LCP (Largest Contentful Paint)

196

- **Unit**: Milliseconds from navigation start

197

- **Good**: ≤ 2.5s (fast loading of main content)

198

- **Poor**: > 4s (slow loading of main content)

199

200

#### TTFB (Time to First Byte)

201

- **Unit**: Milliseconds from navigation start

202

- **Good**: ≤ 800ms (fast server response)

203

- **Poor**: > 1.8s (slow server response)

204

205

## Important Notes

206

207

### Automatic Rating

208

209

The Web Vitals library automatically calculates and provides the rating in the `metric.rating` property, so manual threshold checking is typically unnecessary:

210

211

```typescript

212

import { onLCP } from "web-vitals";

213

214

onLCP((metric) => {

215

// Rating is automatically calculated

216

console.log('LCP rating:', metric.rating); // 'good', 'needs-improvement', or 'poor'

217

218

// No need to manually check thresholds

219

// const rating = metric.value <= LCPThresholds[0] ? 'good' :

220

// metric.value <= LCPThresholds[1] ? 'needs-improvement' : 'poor';

221

});

222

```

223

224

### Threshold Updates

225

226

Threshold values may be updated in future versions of the library to reflect evolving web performance standards and user expectations. Always use the exported constants rather than hardcoding values.

227

228

### Cross-Metric Analysis

229

230

```typescript

231

import { onLCP, onCLS, onINP } from "web-vitals";

232

233

const metrics = {

234

lcp: null as any,

235

cls: null as any,

236

inp: null as any

237

};

238

239

onLCP(metric => metrics.lcp = metric);

240

onCLS(metric => metrics.cls = metric);

241

onINP(metric => metrics.inp = metric);

242

243

// Analyze overall page performance

244

setTimeout(() => {

245

const goodMetrics = Object.values(metrics)

246

.filter(metric => metric && metric.rating === 'good').length;

247

248

const totalMetrics = Object.values(metrics)

249

.filter(metric => metric !== null).length;

250

251

console.log(`Overall performance: ${goodMetrics}/${totalMetrics} metrics are good`);

252

}, 5000);

253

```