Back to Blog
PerformanceJune 30, 20265 min read

INP Optimization: How to Stop Third-Party Scripts from Blocking

INP optimization is critical for responsiveness. Learn how to stop third-party script performance issues from blocking your main thread and slowing users.

INPWeb PerformanceJavaScriptOptimizationWeb VitalsPerformance

During a recent audit for a client, I noticed their Interaction to Next Paint (INP) score was consistently hovering above 400ms—well into the "poor" category. The culprits weren't the application's core logic, but a collection of marketing trackers, chat widgets, and analytics tags firing all at once. When a user tried to click a button, the browser was busy parsing these third-party scripts, leading to a frustrating, sluggish experience.

If your site feels heavy despite having a lean codebase, you’re likely dealing with main thread blocking caused by external dependencies. Here is how I approach this.

Identifying the Culprits of Main Thread Blocking

Before you start deleting scripts, you need data. Open Chrome DevTools, navigate to the Performance tab, and record a user interaction. Look for the "Long Tasks" (marked with a red triangle). If you click on one, the bottom panel will show you exactly what caused it.

Usually, you'll see a waterfall of script execution. If you notice a third-party domain taking up a massive chunk of the main thread, that’s your primary target for INP optimization. You can also use the "Coverage" tab in DevTools to see how much of the code loaded by these scripts is actually executed during the page lifecycle. You'll often find that 60-70% of the loaded bytes are never used.

Strategies for Third-Party Script Performance

Once you've identified the heavy hitters, you need to change how they interact with your page. I’ve found that the best approach is to stop them from competing with your core application logic.

1. Offloading with Web Workers

If a script doesn't need direct access to the DOM, it shouldn't be on the main thread. I’ve had great success using Third-Party Script Optimization: Offloading Scripts with Partytown to move heavy trackers into a Web Worker. By isolating these in a separate thread, the main thread remains free to handle user interactions immediately.

2. Deferred and Lazy Execution

Don't load everything at window.onload. If a chat widget or a survey popup isn't needed until the user scrolls, use an IntersectionObserver to trigger the script injection only when it enters the viewport. This keeps the initial load lightweight and prevents the browser from becoming overwhelmed during the critical first few seconds of page interaction.

3. Using the Scheduler API

Sometimes you can't avoid running a script, but you can control when it runs. I often use scheduler.yield() to break up long-running tasks into smaller chunks. As I discussed in Mastering scheduler.yield for Better Main-Thread Performance, this allows the browser to pause execution, check for pending user interactions, and then resume. It’s a lifesaver for INP.

Comparison: Handling Third-Party Scripts

When deciding how to handle a specific script, I use this mental framework to weigh my options:

StrategyComplexityBest ForMain Thread Impact
Async/DeferLowNon-critical scriptsMinimal
Lazy LoadingMediumWidgets, PopupsVery Low
Web WorkersHighTrackers, AnalyticsNone (Isolated)
scheduler.yieldMediumHeavy computationLow (Interruptible)

The "Wrong Turn" We Took

Early on, we tried simply removing all third-party scripts to see if it fixed our performance. While our INP dropped from 400ms to about 80ms, the business lost all visibility into user behavior, and the customer service team was flooded with tickets because the help-desk widget was gone.

We had to walk back that decision and instead adopt a "budgeted" approach. We moved the analytics to a background worker and set a threshold where no single script could occupy the main thread for more than 50ms at a time. This balance kept our metrics healthy without sacrificing business requirements.

Next Steps for Your Site

If you're still seeing high interaction latency, check if your hydration process is part of the problem. If you're using a framework like React or Vue, your application's own hydration might be fighting those third-party scripts for CPU cycles. Reviewing Interaction to Next Paint: Architecting Deferred Hydration can help you decide if you're hydrating too much, too soon.

Remember: third-party script performance isn't about having zero scripts. It’s about ensuring that your users' input takes priority over a tracking pixel firing halfway across the world. Don't be afraid to aggressive prune or delay scripts that don't add immediate value to the user experience.

FAQ

Q: How do I know if a script is blocking the main thread? A: Use the Performance panel in Chrome DevTools. Look for "Long Tasks" that exceed 50ms. If the script source points to a third-party domain (e.g., cdn.tracker.com), it’s a blocker.

Q: Is moving scripts to a Web Worker always the right move? A: Not always. If the script requires constant DOM access, it won't work in a Web Worker without significant refactoring (like using Partytown). Use it primarily for analytics, logging, and background processing.

Q: What is the ideal INP target? A: Google recommends an INP of 200ms or less for a "good" user experience. Anything above 500ms is considered poor and requires immediate attention.

I'm still experimenting with how to handle scripts that dynamically inject other scripts. Those "nested" dependencies are often the hardest to track down, and I'm currently looking into using Request Interception in a service worker to see if I can block them at the network level entirely. Let me know if you find a cleaner way to handle those.

Similar Posts