>soren hale — frontend developer
// Writing
An INP budget you can keep
How I set an interaction budget with a team, and what we do on the day it breaks.
- to read
- 2 min
- published
- 2026-07-30
- filed under
- Performance

Interaction to Next Paint is the first web vital that measures what a product feels like after it has loaded. It is also the easiest one to lose slowly: every feature adds a few milliseconds and no single pull request looks guilty.
Pick the number together
I ask the team to use the product on a four year old phone for ten minutes. The number we agree on after that is always lower than the one I would have proposed. For most products it lands at 200 ms at the 75th percentile, and 100 ms for the two or three interactions that make money.
Report the interaction, not the score
A number on a dashboard tells you that something is slow. It does not tell you what. So the report carries the element.
import { onINP } from "web-vitals/attribution"
onINP(({ value, attribution }) => {
if (value <= 200) return
report({
ms: Math.round(value),
target: attribution.interactionTarget,
phase: attribution.inputDelay > attribution.processingDuration ? "input delay" : "handler",
})
})
After a week the list sorts itself. On the Kestrel dashboard nine tenths of the slow interactions were one filter on one table.
Make it somebody's Tuesday
A budget with no owner is a wish. Ours has a name on it each sprint and a chart on the wall. The owner does not fix everything; they notice first and ask the right team.
When it breaks
We do not revert. We find the interaction, fix that one, and write down what caused it in a file called slow.md that now has thirty one entries.
- Twelve were work done on every keystroke that could wait for a pause.
- Nine were lists that rendered rows nobody could see.
- Six were third party scripts.
The file is the most useful onboarding document we have. New developers read it and stop making the first twelve mistakes.
// Writing
An INP budget you can keep
How I set an interaction budget with a team, and what we do on the day it breaks.
- to read
- 2 min
- published
- 2026-07-30
- filed under
- Performance

Interaction to Next Paint is the first web vital that measures what a product feels like after it has loaded. It is also the easiest one to lose slowly: every feature adds a few milliseconds and no single pull request looks guilty.
Pick the number together
I ask the team to use the product on a four year old phone for ten minutes. The number we agree on after that is always lower than the one I would have proposed. For most products it lands at 200 ms at the 75th percentile, and 100 ms for the two or three interactions that make money.
Report the interaction, not the score
A number on a dashboard tells you that something is slow. It does not tell you what. So the report carries the element.
import { onINP } from "web-vitals/attribution"
onINP(({ value, attribution }) => {
if (value <= 200) return
report({
ms: Math.round(value),
target: attribution.interactionTarget,
phase: attribution.inputDelay > attribution.processingDuration ? "input delay" : "handler",
})
})
After a week the list sorts itself. On the Kestrel dashboard nine tenths of the slow interactions were one filter on one table.
Make it somebody's Tuesday
A budget with no owner is a wish. Ours has a name on it each sprint and a chart on the wall. The owner does not fix everything; they notice first and ask the right team.
When it breaks
We do not revert. We find the interaction, fix that one, and write down what caused it in a file called slow.md that now has thirty one entries.
- Twelve were work done on every keystroke that could wait for a pause.
- Nine were lists that rendered rows nobody could see.
- Six were third party scripts.
The file is the most useful onboarding document we have. New developers read it and stop making the first twelve mistakes.
// Writing
An INP budget you can keep
How I set an interaction budget with a team, and what we do on the day it breaks.
- to read
- 2 min
- published
- 2026-07-30
- filed under
- Performance

Interaction to Next Paint is the first web vital that measures what a product feels like after it has loaded. It is also the easiest one to lose slowly: every feature adds a few milliseconds and no single pull request looks guilty.
Pick the number together
I ask the team to use the product on a four year old phone for ten minutes. The number we agree on after that is always lower than the one I would have proposed. For most products it lands at 200 ms at the 75th percentile, and 100 ms for the two or three interactions that make money.
Report the interaction, not the score
A number on a dashboard tells you that something is slow. It does not tell you what. So the report carries the element.
import { onINP } from "web-vitals/attribution"
onINP(({ value, attribution }) => {
if (value <= 200) return
report({
ms: Math.round(value),
target: attribution.interactionTarget,
phase: attribution.inputDelay > attribution.processingDuration ? "input delay" : "handler",
})
})
After a week the list sorts itself. On the Kestrel dashboard nine tenths of the slow interactions were one filter on one table.
Make it somebody's Tuesday
A budget with no owner is a wish. Ours has a name on it each sprint and a chart on the wall. The owner does not fix everything; they notice first and ask the right team.
When it breaks
We do not revert. We find the interaction, fix that one, and write down what caused it in a file called slow.md that now has thirty one entries.
- Twelve were work done on every keystroke that could wait for a pause.
- Nine were lists that rendered rows nobody could see.
- Six were third party scripts.
The file is the most useful onboarding document we have. New developers read it and stop making the first twelve mistakes.
// Start a project
Tell me what you are building.
Three quick picks so the first reply is useful. You finish the message on the contact page.
// Start a project
Tell me what you are building.
Three quick picks so the first reply is useful. You finish the message on the contact page.
// Start a project
Tell me what you are building.
Three quick picks so the first reply is useful. You finish the message on the contact page.