search
HomeWeb Front-endJS TutorialLearning the new Svelte veactivity System

So Svelte v5, the newest rendition of what probably is the best front-end framework in existence, has been released and it is vastly different from its previous version. The main difference lies at its core: How reactivity of variables is implemented. Because of these changes, Svelte became easier, and at the same time a bit more difficult.

Since I have been working intensely with Svelte v5 since v5@next.155 in real-world micro-frontend projects, I decided to write this series of articles to pass on my gained knowledge to help you understand, embrace and potentially migrate your code to Svelte v5.

A Brief Introduction: Svelte v4 Reactivity

Svelte v4’s reactivity system is nothing short of a work of art: Svelte statically analyzes code in components and then generates code that imperatively changes the DOM whenever regular JavaScript variables change. Simple, elegant, and very performant. A quick example:

<script lang="ts">
    let clickCount = 0;

    function countClicks() {
        ++clickCount;
    }
</script>

<document on:click="{()"> countClicks()} />
<pre class="brush:php;toolbar:false">Clicks inside document: {clickCount}

This simple component adds a “click” event listener to the document object and counts clicks. The click count is displayed in real time with just the above code. Amazing, right?

Like everything in life, though, this is not perfect. Rich Harris (the creator of Svelte) has explained the caveats, and I won’t go through those in this article. I’ll just mention one: Code refactor.

Code Refactoring is not Possible

One of the more important caveats is the inability to carry this reactivity system outside the component. One cannot, for instance, create a reusable module that encapsulates the implementation of the countClicks function in the example.

Because reactivity depends on static analysis, taking the function away and into a module will hide the variable mutation to the static code analyzer and then reactivity is lost.

Reactivity in Svelte v5: Runes

The term rune refers to a “magic symbol”, and is the term that Svelte adopted to name the following function-looking “magic” terms:

  • $state

  • $props

  • $bindable

  • $derived

  • $effect

Reactivity in Svelte v5 is governed by the use of these runes.

It is important to note that while they look like R-values, their produced code is really L-values. In other words, don’t think that you can pass around state from variable to variable. This is detailed a bit more below in the $state rune section.

The main advantages of this new reactivity system are:

  • Ability to refactor reactive code outside of components

  • Fine-grained reactivity

The former means that we can have reactive variables outside components; the latter means that component re-renders are more targeted when it comes to reacting on changing state.

The ability to have state outside components is not covered in this article but follow the series because an article about this will come.

Fine-grained reactivity, on the other hand, means that Svelte can now know which property changed in a state object, and only re-renders (and re-runs effects and recalculates derived values) what is affected by that specific property only. This is in some cases a major performance improvement. As a quick example: If a large table component sees a new row added to its data, Svelte will only render the new row. If a single cell value in the 3rd row changes, only the cell that shows the value will be re-rendered. Is it clear now? Hopefully it is, but if not, hit me in the Comments section.

The $state Rune

This rune is used to create reactive state. Let’s re-write the Svelte v4 code sample from above:

<script lang="ts">
    let clickCount = 0;

    function countClicks() {
        ++clickCount;
    }
</script>

<document on:click="{()"> countClicks()} />
<pre class="brush:php;toolbar:false">Clicks inside document: {clickCount}

To achieve the same result that we did in Svelte v4, we simply used $state(0) instead of 0.

The main rule that governs this rune is that it can only be used to initialize variables or class fields, and it has to do with the important note you read a minute ago: Runes look like functions syntactically, but they are not. The compiler replaces runes with code that is not compatible with the idea of what a function does, which is calculate and return a value. This means that the following doesn’t create a second reactive variable:

<script lang="ts">
    let clickCount = $state(0);

    function countClicks() {
        ++clickCount;
    }
</script>

<document onclick="{()"> countClicks()} />
<pre class="brush:php;toolbar:false">Clicks inside document: {clickCount}

The reactive nature of clickCount is not transferred or copied over to secondClickCount by virtue of using the assignment operator. If runes were functions, the above would have worked, but they are not.

There is one more important thing to mention about $state: It makes its value deeply reactive. This means that if the value is an object whose properties contain objects, then the properties of the contained objects are also reactive. This pattern applies recursively, so the entire object graph is ultimately reactive.

The $props Rune

Component properties are expected to be reactive, and Svelte v5 achieves this using the $props rune.

<script lang="ts">
    let clickCount = $state(0);
    let secondClickCount = clickCount;

    function countClicks() {
        ++clickCount;
    }
</script>

Because using TypeScript is everything to a project, we start by declaring the component properties using a type. Optional properties are marked by appending ? to its name.

Then comes the use of the rune, which is a de-structuring statement. The example shows how to assign default values and how to allow “rest” properties, which is any other property. The component spreads (applies) this “rest” of properties as properties (attributes) on the span HTML element.

You could do let props: Props = $props(); to define the properties and it works, but then you cannot specify defaults for the various properties, so I suggest you always declare properties as shown. I also wouldn’t know how to declare restProperties either if not de-structuring, by the way.

If you have been paying attention, the above produces a TypeScript error. After all, the Props type has no mention about any “rest” property. How can we type restProps?

Generally speaking, you could do stuff like the following to allow all kinds of stuff. It is up to your TypeScript skills, I suppose.

The following opens the Props type up to allow any data-* attribute:

<script lang="ts">
    let clickCount = 0;

    function countClicks() {
        ++clickCount;
    }
</script>

<document on:click="{()"> countClicks()} />
<pre class="brush:php;toolbar:false">Clicks inside document: {clickCount}

This one allows anything:

<script lang="ts">
    let clickCount = $state(0);

    function countClicks() {
        ++clickCount;
    }
</script>

<document onclick="{()"> countClicks()} />
<pre class="brush:php;toolbar:false">Clicks inside document: {clickCount}

But more often than not, one would need to allow the attributes of the HTML element that receives restProps, and in our example, that was the span HTML element.

For this common scenario, Svelte v5 provides types that should cover most of the HTML elements:

<script lang="ts">
    let clickCount = $state(0);
    let secondClickCount = clickCount;

    function countClicks() {
        ++clickCount;
    }
</script>

Using the latter will make GUI’s like VS Code provide accurate Intellisense on the possible props (attributes) for the span HTML element. Nice, right?

The HTMLAttributes interface is used for HTML elements that have no particularities in their list of properties. Many elements, however, do have. For example, rather than doing HTMLAttributes, import the HTMLButtonAttributes interface from 'svelte/elements'.

The last detail is default values. There’s really not much to say and the example says it all: The default value of the operation prop is 'sum'. If the property is not specified when the component is used, that’s the value the prop will assume.

If the wanted default is undefined, then don’t specify anything at all.

The $bindable Rune

This one is a very specific rune that can only be used in component properties. It marks a property as bindable.

If you don’t know or don’t recall, Svelte allows for 2-way binding of properties. Vue also has this feature, and in contrast, React does not.

Usage is super simple:

<script lang="ts">
    type Props = {
        data: number[];
        operation?: 'sum', 'avg';
    };

    let {
        data,
        operation = 'sum',
        ...restProps,
    }: Props = $props();

    function sum() {
        return data.reduce((p, c) => p + c);
    }

    function avg() {
        return sum() / data.length
    }
</script>

<span class="amount">{operation === 'sum' ? sum() : avg()}</span>

<style>
    .amount {
        font-family: monospace;
    }
</style>

Always make properties that get their values modified as bindable, or Svelte will complain with a console warning. The warning states that components should not modify state that doesn’t belong to them, and that if this is intended, then a binding should be used.

As shown in the example, one can specify a property default through the $bindable rune. The example sets the property’s default to 5.

But does a default even make sense here? Well, yes. Declaring a property as bindable doesn’t make it required.

The $derived Rune

Whenever we need to calculate a value using values from props or other reactive state (that can change over time), we use the $derived rune.

Bringing the example component that calculates sums and averages back, we can re-write it using this rune:

<script lang="ts">
    let clickCount = 0;

    function countClicks() {
        ++clickCount;
    }
</script>

<document on:click="{()"> countClicks()} />
<pre class="brush:php;toolbar:false">Clicks inside document: {clickCount}

Now we have a new variable named result that is as reactive as its input and will automatically recalculate every time data in the data array changes. It itself is a reactive variable, so the template (the HTML part of the component) that uses it will also update.

The $effect Rune

This rune allows us to specify arbitrary code that runs whenever reactive data changes. For this rune to work its magic, it keeps track of the reactive data that is read during its execution. This inventory of reactive data is then used to re-trigger the effect whenever anything in the inventory changes its value.

Probably the most common scenario is to re-trigger a data fetch operation based on changing values:

<script lang="ts">
    let clickCount = $state(0);

    function countClicks() {
        ++clickCount;
    }
</script>

<document onclick="{()"> countClicks()} />
<pre class="brush:php;toolbar:false">Clicks inside document: {clickCount}

Asynchronous operations are usually the norm inside effects whenever we don’t want our $derived variables to hold promises. Personally, though and because it is so easy to work with promises in Svelte, I would simply use a $derived value. The variant shown next makes data a reactive calculated value that holds a promise:

<script lang="ts">
    let clickCount = $state(0);
    let secondClickCount = clickCount;

    function countClicks() {
        ++clickCount;
    }
</script>

Generally speaking, if you are doing a combination of $state and $effect, you’re most likely better off with a $derived. There are exceptions to this rule, though, so take it as a rule of thumb and not the Holy Word.

If data fetching is not a good example for $effect, then what is? Let’s see this one:

<script lang="ts">
    type Props = {
        data: number[];
        operation?: 'sum', 'avg';
    };

    let {
        data,
        operation = 'sum',
        ...restProps,
    }: Props = $props();

    function sum() {
        return data.reduce((p, c) => p + c);
    }

    function avg() {
        return sum() / data.length
    }
</script>

<span class="amount">{operation === 'sum' ? sum() : avg()}</span>

<style>
    .amount {
        font-family: monospace;
    }
</style>

This is a simple timer component that is controlled via its status property. The $effect rune is used here to enforce the timer’s operation. Can you imagine refactoring this to $derived? By the way, don’t try because elapsed is a prop, so it cannot be $derived and prop at the same time.

Conclusion

Svelte v5 comes with a brand-new reactivity engine that is geared towards better performance in re-renders and better code refactoring. Using the new reactivity system is both simple and complex: Simple because the common scenarios are well covered by the system’s design, and a bit more difficult because, when compared to v4, the code has become a bit more complex.

Regardless, the new system is powerful and caters for most scenarios gracefully and effectively, providing runes for all possibilities that are straightforward to use, albeit a bit odd at first.

What’s Next

This article has only covered the introductory part of runes, plus some bits of personal experience using them. There are more topics to cover to help you, fellow reader, ramp up more quickly with this new rendition of Svelte, namely:

  • In-depth knowledge of how $effect works

  • Advanced runes ($state.raw, $derived.by, $effect.pre, etc.)

  • Replacing stores with reactive state

  • Out-of-the-norm scenarios

Bonus: Performance Charts

Have a look at these benchmark results: Interactive Results (krausest.github.io)

Now, the list of frameworks is appalling, so you can copy the following JSON, and then paste it into the web page using the Paste button (see screenshot):

<script lang="ts">
    let clickCount = 0;

    function countClicks() {
        ++clickCount;
    }
</script>

<document on:click="{()"> countClicks()} />
<pre class="brush:php;toolbar:false">Clicks inside document: {clickCount}

Learning the new Svelte veactivity System

By the way, I think that simply focusing the window and pasting via keyboard works too.

This narrows down the list of frameworks to the more popular ones, or at least what I consider popular. Maybe you know better than me.

It is a shame that Svelte v4 is no longer available in the charts, but as you can see, out of the selected frameworks, the top 3 is undisputable: Vanilla JS, Solid and Svelte.

On the other end of the spectrum, it is just sad to see React v19 performing so poorly. Wasn’t the compiler supposed to make it like a lot better? It seems that it ended up being wasted effort. Sure, it seems to outperform React v18, but that’s about it. It is uncertain to me why Meta continues to invest money in React. Thoughts, anyone?

The above is the detailed content of Learning the new Svelte veactivity System. For more information, please follow other related articles on the PHP Chinese website!

Statement
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Python and JavaScript: Understanding the Strengths of EachPython and JavaScript: Understanding the Strengths of EachMay 06, 2025 am 12:15 AM

Python and JavaScript each have their own advantages, and the choice depends on project needs and personal preferences. 1. Python is easy to learn, with concise syntax, suitable for data science and back-end development, but has a slow execution speed. 2. JavaScript is everywhere in front-end development and has strong asynchronous programming capabilities. Node.js makes it suitable for full-stack development, but the syntax may be complex and error-prone.

JavaScript's Core: Is It Built on C or C  ?JavaScript's Core: Is It Built on C or C ?May 05, 2025 am 12:07 AM

JavaScriptisnotbuiltonCorC ;it'saninterpretedlanguagethatrunsonenginesoftenwritteninC .1)JavaScriptwasdesignedasalightweight,interpretedlanguageforwebbrowsers.2)EnginesevolvedfromsimpleinterpreterstoJITcompilers,typicallyinC ,improvingperformance.

JavaScript Applications: From Front-End to Back-EndJavaScript Applications: From Front-End to Back-EndMay 04, 2025 am 12:12 AM

JavaScript can be used for front-end and back-end development. The front-end enhances the user experience through DOM operations, and the back-end handles server tasks through Node.js. 1. Front-end example: Change the content of the web page text. 2. Backend example: Create a Node.js server.

Python vs. JavaScript: Which Language Should You Learn?Python vs. JavaScript: Which Language Should You Learn?May 03, 2025 am 12:10 AM

Choosing Python or JavaScript should be based on career development, learning curve and ecosystem: 1) Career development: Python is suitable for data science and back-end development, while JavaScript is suitable for front-end and full-stack development. 2) Learning curve: Python syntax is concise and suitable for beginners; JavaScript syntax is flexible. 3) Ecosystem: Python has rich scientific computing libraries, and JavaScript has a powerful front-end framework.

JavaScript Frameworks: Powering Modern Web DevelopmentJavaScript Frameworks: Powering Modern Web DevelopmentMay 02, 2025 am 12:04 AM

The power of the JavaScript framework lies in simplifying development, improving user experience and application performance. When choosing a framework, consider: 1. Project size and complexity, 2. Team experience, 3. Ecosystem and community support.

The Relationship Between JavaScript, C  , and BrowsersThe Relationship Between JavaScript, C , and BrowsersMay 01, 2025 am 12:06 AM

Introduction I know you may find it strange, what exactly does JavaScript, C and browser have to do? They seem to be unrelated, but in fact, they play a very important role in modern web development. Today we will discuss the close connection between these three. Through this article, you will learn how JavaScript runs in the browser, the role of C in the browser engine, and how they work together to drive rendering and interaction of web pages. We all know the relationship between JavaScript and browser. JavaScript is the core language of front-end development. It runs directly in the browser, making web pages vivid and interesting. Have you ever wondered why JavaScr

Node.js Streams with TypeScriptNode.js Streams with TypeScriptApr 30, 2025 am 08:22 AM

Node.js excels at efficient I/O, largely thanks to streams. Streams process data incrementally, avoiding memory overload—ideal for large files, network tasks, and real-time applications. Combining streams with TypeScript's type safety creates a powe

Python vs. JavaScript: Performance and Efficiency ConsiderationsPython vs. JavaScript: Performance and Efficiency ConsiderationsApr 30, 2025 am 12:08 AM

The differences in performance and efficiency between Python and JavaScript are mainly reflected in: 1) As an interpreted language, Python runs slowly but has high development efficiency and is suitable for rapid prototype development; 2) JavaScript is limited to single thread in the browser, but multi-threading and asynchronous I/O can be used to improve performance in Node.js, and both have advantages in actual projects.

See all articles

Hot AI Tools

Undresser.AI Undress

Undresser.AI Undress

AI-powered app for creating realistic nude photos

AI Clothes Remover

AI Clothes Remover

Online AI tool for removing clothes from photos.

Undress AI Tool

Undress AI Tool

Undress images for free

Clothoff.io

Clothoff.io

AI clothes remover

Video Face Swap

Video Face Swap

Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Tools

Notepad++7.3.1

Notepad++7.3.1

Easy-to-use and free code editor

SublimeText3 Mac version

SublimeText3 Mac version

God-level code editing software (SublimeText3)

mPDF

mPDF

mPDF is a PHP library that can generate PDF files from UTF-8 encoded HTML. The original author, Ian Back, wrote mPDF to output PDF files "on the fly" from his website and handle different languages. It is slower than original scripts like HTML2FPDF and produces larger files when using Unicode fonts, but supports CSS styles etc. and has a lot of enhancements. Supports almost all languages, including RTL (Arabic and Hebrew) and CJK (Chinese, Japanese and Korean). Supports nested block-level elements (such as P, DIV),

MinGW - Minimalist GNU for Windows

MinGW - Minimalist GNU for Windows

This project is in the process of being migrated to osdn.net/projects/mingw, you can continue to follow us there. MinGW: A native Windows port of the GNU Compiler Collection (GCC), freely distributable import libraries and header files for building native Windows applications; includes extensions to the MSVC runtime to support C99 functionality. All MinGW software can run on 64-bit Windows platforms.

MantisBT

MantisBT

Mantis is an easy-to-deploy web-based defect tracking tool designed to aid in product defect tracking. It requires PHP, MySQL and a web server. Check out our demo and hosting services.