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.

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>;

Link to headingAnatomy of INP

Link to headingINP phase: Other code

Link to headingINP phase: Event handler

Link to headingINP phase: Browser render

Link to headingOptimizing INP

Link to headingShorten the event handler phase

Link to headingShorten the duration of the browser render

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>
);
}

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>
);
}

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);
});
});
}

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>
);
}

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.

Link to headingSummary