How INP works
Which clicks, taps and key presses INP counts, how one value is picked for a visit, why good is 200 ms, and why INP replaced FID, recorded in Chrome.
- 01Tell what countsSay which of a visitor's actions are interactions for INP, and why pointing and scrolling aren't.
- 02Pick a visit's INPChoose one value from many interactions, skipping one for every 50, the way web-vitals and Chrome do.
- 03Read the ratings, and what FID missedRate an INP against 200 and 500 ms, say where those lines come from, and explain why FID wasn't enough.
One number for a whole visit
Asha is on the Leaf and Kettle tea list, on her laptop. It has a touch screen, so sometimes she taps instead of clicking.
She does 15 things. She points at Mint tea to read about it. She scrolls down and back up. She clicks Add on Green tea, and taps Add on Mint tea. She swipes the list up and down with a finger. She taps the search box, types "chai" and deletes it. Then she types "mint" and deletes that too. She taps Sort by price. Last, she points at Earl Grey and clicks its Add button.
Almost everything answers at once. But when she taps Sort by price, nothing happens. The list sits still for about a third of a second. Then it jumps into its new order. A frame is one picture the browser draws on the screen. Here is her screen at that tap, in real frames Chrome captured:
Ravi builds the shop's site. He reads its numbers in the Chrome User Experience Report, CrUX: Chrome's record of how real visits went (lesson 10.1). For how quickly a page answers, Chrome records one number for each visit: its Interaction to Next Paint, or INP.
So Ravi wonders. Asha's visit had many quick moments and one slow one. Which single number did it send? And why that one?
Here is her visit in three numbers, as Chrome counted them:
// Asha's visit to the tea list, as Chrome counted it.const visit = { events: 309, interactions: 21, slowest: 304 }; console.log(`Chrome timed ${visit.events} events.`);console.log(`They made ${visit.interactions} interactions.`);console.log(`The slowest took ${visit.slowest} ms. That's her INP.`);- Line 1 is a comment.
- Line 2 holds three numbers.
eventsis how many input events Chrome timed: every pointer, mouse, touch, key and text event.interactionsis how many clicks, taps and key presses there were.slowestis the slowest interaction, in milliseconds (ms). - Lines 4 to 6 print
Chrome timed 309 events.,They made 21 interactions.andThe slowest took 304 ms. That's her INP.
So the short answer is 304 ms: the tap on Sort by price. The long answer takes four steps, and each section below takes one.
- Which of her 15 actions count?
- How do 21 interactions become one number?
- Is 304 ms good?
- Why did the old metric, FID, never notice taps like this one?
A library gives a feel for the whole idea:
You spend an afternoon at the library. Some of the time, you walk along the shelves and look at covers. Nobody times that. But each time you ask the librarian something, you wait for her to look up and say "one moment". Later, a friend asks how the help desk was. You don't count the browsing. You remember the question you waited longest for.
- In real life: Asking the librarian a question
- In INP: A click, a tap or a key press: an interaction
- In real life: Her looking up to say "one moment"
- In INP: The next frame: the page shows it heard you
- In real life: Walking along the shelves, looking at covers
- In INP: Scrolling and pointing: not interactions
- In real life: The question you waited longest for
- In INP: The visit's INP
INP is one number for a whole visit: the slowest click, tap or key press, timed from the input to the next frame. On long visits, it skips one for every 50 interactions. Chrome records it for each visit. CrUX reports the 75th percentile of many visits: the value three in four visits were at or under. Good is 200 ms or less.
How the course recorded it
- Asha's laptop: Chrome 157, in a window 412 by 480 pixels, with a touch screen. Playwright, a tool that drives a real browser, made every move. It used Chrome's own input: the mouse, the trackpad, taps, swipes and the keyboard.
- The page: the tea list from lesson 25.1, with a search box, a Sort by price button and an Add button on every tea. Some of its event handlers, the code that runs when an event happens, are slow on purpose. Pointing at a tea's name runs 120 ms of work, each Add 90 ms, each letter in the search box 30 ms, and Sort by price 300 ms.
- What the page measured: every event entry of 16 ms or more, and INP from the web-vitals library, version 6.2.3. Chrome also wrote a trace: a detailed record of everything it did, with screenshots. The trace lists every event Chrome timed, quick ones too.
- A second, longer visit: on another day, Asha does the same 15 things. Then she writes a 50-letter note for Meera's gift card.
Clicks, taps and key presses
web.dev names exactly three kinds of interaction for INP:
- clicking with a mouse;
- tapping on a touch screen;
- pressing a key, on a real keyboard or one on the screen.
Hovering, zooming and scrolling aren't on the list. How does Chrome tell them apart? With an ID. Lesson 11.4 showed that the events of one click, tap or key press share one interactionId. Every other event gets 0. Here is what five of Asha's actions left behind:
// Five things Asha did: [what she did, events Chrome timed, interactions].const actions = [ ["Point at Mint tea", 18, 0], ["Swipe the list up", 26, 0], ["Click Add on Green tea", 23, 1], ["Tap Add on Mint tea", 29, 1], ['Type "chai"', 22, 4],];for (const [action, events, interactions] of actions) { console.log(`${action}: ${events} events, interactions: ${interactions}`);}- Lines 3 to 7 hold one action each: what she did, how many events Chrome timed, and how many interactions they made. The counts come from the trace.
- Lines 9 to 11 print one line per action.
- It prints
Point at Mint tea: 18 events, interactions: 0andSwipe the list up: 26 events, interactions: 0. Then come the click and the tap, with 1 each, andType "chai": 22 events, interactions: 4.
Every action fired a crowd of events. Only some made interactions. Typing four letters made four: one per key press. Here is Asha's screen at the end of six actions:
Pointing: slow, and still ignored
When Asha pointed at Mint tea, a line about it appeared. That handler does 120 ms of work, so Chrome timed its events at 136 ms. That's slower than every interaction of her visit but one. INP ignored it anyway, because all 18 events had interactionId 0.
Why it matters: a slow hover handler can still hurt. A tap that came during those 120 ms would have to wait. That wait would show in the tap's own time, not as the hover's.
Scrolling: no events, or a cancelled press
The trackpad scroll left no event of its own. The Event Timing standard, which defines these timings, leaves out wheel events on purpose. They fire again and again while you move, so it calls them continuous. The only events in those moments came from the resting mouse, as the page moved under it. web.dev gives one more reason to leave scrolling out. Browsers can often scroll on a separate thread, so the page's code doesn't slow it down.
The swipe is more interesting. A finger going down fires pointerdown, as a tap does. At that moment, Chrome can't know if it's a tap or a swipe. So the standard holds the pointerdown back. When the finger moves and the page starts to scroll, Chrome fires pointercancel. The held pointerdown then gets interactionId 0. It's like reaching for a doorbell, then walking on. That's exactly what Asha's two swipes did.
Two counters on the page agree. performance.eventCounts counts every event of each kind Event Timing watches. performance.interactionCount counts the interactions:
// From the end of Asha's visit: performance.eventCounts and performance.interactionCount.const counts = { pointerdown: 7, pointercancel: 2, keydown: 16 };const interactionCount = 21; const presses = counts.pointerdown - counts.pointercancel;console.log(`Clicks and taps: ${presses}`);console.log(`Key presses: ${counts.keydown}`);console.log(`Together: ${presses + counts.keydown}. Chrome counted ${interactionCount}.`);- Line 2 holds three of Chrome's counts. Line 3 holds its count of interactions.
- Line 5 takes the cancelled presses away from the presses: the two swipes weren't taps.
- Lines 6 to 8 print
Clicks and taps: 5,Key presses: 16andTogether: 21. Chrome counted 21.
One thing is missing from eventCounts: it has no entry for wheel or scroll at all. Chrome doesn't count them, because Event Timing doesn't time them.
Pick any of Asha's 15 actions to see every event Chrome timed for it:
pointerover×2interactionId 0pointerenter×8interactionId 0mouseover×2interactionId 0pointeroutinteractionId 0pointerleave×4interactionId 0mouseoutinteractionId 0
Every event here has interactionId 0, so INP ignores them all, even the slowest, which took 136 ms.
The next paint, not the whole job
An interaction's time stops at the next frame, not when all its work is done. web.dev says INP doesn't try to measure every effect of a click, like a fetch that finishes later. It measures how long the next paint was held up. That first frame is the page's "one moment", like the librarian looking up.
So a press counts even when nothing much changes. Asha's tap on the search box ran none of the page's code. Chrome still timed it, at 16 ms, as one of her 21. Lesson 23.3 found the other side of this: a tap that came before the page was listening did nothing, and still counted as 0 ms.
Taps inside an iframe, a page inside the page like a video player, count for Chrome's INP too. But the page's own JavaScript can't see them. So your own measurements can come out lower than CrUX's.
From many interactions to one value
Asha's visit had 21 interactions. Each one has a latency: the longest duration among its events, from the input to the next frame (lesson 11.4). Now pick one number for the whole visit.
Why not the average? Her average was about 54 ms. It hides the one tap that felt broken. web.dev wants a value that "all (or nearly all)" interactions were beneath. So INP takes the slowest.
On long visits, INP lets a few off: one for every 50 interactions. web.dev explains why. The more interactions a visit has, the more likely a random hiccup becomes. One bad moment shouldn't decide a long, smooth visit. Skipping one in 50 is close to taking the 98th percentile: a value about 98 in 100 interactions were at or under.
- Fewer than 50 interactions: INP is the slowest.
- 50 to 99: the second slowest.
- 100 to 149: the third slowest, and so on.
To pick the slowest, you don't need to remember every interaction. An arcade game shows how:
An old arcade game keeps a table of the ten best scores, highest first. A new game only gets on if it beats the lowest score there. Every other game is forgotten. But the machine still counts every game played, even the forgotten ones.
- In real life: The ten rows of the table
- In INP: The ten slowest interactions web-vitals keeps
- In real life: A new game that beats the lowest row
- In INP: A new interaction slower than the tenth
- In real life: The counter of games played
- In INP:
performance.interactionCount - In real life: Reading a row from the top
- In INP: Picking INP: row 1, or lower on a long visit
The web-vitals library, a small script from Chrome's team that measures these metrics on real visits, does exactly that. Its file InteractionManager.ts, in version 6.2.3, works like this:
- It keeps at most ten interactions, the slowest ones. The limit is called
MAX_INTERACTIONS_TO_CONSIDER. - It counts every interaction with
performance.interactionCount, quick ones too. - It picks the row at
Math.floor(count / 50), counting from 0. If the list is shorter, it takes the last row. - It only listens for events of 40 ms or more. It also listens for the first-input entry: Chrome's copy of the first interaction's first event, sent however quick it was. That way, a visit with only quick interactions still gets a value.
Here is that rule, run on web-vitals' lists from both of Asha's visits:
// web-vitals' list of slowest interactions for each visit, slowest first, in ms.const firstVisit = { interactions: 21, slowest: [304, 104, 104, 96, 40] };const longVisit = { interactions: 72, slowest: [304, 96, 96, 96] }; const inp = (visit) => { const skip = Math.floor(visit.interactions / 50); return visit.slowest[Math.min(skip, visit.slowest.length - 1)];};console.log(`First visit: INP ${inp(firstVisit)} ms`);console.log(`Long visit: INP ${inp(longVisit)} ms`);- Line 2 is the first visit: 21 interactions, and the five of them that reached 40 ms, slowest first.
- Line 3 is the long visit: 72 interactions. Only four reached 40 ms. The 50 letters of the note were all quicker.
- Line 6 works out how many to skip: one for every full 50.
- Line 7 takes the row after the skipped ones, or the last row.
- Lines 9 and 10 print
First visit: INP 304 msandLong visit: INP 96 ms.
On the long visit, the slow tap was still there, and still 304 ms. The 50 quick letters bought it one skip. Why it matters: a long visit forgives one slow moment. A short visit forgives nothing.
When did web-vitals settle on 96 ms? It sends a new report each time INP changes. Here are its reports from the long visit:
// web-vitals' reports from the long visit: [ms since the page started, INP in ms, interactions so far].const reports = [[5101, 96, 1], [15856, 304, 20], [28356, 96, 72]]; for (const [at, inp, count] of reports) { console.log(`At ${at} ms, after interaction ${count}: INP ${inp} ms`);}- Line 2 holds three reports: when each came, the INP it sent, and how many interactions there had been.
- It prints
At 5101 ms, after interaction 1: INP 96 msandAt 15856 ms, after interaction 20: INP 304 ms. ThenAt 28356 ms, after interaction 72: INP 96 ms.
The first report came after her first click. The second came after the slow tap, her 20th interaction. The third came only when she left, as the tea list was hidden. By then she had made 72, so the slow tap was skipped. Why it matters: INP isn't final until the visit ends.
Chrome doesn't use web-vitals for its own records. It works out INP in its own code, and uses the same rule. That code also keeps the ten longest interactions, and skips one for every 50.
Try the rule yourself. The list holds Asha's ten slowest interactions. Pretend she made more quick ones, and watch which row becomes INP:
- Sort by price
304 ms - Add, Green tea
104 ms - Add, Mint tea
104 ms - Add, Earl Grey
96 ms - The c key
40 ms - The h key
32 ms - The a key
32 ms - The i key
32 ms - Delete it, one key at a time
32 ms - Delete it, one key at a time
32 ms
21 ÷ 50 rounds down to 0, so nothing is skipped. INP is the slowest.
The 200 ms line
Asha's first visit had an INP of 304 ms. Is that good? web-vitals rates INP against the two lines from lesson 3.1:
// web-vitals' ratings for INP: good up to 200 ms, poor over 500 ms.const rate = (inp) => (inp > 500 ? "poor" : inp > 200 ? "needs improvement" : "good"); console.log(`First visit, 304 ms: ${rate(304)}`);console.log(`Long visit, 96 ms: ${rate(96)}`);console.log(`Exactly 200 ms: ${rate(200)}`);- Line 2 checks for poor first: over 500 ms. Then needs improvement: over 200 ms. Anything else is good.
- Lines 4 to 6 print
First visit, 304 ms: needs improvement,Long visit, 96 ms: goodandExactly 200 ms: good.
Why 200 ms?
Lesson 2.1 told the story of a light switch. When the light comes on within about 100 ms, it feels like you turned it on. Research on cause and effect says the same. In a classic study, people watched one shape bump into another. When the second moved within about 100 ms, they saw the first one cause it. Between 100 and 200 ms, they weren't sure. After 200 ms, the link was gone.
So the research points at 100 ms. But a line only helps if sites can reach it. When Chrome's team chose INP's lines, they checked CrUX. At 100 ms, only 12% of sites on phones would have been good. At 200 ms, 56% would. They chose 200 ms: near the research, and within reach.
The poor line got the same check. At 300 ms, more than half of the 10,000 most popular sites would have been poor on phones. At 500 ms, about a quarter would. So poor starts above 500 ms.
Passengers would love every bus to arrive to the second. In city traffic, no bus company can promise that. So the company sets a rule its routes can keep. A bus up to two minutes late counts as on time. More than five minutes late counts as very late.
- In real life: Arriving to the second
- In INP: The 100 ms the research points to
- In real life: Up to two minutes late: on time
- In INP: Up to 200 ms: good
- In real life: More than five minutes late: very late
- In INP: Over 500 ms: poor
- In real life: A rule most routes can keep
- In INP: A line many sites could already reach
From one visit to the whole site
Asha's visit is one of many. Chrome records one INP per visit, and CrUX gathers them. It reports the 75th percentile: the value three in four visits were at or under. Lesson 10.1 called it the survey's headline. web.dev splits it by phones and desktops, too.
So here is Ravi's answer. Asha's visit sent 304 ms, which needs improvement. Whether the whole site is good depends on the other visits. Three in four of them need an INP of 200 ms or less.
Now feel the lines yourself. The button below runs a real handler for as long as you choose. Your own browser times each press, the way Chrome timed Asha's. Compare 150 ms of work with 250 ms. Does the second feel like a pause?
// What the panel's button runs, and how it times your click.button.addEventListener("click", () => busy(work)); new PerformanceObserver((list) => { for (const entry of list.getEntries()) { if (entry.target === button && entry.interactionId) show(entry.duration, rate(entry.duration)); }}).observe({ type: "event", durationThreshold: 16 });- No presses timed yet.
Pick how long the button's handler works, then press Add to cart. Your browser times the interaction, from your press to its next frame.
Why FID wasn't enough
Before INP, Chrome's responsiveness metric was First Input Delay, or FID. web.dev defines it as the time from a person's first interaction until the browser can start running its event handlers. Good was 100 ms or less.
In code, FID is one subtraction on the first-input entry from lesson 11.4. Here it is for Asha's first visit:
// Asha's first input: the pointerdown of her click on Add, Green tea. Times in ms.const firstInput = { startTime: 4363.1, processingStart: 4365.9 };const inp = 304; const fid = firstInput.processingStart - firstInput.startTime;console.log(`FID: ${fid.toFixed(1)} ms`);console.log(`INP: ${inp} ms`);- Line 2 holds two times from the first-input entry: the pointerdown of her click on Add, Green tea.
- Line 3 is her INP.
- Line 5 subtracts: the wait before her first click's handlers could start.
- Lines 6 and 7 print
FID: 2.8 msandINP: 304 ms.
By FID, Asha's visit was fast. By INP, it needs improvement. The picture shows what each one looked at:
Chrome's team pointed out that both gaps are in FID's name: "first input" and "delay".
- First: FID only looked at her first click. It never saw the tap on Sort by price, her 20th interaction.
- Delay: even for that first click, it stopped as the handler started. The click took 104 ms to show the new cart count. FID saw the first 2.8 ms of it.
A hotel asks every guest one question: at check-in, how long until the receptionist looked up? Most guests say "straight away", so the hotel scores well. Nobody asks how long the check-in itself took. Nobody asks about the room service that took an hour on the third night.
- In real life: How long until the receptionist looked up
- In INP: Input delay: until the handlers start
- In real life: Asking about check-in only
- In INP: Timing the first input only
- In real life: The check-in itself
- In INP: Processing and drawing: the rest of the wait
- In real life: Room service on the third night
- In INP: Later interactions, like the tap on Sort by price
Why was FID built that way? web.dev says the first impression shapes how people judge a site. And the worst delays often came while a page was still loading. But web.dev also says 90% of a person's time on a page comes after it loads. FID looked at just one moment of a visit: the first input.
The numbers show how much it missed. When Chrome announced the change in 2023, 93% of sites had good FID on phones. Only 65% had good INP.
INP replaced FID as a Core Web Vital, one of the three metrics web.dev asks every site to measure, on March 12, 2024. Chrome's tools stopped supporting FID on September 9, 2024, and CrUX dropped it that month. web-vitals 5 removed its onFID() function. Lesson 2.3 has the whole road in a table.
Read this table across: each row asks one question of both metrics.
| Question | First Input Delay (FID) | Interaction to Next Paint (INP) |
|---|---|---|
| Which inputs? | Only the first click, tap or key press. | Every click, tap and key press of the visit. |
| Which part of the wait? | Only the input delay, until the handlers start. | All of it, until the next frame is painted. |
| Which number for the visit? | The first input's delay. | The slowest interaction, skipping one for every 50. |
| Good is | 100 ms or less. | 200 ms or less. |
| Asha's first visit | 2.8 ms: good. | 304 ms: needs improvement. |
| Status | Retired. Chrome's tools stopped supporting it on September 9, 2024. | A Core Web Vital since March 12, 2024. |
Does it count toward INP?
Each action below happens on the tea list. Sort it: does it make an interaction that INP counts?
- Clicking Add with a mouse
- Tapping Sort by price on a touch screen
- Pressing Backspace in the search box
- Tapping the search box, which runs none of the page's code
- Pointing at a tea's name to read about it
- Scrolling the list with a trackpad
- Swiping the list with a finger
- A script calling
button.click()
Sort each action: an interaction INP counts, or not an interaction.
Common mix-ups
- "INP is the average interaction." No. It's the slowest, skipping one for every 50. Asha's average was about 54 ms, and her INP was 304 ms.
- "Anything slow on the page raises INP." No. Only clicks, taps and key presses count. Her hover took 136 ms and didn't count.
- "A slow, jerky scroll makes INP worse." No. Scrolling isn't an interaction. A swipe's press is cancelled, and wheel events aren't timed at all.
- "INP waits until the work is done." No. It stops at the next frame. A spinner shown at once ends the wait, even if a fetch takes longer.
- "web-vitals only counts the interactions it hears about." No. It only hears about the slow ones, but it counts every interaction with
performance.interactionCount. The long visit had 72. - "A good FID meant a responsive page." No. Asha's FID was 2.8 ms. Her INP was 304 ms.
- "A visit with no clicks has an INP of 0." No. It has no INP at all. web.dev says pages where people only scroll or hover report no value.
Practice exercises
Answer first, then use the hints.
The same list came from a visit with 120 interactions. What does it print?
// web-vitals' list for a busier visit, slowest first, in ms.
const busyVisit = { interactions: 120, slowest: [304, 104, 104, 96, 40] };
const inp = (visit) => {
const skip = Math.floor(visit.interactions / 50);
return visit.slowest[Math.min(skip, visit.slowest.length - 1)];
};
console.log(`Busy visit: INP ${inp(busyVisit)} ms`);Busy visit: INP 104 ms. With 120 interactions, two are skipped: 304 and the first 104. The next row is the other 104.
What does this print for an INP of exactly 500 ms?
// web-vitals' ratings for INP: good up to 200 ms, poor over 500 ms.
const rate = (inp) => (inp > 500 ? "poor" : inp > 200 ? "needs improvement" : "good");
console.log(`Exactly 500 ms: ${rate(500)}`);Exactly 500 ms: needs improvement. Poor starts above 500 ms, so 500 itself still needs improvement.
In the one-value playground, set 500 interactions. Which latency becomes INP?
32 ms. 500 divided by 50 is 10, but the list only holds ten rows. So the rule takes the last one: one of her key presses.
How many events did Chrome time when Asha tapped Sort by price?
27 events. Pointer, touch and mouse events all fired for one tap. Only pointerdown, pointerup and click carried the interactionId.
Open the course's trace of Asha's first visit in Chrome's DevTools. What does DevTools call the interaction that has the red triangle?
Pointer. DevTools names each interaction by kind: Pointer for clicks and taps, Keyboard for key presses. The one with the red triangle is the tap on Sort by price.
In Chrome's own INP code, what is the constant that holds the 50 called?
kHighPercentileUpdateFrequency, which is 50. Chrome's own code skips one interaction for every 50, like web-vitals. Its list holds kMaxInteractions, 10.
Check your understanding
For each question, ask which actions count, how many interactions there were, and which line the value crosses.
Question 1 of 7What does the web-vitals pick example print on its first line?
Read the code, then predict// web-vitals' list of slowest interactions for each visit, slowest first, in ms. const firstVisit = { interactions: 21, slowest: [304, 104, 104, 96, 40] }; const longVisit = { interactions: 72, slowest: [304, 96, 96, 96] }; const inp = (visit) => { const skip = Math.floor(visit.interactions / 50); return visit.slowest[Math.min(skip, visit.slowest.length - 1)]; }; console.log(`First visit: INP ${inp(firstVisit)} ms`); console.log(`Long visit: INP ${inp(longVisit)} ms`);Choose an answer to see the explanation.
Question 2 of 7Asha points at Mint tea, and the page's hover handler keeps the main thread busy for 120 ms. What does INP do with it?
Choose an answer to see the explanation.
Question 3 of 7A visit has 130 interactions. Which one is its INP?
Choose an answer to see the explanation.
Question 4 of 7Research says about 100 ms feels instant. Why is INP's good line 200 ms?
Choose an answer to see the explanation.
Question 5 of 7What does the rating example print on its last line?
Read the code, then predict// web-vitals' ratings for INP: good up to 200 ms, poor over 500 ms. const rate = (inp) => (inp > 500 ? "poor" : inp > 200 ? "needs improvement" : "good"); console.log(`First visit, 304 ms: ${rate(304)}`); console.log(`Long visit, 96 ms: ${rate(96)}`); console.log(`Exactly 200 ms: ${rate(200)}`);Choose an answer to see the explanation.
Question 6 of 7What does this FID example print, for the long visit's first input?
Read the code, then predict// Asha's first input: the pointerdown of her click on Add, Green tea. Times in ms. const firstInput = { startTime: 4304.3, processingStart: 4304.4 }; const inp = 96; const fid = firstInput.processingStart - firstInput.startTime; console.log(`FID: ${fid.toFixed(1)} ms`); console.log(`INP: ${inp} ms`);Choose an answer to see the explanation.
Question 7 of 7CrUX says a site's INP is 180 ms. What does that tell you?
Choose an answer to see the explanation.
Key takeaways
- Only clicks, taps and key presses are interactions. Pointing, scrolling, swiping and zooming aren't.
- An interaction lasts from the input to the next frame, not until all its work is done.
- A visit's INP is its slowest interaction, skipping one for every 50. web-vitals and Chrome keep the ten slowest, and count them all.
- Good is 200 ms or less, poor is over 500 ms, at the 75th percentile of a site's visits.
- FID timed only the first input's wait. Asha's FID was 2.8 ms while her INP was 304 ms.
Remember the one-liner.
Count the clicks, taps and key presses, time each to its next frame, and keep the slowest.
Sources
The facts on this page come from these specs, docs and source files:
- Interaction to Next Paint (INP), web.dev.
- Event Timing, W3C.
- First Input Delay (FID), web.dev.
- InteractionManager.ts, web-vitals 6.2.3, GoogleChrome on GitHub.
- onINP.ts, web-vitals 6.2.3, GoogleChrome on GitHub.
- interaction_to_next_paint_calculator.cc, Chromium.
- CrUX metrics, Chrome for Developers.
- Defining the Core Web Vitals metrics thresholds, web.dev.
- Advancing Interaction to Next Paint, web.dev.
- Interaction to Next Paint becomes a Core Web Vital on March 12, web.dev.
- CrUX release notes, Chrome for Developers.
- Performance features reference, Chrome for Developers.
Coming next: The three phases of an interaction. Asha's slow tap took 304 ms. The next lesson splits a slow interaction into its three parts, to find which one to fix.