惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

TaoSecurity Blog
TaoSecurity Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
F
Fortinet All Blogs
Cisco Talos Blog
Cisco Talos Blog
D
Darknet – Hacking Tools, Hacker News & Cyber Security
S
Secure Thoughts
美团技术团队
雷峰网
雷峰网
Hugging Face - Blog
Hugging Face - Blog
博客园_首页
C
CXSECURITY Database RSS Feed - CXSecurity.com
Engineering at Meta
Engineering at Meta
人人都是产品经理
人人都是产品经理
月光博客
月光博客
T
Tor Project blog
P
Privacy & Cybersecurity Law Blog
Recorded Future
Recorded Future
I
Intezer
博客园 - 【当耐特】
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
GbyAI
GbyAI
罗磊的独立博客
V
V2EX
Google DeepMind News
Google DeepMind News
D
DataBreaches.Net
Last Week in AI
Last Week in AI
T
Tailwind CSS Blog
www.infosecurity-magazine.com
www.infosecurity-magazine.com
A
About on SuperTechFans
Scott Helme
Scott Helme
Vercel News
Vercel News
Spread Privacy
Spread Privacy
T
Threat Research - Cisco Blogs
Recent Announcements
Recent Announcements
Hacker News: Ask HN
Hacker News: Ask HN
C
CERT Recently Published Vulnerability Notes
G
Google Developers Blog
B
Blog
博客园 - 叶小钗
WordPress大学
WordPress大学
博客园 - 聂微东
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Jina AI
Jina AI
IT之家
IT之家
C
Cybersecurity and Infrastructure Security Agency CISA
P
Palo Alto Networks Blog
小众软件
小众软件
博客园 - Franky
Microsoft Azure Blog
Microsoft Azure Blog
AWS News Blog
AWS News Blog

Vercel News

Vercel Open Source Program: Winter 2026 cohort How Notion Workers run untrusted code at scale with Vercel Sandbox How we run Vercel's CDN in front of Discourse From idea to secure checkout in minutes with Stripe Building Slack agents can be easy Scaling redirects to infinity on Vercel Advancing Python typing Gamma builds design-first agents with Vercel How Avalara turns pipe dreams into patent-pending with v0 Keeping community human while scaling with agents How OpenEvidence built a healthcare AI that physicians actually trust Security boundaries in agentic architectures Skills Night: 69,000+ ways agents are getting smarter Video Generation with AI Gateway We Ralph Wiggumed WebStreams to make them 10x faster How Stably ships AI testing agents in hours, not weeks How we built AEO tracking for coding agents Anyone can build agents, but it takes a platform to run them Introducing Geist Pixel The Vercel AI Accelerator is back with $6m in credits Making agent-friendly pages with content negotiation The Vercel OSS Bug Bounty program is now available Introducing the new v0 Run untrusted code with Vercel Sandbox, now generally available How Stripe built a game-changing app in a single flight with v0 How Sensay went from zero to product in six weeks AGENTS.md outperforms skills in our agent evals Agent skills explained: An FAQ Testing if "bash is all you need" AWS databases are now live on the Vercel Marketplace and v0 Use Perplexity Web Search with Vercel AI Gateway Introducing: React Best Practices Nick Bogaty joins Vercel as Chief Revenue Officer How Mux shipped durable video workflows with their @mux/ai SDK How to build agents with filesystems and bash How we made v0 an effective coding agent Stopping the slow death of internal tools Building AI-Generated Pixel Trading Cards with Vercel AI Gateway We removed 80% of our agent’s tools AI SDK 6 Our $1 million hacker challenge for React2Shell Cline now runs on Vercel AI Gateway How to prompt v0 Build smarter workflows with Notion and v0 Vercel launches partner certification Inside Workflow DevKit: How framework integrations work React2Shell Security Bulletin | Vercel Knowledge Base Billions of requests: Black Friday-Cyber Monday 2025 Investing in the Python ecosystem AWS Databases coming to the Vercel Marketplace How we built the v0 iOS app Workflow Builder: Build your own workflow automation platform Vercel Open Source Program: Fall 2025 cohort Self-driving infrastructure Vercel collaborates with Google for Gemini 3 Pro Preview launch Vercel: The anti-vendor-lock-in cloud How Nous Research used BotID to block automated abuse at scale How AI Gateway runs on Fluid compute What we learned building agents at Vercel Build and deploy data applications on Snowflake with v0 BotID Deep Analysis catches a sophisticated bot network in real-time Vercel achieves TISAX AL2 compliance to serve automotive partners Bun runtime on Vercel Functions David Totten Joins Vercel to Lead Global Field Engineering Vercel Ship AI 2025 recap You can just ship agents AI agents and services on the Vercel Marketplace Built-in durability: Introducing Workflow Development Kit Zero-config backends on Vercel AI Cloud Introducing Vercel Agent: Your new Vercel teammate Update regarding Vercel service disruption on October 20, 2025 Agents at work, a partnership with Salesforce and Slack Running Next.js in ChatGPT: How to Build ChatGPT Apps Talha Tariq joins Vercel as CTO of Security Just another (Black) Friday Server rendering benchmarks: Fluid Compute and Cloudflare Workers Towards the AI Cloud: Our Series F Collaborating with Anthropic on Claude Sonnet 4.5 to power intelligent coding agents Preventing the stampede: Request collapsing in the Vercel CDN BotID uncovers hidden SEO poisoning How we made global routing faster with Bloom filters What you need to know about vibe coding Scale to one: How Fluid solves cold starts Addressing security & quality issues with MCP tools - Vercel AI agents at scale: Rox’s Vercel-powered revenue operating system Agentic Infrastructure Zero Data Retention on AI Gateway Optimizing Vercel Sandbox snapshots How Waldium made a blog platform work for humans and AI alike How FLORA shipped a creative agent on Vercel's AI stack Agent responsibly Making Turborepo 96% faster with agents, sandboxes, and humans Unified reporting for all AI Gateway usage new.website joins forces with v0 SERHANT.'s playbook for rapid AI iteration Two startups at global scale without DevOps Chat SDK brings agents to your users 360 billion tokens, 3 million customers, 6 engineers Meet the 2026 Vercel AI Accelerator Cohort Build knowledge agents without embeddings
Demystifying INP: New tools and actionable insights - Vercel – Vercel
Malte Ubl · 2024-06-12 · via Vercel News

9 min read

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

In March 2024 Interaction to Next Paint (INP) became part of Google’s Core Web Vitals, a set of metrics reporting on user experience of web pages based on field data, and used in Google’s search ranking.

Modern frameworks like Next.js are optimized for good baseline INP scores, but the nature of the metric is that what you do in your app really matters for the outcomes. Many reasonable UX patterns actually push browsers to the limit of what they can render within the INP guidelines.

This is an article about how to optimize INP in those hard situations. It’s based on real-world lessons we learned when optimizing the INP of nextjs.org.

Link to headingINP, a confusing metric

It’s fair to say that INP is by far the most misunderstood of all the Core Web Vitals. Let’s start with a hard truth. This web page represented in JSX has perfect INP:

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

Obviously, an empty click handler is going to be fast, but the misunderstanding about INP is more subtle: You don’t have to actually change the DOM for it to count as INP. "Paint" really just means it would have had the chance to paint. It doesn’t matter for the metric if the page changes.

Let’s look at a second example. Here we block the main thread for a full second:

<button

onClick={async () => {

await sleep(100);

blockTheMainThreadForOneSecond();

}}

>

Click me

</button>;

If you click this button once, you also get perfect INP. The sleep(100) gives the browser time to paint, and then it doesn’t matter that you block the main thread for a second.

Now, you might say “Well, why would you add the sleep(100)?”. Of course, you would not, but this is very reasonable code:

<button

onClick={async () => {

const data = await fetchData();

blockTheMainThreadForOneSecond(data);

}}

>

Click me

</button>;

…and once again, this code has perfect INP even though it blocks the main thread for 1 second.

The final example, however, would have an INP of 1 second, which is considered very bad:

<html>

<button

onClick={() => {

blockTheMainThreadForOneSecond();

}}

>

Click me

</button>

</html>;

The main takeaway is that, to optimize INP, our job is to arrange the work we’re giving to the browser such that it gets a chance to paint and react to user action. What it does not mean is that we always have to get all the work done, that we eventually want to do within the 200ms INP deadline.

Link to headingAnatomy of INP

The time from interaction to next paint can be structured like this:

  • It starts with the user interaction

  • Other code part 1: Your event handler running might be blocked by other stuff that happens to run on the main thread.

  • Event handler: Eventually your event handler gets to run.

  • Other code part 2: After that there is another chance for unrelated code to block the main thread

  • Browser render: Finally, if you or other code changed the DOM, the browser will re-render the DOM

  • And then eventually paint.

In order to optimize INP we need to minimize the duration of all the phases above.

Link to headingINP phase: Other code

In the web programming model, all code running in a web page defaults to sharing a single thread and event loop. That means that unrelated code may interfere with handling a user event.

This can be the hardest to optimize, given that it isn’t directly related to the code that you care about at that moment.

One very common cause for such delay is third-party JavaScript. The one thing you can do today, if you discover that third-party JS is causing bad INP on your page, is to call your vendor and ask them to fix their stuff. In fact, I recently did this with our analytics vendor Heap, and to their credit, they shipped a fix.

Link to headingINP phase: Event handler

This is your code which is reacting to the user’s interaction. It may fetch some data, immediately change the DOM, or do anything else. If it does change the DOM, then the specific way in which way it does that will impact the time the browser needs to render the new state.

It is this combination of DOM change and render-time that will most immediately impact the INP score of the user interaction. We have a dedicated section for how to optimize it below.

Link to headingINP phase: Browser render

In this phase, the browser takes all the changes from the previous phase and turns them into actual pixels that can be painted. As we learned in the intro of the post, nothing at all might have changed, in which case this phase takes 0ms and the subsequent paint will also be very fast since there is nothing to paint.

It’s actually very common that this phase is empty. If, for example, you need to fetch data before rendering a response, then this phase would be empty. This can lead to an ironic outcome where you get worse INP if things are faster–e.g. because you preloaded data so that you can respond immediately.

Link to headingOptimizing INP

We’ve previously written about various techniques to optimize INP. The key to good INP is to optimize the phases above:

  • Minimize other JS from running

  • Shorten the event handler phase

  • Shorten the duration of the browser render

In this article, we’ll focus on the latter 2 phases.

Link to headingShorten the event handler phase

Event handlers are often very fast. The primary cost attributable to this phase in modern web applications is rerendering including virtual DOM diffing upon state changes. For React, classic performance optimizations like the use of memoization and immutable context and prop values are the best mechanisms to minimize the duration of this phase.

Link to headingShorten the duration of the browser render

This phase, by definition, lies mostly in the scope of the browser. Also, even though browser vendors are often asking web developers to “chunk their work more,” browsers themselves unfortunately are still performing this rendering phase in a monolithic fashion.

The general gist of optimizing this phase is that most browser layout algorithms are O(n) with the size of the DOM that gets invalidated. Respectively our job is to minimize the amount of DOM that gets invalidated.

One difficult case from nextjs.org was the programming language picker which is used in the documentation section:

From an INP perspective, this is essentially the worst case. Changing the programming language changes the contents of the code, which changes the height of the box, which requires a re-layout of the page. That page may be very long (common for documentation) and so the work the browser has to do is substantial.

It’s fair to say that optimizing this phase falls strictly into the “very advanced web development” category.

  • You may be able to reduce the size of the DOM. Inlined SVGs are often a good candidate for removal from the DOM by replacing them with SVG-images.

  • If you are lucky, it may be an option to use techniques for optimizing animations such as only changing opacity of an element, which does not change the layout tree, and may be doable entirely on the GPU.

  • CSS containment is the primary tool in the toolbox to minimize the work the browser has to do in this phase. It allows the browser to restrict a layout to the area that has containment.

  • For long lists, virtualizing those lists can help, by making the actual DOM size that ever needs to be rendered independent of the actual length of the list.

Link to headingA shippable alternative

But what if we cannot make things fast enough? The “language picker” case above is a good example of the type of experience that will just be slower than 200ms in rendering in current-generation browsers with a sufficiently old mobile device. Sure, one could virtualize the document, but is that really better UX? Shouldn’t browsers be good at viewing content?

So, what can we do in this situation?

It’s time to remember what INP is all about: The metric wants you to acknowledge user input within 200ms. As explained above, it does not expect you to actually draw the response within 200ms. And, again, if you’d need to fetch the data you wouldn’t be able to draw immediately anyway.

What we can do is split up the interaction response into two phases:

  1. Acknowledge user interaction

  2. Actually change the page

Let’s look at this code example for the LanguagePicker:

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>

);

}

When the user selects a new value, it adds a class to the select element and sets the new language. In this example, the class is set to render an acknowledgment of the user action. Remember that setting the new language may be very expensive. So, how can we separate the acknowledgment from the expensive operation, so that the user immediately knows that their action is being processed without having to wait for the expensive work to complete?

Link to headingIntroducing await-interaction-response

We shipped the new module await-interaction-response. You can get started with it right away:

pnpm add await-interaction-response

Here, we use the module to separate the two phases of our code:

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>

);

}

This one line of code ensures that the acknowledgment of the user action can be performed immediately and with minimal INP, while performing the expensive operation right after.

In fact, because we used a native select we don’t need to do anything to acknowledge the user action and can further simplify:

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

The implementation of await-interaction-response is very simple:

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

});

});

}

The code waits for an animation frame, but then also for a timeout. This allows the browser to paint the frame, and then immediately run the code. There is a backup timeout for the special case where the animation frame never runs, which is only relevant for extreme cases like the user moving to a different tab at just the right instant.

Now, it first appears ironic that we delay the work, but we need to be aware that:

  • It enables giving the user immediate feedback that their action was accepted.

  • It only delays the response by a maximum of 1 frame, an average of 8ms, which is way too little for a human to notice for the type of major actions where you’d use this function.

Link to headingEven simpler with modern React

React introduced a very similar API called startTransition which tells React that the state updates in its callback do not need to be executed synchronously. If the slow operation in your app is a result of the state change (like in the examples above), then all you need to do is add startTransition.

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

It can be challenging to reproduce INP issues on more powerful developer machines. We recently launched a few features that can help.

First, Vercel Speed Insights identifies the specific HTML elements impacting your Interaction to Next Paint (INP) metric. These CSS selectors tell you exactly which elements on the page had slow interactions. When optimizing INP, go through this list by frequency prioritizing the most frequent interactions that are slow or need improvement.

The second is support for INP monitoring directly inside the Vercel Toolbar. This gives you the CSS selector of the impacted elements while you're actively previewing your page—in local dev, a staging environment, or even production—so you can see which component 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.

Pairing these two tools with CPU throttling in the Chrome DevTools performance panel makes it quite easy to get results comparable to users on slower devices. One bit of caution: The CPU throttling sometimes yields extreme delays on the very first interaction. It’s safe to ignore this and just click again.

Link to headingSummary

INP is often misunderstood to require painting the full user response within 200ms, when it is really about giving the user feedback that their input is being processed within that time.

The most effective ways to improve INP are:

  • Calling your third-party code vendors like analytics providers and asking them to improve their event handling code. Please do this today!

  • Optimize JS framework rendering performance with tools like React’s memo.

  • Help the browser have an easier time rendering the page with tools like CSS containment.

  • And finally, splitting up event handling into two phases with await-interaction-response to ensure immediate acknowledgment of user input for truly expensive page changes.