Skip to content
Dashboard

Demystifying INP: New tools and actionable insights

CTO, Vercel

A technical guide exploring real-world strategies used to improve INP on Vercel's websites.

Copy link to headingINP, a confusing metric

<button onClick={() => {}}>Click me</button>

<button
onClick={async () => {
await sleep(100);
blockTheMainThreadForOneSecond();
}}
>
Click me
</button>;

<button
onClick={async () => {
const data = await fetchData();
blockTheMainThreadForOneSecond(data);
}}
>
Click me
</button>;

<html>
<button
onClick={() => {
blockTheMainThreadForOneSecond();
}}
>
Click me
</button>
</html>;

Copy link to headingAnatomy of INP

Copy link to headingINP phase: Other code

Copy link to headingINP phase: Event handler

Copy link to headingINP phase: Browser render

Copy link to headingOptimizing INP

Copy link to headingShorten the event handler phase

Copy link to headingShorten the duration of the browser render

Copy link to headingA shippable alternative

import { useState } from 'react';
export function LanguagePicker({ setLanguage }) {
const [selected, setSelected] = useState();
return (
<select
className={selected ? `value-${selected}` : ''}
onChange={(e) => {
setSelected(e.target.value);
setLanguage(e.target.value);
}}
>
<option value="JS">JavaScript</option>
<option value="TS">TypeScript</option>
</select>
);
}

Copy link to headingIntroducing await-interaction-response

pnpm add await-interaction-response

import { useState } from 'react';
import interactionResponse from 'await-interaction-response';
export function LanguagePicker({ setLanguage }) {
const [selected, setSelected] = useState();
return (
<select
className={selected ? `value-${selected}` : ''}
onChange={async (e) => {
setSelected(e.target.value);
await interactionResponse();
setLanguage(e.target.value);
}}
>
<option value="JS">JavaScript</option>
<option value="TS">TypeScript</option>
</select>
);
}

import interactionResponse from 'await-interaction-response';
export function LanguagePicker({ setLanguage }) {
return (
<select onChange={async (e) => {
await interactionResponse();
setLanguage(e.target.value);
}}>
<option value="JS">JavaScript</option>
<option value="TS">TypeScript</option>
</select>
);
}

Copy link to headingThe implementation

export function interactionResponse(): Promise<unknown> {
return new Promise((resolve) => {
setTimeout(resolve, 100); // Fallback for the case where the animation frame never fires.
requestAnimationFrame(() => {
setTimeout(resolve, 0);
});
});
}

Copy link to headingEven simpler with modern React

import { startTransition } from "react";
export function LanguagePicker({ setLanguage }) {
return (
<select onChange={(e) => {
startTransition(() => {
setLanguage(e.target.value);
})
}}>
<option value="JS">JavaScript</option>
<option value="TS">TypeScript</option>
</select>
);
}

Copy link to headingHow to find out what to optimize

The interaction timing tool allows you to see and optimize your website's responsiveness.The interaction timing tool allows you to see and optimize your website's responsiveness.
The interaction timing tool allows you to see and optimize your website's responsiveness.

Copy link to headingSummary

Ready to deploy?