How to fix performance issues in your Svelte application using svelte-render-scan
If you’re working on web applications today, you might have heard that re-renders are bad for performance. But why is that exactly? And more importantly – how can we spot these performance issues in our Svelte and SvelteKit applications?
In this post, I’ll introduce you to svelte-render-scan
, a visual debugging tool I created that helps you see exactly when and where your Svelte components are updating the DOM. We’ll explore why re-renders matter, how Svelte’s approach differs from other frameworks, and how you can use this tool to optimize your applications.
Looking for a video version of this blog post? You can watch it right here:
Why re-renders matter for performance
A re-render happens when the framework you’re using needs to update the underlying Document Object Model (DOM) that makes up all websites. Whether you use Svelte, React, Solid or Qwik, this step is always required to change what the user sees on screen.

The problem comes when we unnecessarily re-render elements that don’t actually need updating. The reason this hurts performance is because updating HTML elements triggers the browser’s rendering pipeline, which happens in several steps:
- Style recalculation – The browser needs to figure out which CSS rules apply to the updated elements
- Layout – If elements move or change size, the browser recalculates positions
- Paint – The browser redraws the affected areas
- Composite – Layers are combined to produce the final image
Each of these steps can be computationally expensive, especially on lower-end devices or when you’re updating many elements at once.
Svelte’s advantage in handling re-renders
There’s been a lot of discussion about re-render performance lately. I especially want to give a shout out to Aiden Bai, who created a tool called react-scan that can show re-renders in React applications. He’s used this to demonstrate how large companies like GitHub and Twitter suffer from poor performance due to excessive re-renders.

But here’s where it gets interesting for us Svelte developers – Svelte’s architecture gives us a huge advantage over frameworks like React.
In Svelte 4, we had this very intentional reactivity system where you create direct connections between your state and DOM updates using variables and reactive statements. This is fundamentally different from the approach of React where components re-run all their code by default when state changes.
Thanks to the new signals primitives in Svelte 5, called Runes, things have gotten even more granular. Svelte now keeps track of exactly which DOM nodes consume which state, making updates incredibly precise and performant.
However, Svelte isn’t completely immune to performance issues. Even in Svelte, you can write code that isn’t optimal or accidentally trigger updates when they’re not needed. So how do we spot these issues when they happen?
Introducing svelte-render-scan
A couple weeks ago, I saw a tool called render-scan by GitHub user NullVoxPopuli. His tool makes it possible to see re-renders in any framework because it uses the standard MutationObserver API under the hood. I decided to port this into a Svelte-specific package with some nice additions for our ecosystem.

svelte-render-scan
adds visual indicators around DOM elements as they update, helping you understand exactly what’s changing in your application. You’ll see boxes with labels like “children added”, “attribute change”, or “text updated” that appear briefly around the affected elements.
Getting started with svelte-render-scan
Let’s see how to add this to your SvelteKit project. First, install the package:
npm install -D svelte-render-scan
For a SvelteKit application, add it to your root +layout.svelte
file so it’s available on all pages during development:
<script>
import { dev } from '$app/environment';
import { RenderScan } from 'svelte-render-scan';
</script>
{#if dev}
<RenderScan />
{/if}
Notice how we’re only including it in development mode – there’s no need to ship this debugging tool to production!
For vanilla Svelte applications, you can simply import and use the component:
<script>
import { RenderScan } from 'svelte-render-scan';
</script>
<RenderScan />
Once added, you’ll see a small eye icon in the bottom right corner of your page. Click it to toggle the render scanning on and off.
A real-world performance issue: keyed each blocks
One common performance issue in Svelte (and other frameworks) involves loops. Let’s look at an example with a contact list:
<script>
let contacts = $state<Contact[]>([
{ id: 1, name: "Alice Johnson", email: "alice@example.com" },
{ id: 2, name: "Bob Smith", email: "bob@example.com" },
{ id: 3, name: "Carol Wilson", email: "carol@example.com" }
]);
...
</script>
<h2>Unkeyed Loop</h2>
<div class="contacts">
{#each contacts as contact}
<div class="contact">
<strong>{contact.name}</strong>
<span>{contact.email}</span>
</div>
{/each}
</div>
When I add a new contact with svelte-render-scan enabled, something unexpected happens – it shows that all the existing contacts are being updated, not just the new one being added!

This happens because Svelte doesn’t know which DOM element corresponds to which contact. The solution is to use a keyed each block:
<h2>Keyed Loop</h2>
<div class="contacts">
{#each contacts as contact (contact.id)}
<div class="contact">
<strong>{contact.name}</strong>
<span>{contact.email}</span>
</div>
{/each}
</div>
Now when a new contact is added, only the new element shows the “children added” indicator:

Final thoughts
While Svelte’s compiler and reactivity system protect us from many common performance pitfalls, tools like svelte-render-scan help us catch the edge cases and write even more efficient code. It’s been fascinating to port this tool to Svelte and see how our framework’s fine-grained reactivity shines compared to other approaches.
Remember, not all re-renders are bad – sometimes you need to update the DOM! The goal is to identify and eliminate unnecessary updates that don’t provide anything of value to your users.
Photo by Fotis Fotopoulos on Unsplash
View Comments
Claude 3.7 Sonnet vs Gemini 2.5 Pro: Which AI Is Better for Coding?
If you’re a programmer, you’re likely already using AI and large language models in...