{"id":3438,"date":"2024-08-14T10:05:23","date_gmt":"2024-08-14T15:05:23","guid":{"rendered":"https:\/\/frontendmasters.com\/blog\/?p=3438"},"modified":"2024-08-14T20:15:38","modified_gmt":"2024-08-15T01:15:38","slug":"fine-grained-reactivity-in-svelte-5","status":"publish","type":"post","link":"https:\/\/frontendmasters.com\/blog\/fine-grained-reactivity-in-svelte-5\/","title":{"rendered":"Fine-Grained Reactivity in Svelte 5"},"content":{"rendered":"\n<p>We&#8217;ve been looking at the up and coming Svelte 5. We looked at basic features like state, props, and side effects. Then we looked at Snippets, which is a lightweight feature Svelte added for re-using bits of HTML within (for now) a single component.<\/p>\n\n\n<div class=\"box article-series\">\n  <header>\n    <h3 class=\"article-series-header\">Article Series<\/h3>\n  <\/header>\n  <div class=\"box-content\">\n            <ol>\n                      <li>\n              <a href=\"https:\/\/frontendmasters.com\/blog\/introducing-svelte-5\/\">Introducing Svelte 5<\/a>\n            <\/li>\n                      <li>\n              <a href=\"https:\/\/frontendmasters.com\/blog\/snippets-in-svelte-5\/\">Snippets in Svelte 5<\/a>\n            <\/li>\n                      <li>\n              <a href=\"https:\/\/frontendmasters.com\/blog\/fine-grained-reactivity-in-svelte-5\/\">Fine-Grained Reactivity in Svelte 5<\/a>\n            <\/li>\n                  <\/ol>\n        <\/div>\n<\/div>\n\n\n\n<p>In this post, we&#8217;ll take a close look at Svelte&#8217;s new fine-grained reactivity.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"what-is-fine-grained-reactivity\">What is fine-grained reactivity?<\/h2>\n\n\n\n<p>The best way to describe fine-grained reactivity is to show what it isn&#8217;t, and the best example of non-fine grained reactivity is React. In React, in <em>any<\/em> component, setting a single piece of state will cause the entire component, and all of the descendent components to re-render (unless they&#8217;re created with <code>React.memo<\/code>). Even if the state you&#8217;re setting is rendered in a single, simple <code>&lt;span><\/code> tag in the component, and not used anywhere else at all, the entire world from that component on down will be re-rendered.<\/p>\n\n\n\n<p>This may seem absurdly wasteful, but in reality this is a consequence of the design features that made React popular when it was new: the data, values, callbacks, etc., that we pass through our component trees are all plain JavaScript. We pass plain, vanilla JavaScript objects, arrays and functions around our components and everything just works. At the time, this made an incredibly compelling case for React compared to alternatives like Angular 1 and Knockout. But since then, alternatives like Svelte have closed the gap. My&nbsp;<a href=\"https:\/\/frontendmasters.com\/blog\/introducing-svelte-5\/\">first post<\/a>&nbsp;on Svelte 5 showed just how simple, flexible, and most importantly <em>reliable<\/em> Svlete&#8217;s new state management primitives are. This post will show you the performance wins these primitives buy us.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"premature-optimization-is-still-bad\">Premature optimization is still bad<\/h2>\n\n\n\n<p>This post will walk through some Svelte templates using trickery to snoop on just how much of a component is being re-rendered when we change state. This is&nbsp;<strong>not<\/strong>&nbsp;something you will usually do or care about. As always, write clear, understandable code, then optimize when needed (not before). Svelte 4 is considerably less efficient than Svelte 5, but still much more performant than what React does out of the box. And React is more than fast enough for the overwhelming majority of use cases \u2014&nbsp;so it&#8217;s all relative.<\/p>\n\n\n\n<p>Being <em>fast enough<\/em> doesn&#8217;t mean we can&#8217;t still look at how much of a better performance baseline Svelte now starts you off at. With a fast-growing ecosystem, and now an incredibly compelling performance story, hopefully this post will encourage you to at least look at Svelte for your next project.<\/p>\n\n\n\n<p>If you&#8217;d like to try out the code we&#8217;ll be looking at in this post, it&#8217;s all in&nbsp;<a href=\"https:\/\/github.com\/arackaf\/svelte-fine-grained-reactivity\">this repo<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"getting-started\">Getting started<\/h2>\n\n\n\n<p>The code we&#8217;ll be looking at is from a <a href=\"https:\/\/kit.svelte.dev\/docs\/creating-a-project\">SvelteKit scaffolded project<\/a>. If you&#8217;ve never used Svelte<em>Kit<\/em>&nbsp;before that&#8217;s totally fine. We&#8217;re not really using any SvelteKit features until the very end of this post, and even then it&#8217;s just re-hashing what we&#8217;ll have already covered.<\/p>\n\n\n\n<p>Throughout this post, we&#8217;re going to be inspecting if and when individual bindings in a component are re-evaluated when we change state. There&#8217;s various ways to do this, but the simplest, and frankly <em>dumbest<\/em>, is to force some global, non-reactive, always-changing state into these bindings. What do I mean by that? In the root page that hosts our site, I&#8217;m adding this:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">script<\/span>&gt;<\/span><span class=\"javascript\">\n  <span class=\"hljs-keyword\">var<\/span> __i = <span class=\"hljs-number\">0<\/span>;\n  <span class=\"hljs-keyword\">var<\/span> getCounter = <span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> __i++;\n<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">script<\/span>&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>This adds a global&nbsp;<code>getCounter<\/code>&nbsp;function, as well as the&nbsp;<code>__i<\/code>&nbsp;variable.&nbsp;<code>getCounter<\/code>&nbsp;will always return the next value, and if we stick a call to it in our bindings, we&#8217;ll be able to snoop on when those bindings are being re-executed by Svelte. If you&#8217;re using TypeScript, you can avoid errors when calling this like so:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"TypeScript\" data-shcb-language-slug=\"typescript\"><span><code class=\"hljs language-typescript\"><span class=\"hljs-keyword\">declare<\/span> global {\n  <span class=\"hljs-keyword\">interface<\/span> Window {\n    getCounter(): <span class=\"hljs-built_in\">number<\/span>;\n  }\n}\n\n<span class=\"hljs-keyword\">export<\/span> {};<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">TypeScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">typescript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>This post will look at different pages binding to the same data, declared mostly like this (we&#8217;ll note differences as we go).<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-keyword\">let<\/span> tasks = &#91;\n  { <span class=\"hljs-attr\">id<\/span>: <span class=\"hljs-number\">1<\/span>, <span class=\"hljs-attr\">title<\/span>: <span class=\"hljs-string\">\"Task A\"<\/span>, <span class=\"hljs-attr\">assigned<\/span>: <span class=\"hljs-string\">\"Adam\"<\/span>, <span class=\"hljs-attr\">importance<\/span>: <span class=\"hljs-string\">\"Low\"<\/span> },\n  { <span class=\"hljs-attr\">id<\/span>: <span class=\"hljs-number\">2<\/span>, <span class=\"hljs-attr\">title<\/span>: <span class=\"hljs-string\">\"Task B\"<\/span>, <span class=\"hljs-attr\">assigned<\/span>: <span class=\"hljs-string\">\"Adam\"<\/span>, <span class=\"hljs-attr\">importance<\/span>: <span class=\"hljs-string\">\"Medium\"<\/span> },\n  { <span class=\"hljs-attr\">id<\/span>: <span class=\"hljs-number\">3<\/span>, <span class=\"hljs-attr\">title<\/span>: <span class=\"hljs-string\">\"Task C\"<\/span>, <span class=\"hljs-attr\">assigned<\/span>: <span class=\"hljs-string\">\"Adam\"<\/span>, <span class=\"hljs-attr\">importance<\/span>: <span class=\"hljs-string\">\"High\"<\/span> },\n  { <span class=\"hljs-attr\">id<\/span>: <span class=\"hljs-number\">4<\/span>, <span class=\"hljs-attr\">title<\/span>: <span class=\"hljs-string\">\"Task D\"<\/span>, <span class=\"hljs-attr\">assigned<\/span>: <span class=\"hljs-string\">\"Mike\"<\/span>, <span class=\"hljs-attr\">importance<\/span>: <span class=\"hljs-string\">\"Medium\"<\/span> },\n  { <span class=\"hljs-attr\">id<\/span>: <span class=\"hljs-number\">5<\/span>, <span class=\"hljs-attr\">title<\/span>: <span class=\"hljs-string\">\"Task E\"<\/span>, <span class=\"hljs-attr\">assigned<\/span>: <span class=\"hljs-string\">\"Adam\"<\/span>, <span class=\"hljs-attr\">importance<\/span>: <span class=\"hljs-string\">\"High\"<\/span> },\n  { <span class=\"hljs-attr\">id<\/span>: <span class=\"hljs-number\">6<\/span>, <span class=\"hljs-attr\">title<\/span>: <span class=\"hljs-string\">\"Task F\"<\/span>, <span class=\"hljs-attr\">assigned<\/span>: <span class=\"hljs-string\">\"Adam\"<\/span>, <span class=\"hljs-attr\">importance<\/span>: <span class=\"hljs-string\">\"High\"<\/span> },\n  { <span class=\"hljs-attr\">id<\/span>: <span class=\"hljs-number\">7<\/span>, <span class=\"hljs-attr\">title<\/span>: <span class=\"hljs-string\">\"Task G\"<\/span>, <span class=\"hljs-attr\">assigned<\/span>: <span class=\"hljs-string\">\"Steve\"<\/span>, <span class=\"hljs-attr\">importance<\/span>: <span class=\"hljs-string\">\"Low\"<\/span> },\n  { <span class=\"hljs-attr\">id<\/span>: <span class=\"hljs-number\">8<\/span>, <span class=\"hljs-attr\">title<\/span>: <span class=\"hljs-string\">\"Task H\"<\/span>, <span class=\"hljs-attr\">assigned<\/span>: <span class=\"hljs-string\">\"Adam\"<\/span>, <span class=\"hljs-attr\">importance<\/span>: <span class=\"hljs-string\">\"High\"<\/span> },\n  { <span class=\"hljs-attr\">id<\/span>: <span class=\"hljs-number\">9<\/span>, <span class=\"hljs-attr\">title<\/span>: <span class=\"hljs-string\">\"Task I\"<\/span>, <span class=\"hljs-attr\">assigned<\/span>: <span class=\"hljs-string\">\"Adam\"<\/span>, <span class=\"hljs-attr\">importance<\/span>: <span class=\"hljs-string\">\"Low\"<\/span> },\n  { <span class=\"hljs-attr\">id<\/span>: <span class=\"hljs-number\">10<\/span>, <span class=\"hljs-attr\">title<\/span>: <span class=\"hljs-string\">\"Task J\"<\/span>, <span class=\"hljs-attr\">assigned<\/span>: <span class=\"hljs-string\">\"Mark\"<\/span>, <span class=\"hljs-attr\">importance<\/span>: <span class=\"hljs-string\">\"High\"<\/span> },\n  { <span class=\"hljs-attr\">id<\/span>: <span class=\"hljs-number\">11<\/span>, <span class=\"hljs-attr\">title<\/span>: <span class=\"hljs-string\">\"Task K\"<\/span>, <span class=\"hljs-attr\">assigned<\/span>: <span class=\"hljs-string\">\"Adam\"<\/span>, <span class=\"hljs-attr\">importance<\/span>: <span class=\"hljs-string\">\"Medium\"<\/span> },\n  { <span class=\"hljs-attr\">id<\/span>: <span class=\"hljs-number\">12<\/span>, <span class=\"hljs-attr\">title<\/span>: <span class=\"hljs-string\">\"Task L\"<\/span>, <span class=\"hljs-attr\">assigned<\/span>: <span class=\"hljs-string\">\"Adam\"<\/span>, <span class=\"hljs-attr\">importance<\/span>: <span class=\"hljs-string\">\"High\"<\/span> },\n];<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>And we&#8217;ll render these tasks with this markup:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n  {#each tasks as t}\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">span<\/span>&gt;<\/span>{t.id + getCounter()}<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">span<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">button<\/span> <span class=\"hljs-attr\">onclick<\/span>=<span class=\"hljs-string\">{()<\/span> =&gt;<\/span> (t.id += 10)} class=\"border p-2\"&gt;Update id<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">button<\/span>&gt;<\/span>\n     <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n     <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n       <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">span<\/span>&gt;<\/span>{t.title + getCounter()}<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">span<\/span>&gt;<\/span>\n       <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">button<\/span> <span class=\"hljs-attr\">onclick<\/span>=<span class=\"hljs-string\">{()<\/span> =&gt;<\/span> (t.title += 'X')} class=\"border p-2\"&gt;Update title<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">button<\/span>&gt;<\/span>\n     <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n     <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">span<\/span>&gt;<\/span>{t.assigned + getCounter()}<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">span<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">button<\/span> <span class=\"hljs-attr\">onclick<\/span>=<span class=\"hljs-string\">{()<\/span> =&gt;<\/span> (t.assigned += 'X')} class=\"border p-2\"&gt;Update assigned<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">button<\/span>&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">span<\/span>&gt;<\/span>{t.importance + getCounter()}<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">span<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">button<\/span> <span class=\"hljs-attr\">onclick<\/span>=<span class=\"hljs-string\">{()<\/span> =&gt;<\/span> (t.importance += 'X')} class=\"border p-2\"&gt;Update importance<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">button<\/span>&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n  {\/each}\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>The Svelte 4 code we&#8217;ll start with uses the&nbsp;<code>on:click<\/code>&nbsp;syntax for events, but everything else will be the same.<\/p>\n\n\n\n<p>The calls to&nbsp;<code>getCounter<\/code> inside the bindings will let us see when those bindings are re-executed, since the call to&nbsp;<code>getCounter()<\/code>&nbsp;will always return a new value.<\/p>\n\n\n\n<p>Let&#8217;s get started!<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"svelte-4\">Svelte 4<\/h2>\n\n\n\n<p>We&#8217;ll render the content we saw above, using Svelte 4.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"399\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/08\/svelte4.png?resize=1024%2C399&#038;ssl=1\" alt=\"\" class=\"wp-image-3465\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/08\/svelte4.png?resize=1024%2C399&amp;ssl=1 1024w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/08\/svelte4.png?resize=300%2C117&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/08\/svelte4.png?resize=768%2C299&amp;ssl=1 768w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/08\/svelte4.png?resize=1536%2C598&amp;ssl=1 1536w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/08\/svelte4.png?resize=2048%2C797&amp;ssl=1 2048w\" sizes=\"auto, (max-width: 1000px) 100vw, 1000px\" \/><\/figure>\n\n\n\n<p>Plain and simple. But now let&#8217;s click any of those buttons, to modify one property, of one of those tasks\u2014it doesn&#8217;t matter which.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"387\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/08\/svelte4-updated.png?resize=1024%2C387&#038;ssl=1\" alt=\"\" class=\"wp-image-3466\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/08\/svelte4-updated.png?resize=1024%2C387&amp;ssl=1 1024w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/08\/svelte4-updated.png?resize=300%2C114&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/08\/svelte4-updated.png?resize=768%2C291&amp;ssl=1 768w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/08\/svelte4-updated.png?resize=1536%2C581&amp;ssl=1 1536w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/08\/svelte4-updated.png?resize=2048%2C775&amp;ssl=1 2048w\" sizes=\"auto, (max-width: 1000px) 100vw, 1000px\" \/><\/figure>\n\n\n\n<p>Notice that the entire component (every binding in the component) re-rendered. As inefficient as this seems, it&#8217;s&nbsp;<em>still<\/em>&nbsp;much better than what React does. It&#8217;s not remotely uncommon for a single state update to trigger&nbsp;<em>multiple<\/em>&nbsp;re-renders of&nbsp;<em>many<\/em>&nbsp;components.<\/p>\n\n\n\n<p>Let&#8217;s see how Svelte 5 improves things.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"svelte-5\">Svelte 5<\/h2>\n\n\n\n<p>For Svelte 5, the code is pretty much the same, except we declare our state like this:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-keyword\">let<\/span> tasks = $state(&#91;\n  { <span class=\"hljs-attr\">id<\/span>: <span class=\"hljs-number\">1<\/span>, <span class=\"hljs-attr\">title<\/span>: <span class=\"hljs-string\">\"Task A\"<\/span>, <span class=\"hljs-attr\">assigned<\/span>: <span class=\"hljs-string\">\"Adam\"<\/span>, <span class=\"hljs-attr\">importance<\/span>: <span class=\"hljs-string\">\"Low\"<\/span> },\n  &lt;em&gt;<span class=\"hljs-comment\">\/\/ and so on ...&lt;\/em&gt;<\/span>\n  { <span class=\"hljs-attr\">id<\/span>: <span class=\"hljs-number\">12<\/span>, <span class=\"hljs-attr\">title<\/span>: <span class=\"hljs-string\">\"Task L\"<\/span>, <span class=\"hljs-attr\">assigned<\/span>: <span class=\"hljs-string\">\"Adam\"<\/span>, <span class=\"hljs-attr\">importance<\/span>: <span class=\"hljs-string\">\"High\"<\/span> },\n]);<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>We render the page, and see the same as before. If you&#8217;re following along in the repo, be sure to refresh the page after navigating, so the page will start over with the global counter.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"395\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/08\/svelte5.png?resize=1024%2C395&#038;ssl=1\" alt=\"\" class=\"wp-image-3468\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/08\/svelte5.png?resize=1024%2C395&amp;ssl=1 1024w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/08\/svelte5.png?resize=300%2C116&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/08\/svelte5.png?resize=768%2C296&amp;ssl=1 768w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/08\/svelte5.png?resize=1536%2C592&amp;ssl=1 1536w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/08\/svelte5.png?resize=2048%2C790&amp;ssl=1 2048w\" sizes=\"auto, (max-width: 1000px) 100vw, 1000px\" \/><\/figure>\n\n\n\n<p>Now let&#8217;s change one piece of state, as before. We&#8217;ll update the title for Task C, the third one.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"392\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/08\/svelte5-updated.png?resize=1024%2C392&#038;ssl=1\" alt=\"\" class=\"wp-image-3470\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/08\/svelte5-updated.png?resize=1024%2C392&amp;ssl=1 1024w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/08\/svelte5-updated.png?resize=300%2C115&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/08\/svelte5-updated.png?resize=768%2C294&amp;ssl=1 768w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/08\/svelte5-updated.png?resize=1536%2C588&amp;ssl=1 1536w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/08\/svelte5-updated.png?resize=2048%2C784&amp;ssl=1 2048w\" sizes=\"auto, (max-width: 1000px) 100vw, 1000px\" \/><\/figure>\n\n\n\n<p>Just like that, only the <em>single<\/em> piece of state we modified has re-rendered. Svelte was smart enough to leave everything else alone. 99% of the time this won&#8217;t make any difference, but if you&#8217;re rendering a&nbsp;<em>lot<\/em>&nbsp;of data on a page, this can be a substantial performance win.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"why-did-this-happen\">Why did this happen?<\/h3>\n\n\n\n<p>This is the default behavior when we pass arrays and objects (and arrays of objects) into the&nbsp;<code>$state<\/code>&nbsp;rune, like we did with:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-keyword\">let<\/span> tasks = $state(&#91;<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Svelte will read everything you pass, set up&nbsp;<a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/JavaScript\/Reference\/Global_Objects\/Proxy\">Proxy<\/a>&nbsp;objects to track what changes, and update the absolute minimum amount of DOM nodes necessary.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"false-coda\">False Coda<\/h2>\n\n\n\n<p>We could end the post here. Use the&nbsp;<code>$state<\/code>&nbsp;primitive to track your reactive data. Svelte will make it deeply reactive, and update whatever it needs to update when you change anything. This will be&nbsp;<em>just fine<\/em>&nbsp;the vast majority of the time.<\/p>\n\n\n\n<p>But what if you&#8217;re writing a web application that has to manage a&nbsp;<em>ton<\/em>&nbsp;of data? Making everything deeply reactive is not without cost.<\/p>\n\n\n\n<p>Let&#8217;s see how we can tell Svelte that only&nbsp;<em>some of<\/em>&nbsp;our data is reactive. I&#8217;ll stress again, laboring over this will almost never be needed. But it&#8217;s good to know how it works if it ever comes up.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"rediscovering-a-long-lost-javascript-feature\">Rediscovering a long-lost JavaScript feature<\/h2>\n\n\n\n<p>Classes in JavaScript have gotten an unfortunately bad reputation. Classes are an outstanding way to declare the&nbsp;<em>structure<\/em>&nbsp;of a set of objects, which also happen to come with a built-in factory function for creating those objects. Not only that, but TypeScript is deeply integrated with them.<\/p>\n\n\n\n<p>You can declare:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-7\" data-shcb-language-name=\"TypeScript\" data-shcb-language-slug=\"typescript\"><span><code class=\"hljs language-typescript\"><span class=\"hljs-keyword\">class<\/span> Person {\n  firstName: <span class=\"hljs-built_in\">string<\/span>;\n  lastName: <span class=\"hljs-built_in\">string<\/span>;\n\n  <span class=\"hljs-keyword\">constructor<\/span>(<span class=\"hljs-params\">firstName: <span class=\"hljs-built_in\">string<\/span>, lastName: <span class=\"hljs-built_in\">string<\/span><\/span>) {\n    <span class=\"hljs-keyword\">this<\/span>.firstName = firstName;\n    <span class=\"hljs-keyword\">this<\/span>.lastName = lastName;\n  }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-7\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">TypeScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">typescript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Not only will this provide you a factory function for creating instances of a Person, via&nbsp;<code>new Person('Adam', 'Rackis')<\/code>, but&nbsp;<code>Person<\/code>&nbsp;can also be used as a type within TypeScript. You can create variables or function parameters of type&nbsp;<code>Person<\/code>. It&#8217;s one of the few things that exist as a runtime construct and <em>also<\/em> a TypeScript type.<\/p>\n\n\n\n<p>That said, if you find yourself reaching for&nbsp;<code>extends<\/code>&nbsp;in order to create deep inheritance hierarchies with classes, please please re-think your decisions.<\/p>\n\n\n\n<p>Anyway, why am I bringing up classes in this post?<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"fine-grained-reactivity-in-svelte-5\">Fine-grained reactivity in Svelte 5<\/h2>\n\n\n\n<p>If you have a performance-sensitive section of code where you need to mark some properties as non-reactive, you can do this by creating class instances rather than vanilla JavaScript objects. Let&#8217;s define a Task class for our tasks. For the properties we&nbsp;<em>want to<\/em>&nbsp;be reactive, we&#8217;ll set default values with the&nbsp;<code>$state()<\/code>&nbsp;rune. For properties we&nbsp;<em>don&#8217;t<\/em>&nbsp;want to be reactive, we won&#8217;t.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-8\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">Task<\/span> <\/span>{\n  <span class=\"hljs-attr\">id<\/span>: number = <span class=\"hljs-number\">0<\/span>;\n  title = $state(<span class=\"hljs-string\">\"\"<\/span>);\n  assigned = $state(<span class=\"hljs-string\">\"\"<\/span>);\n  importance = $state(<span class=\"hljs-string\">\"\"<\/span>);\n\n  <span class=\"hljs-keyword\">constructor<\/span>(data: Task) {\n    <span class=\"hljs-built_in\">Object<\/span>.assign(<span class=\"hljs-keyword\">this<\/span>, data);\n  }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-8\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>And then we just use that class<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-9\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-keyword\">let<\/span> tasks = $state(&#91;\n  <span class=\"hljs-keyword\">new<\/span> Task({ <span class=\"hljs-attr\">id<\/span>: <span class=\"hljs-number\">1<\/span>, <span class=\"hljs-attr\">title<\/span>: <span class=\"hljs-string\">\"Task A\"<\/span>, <span class=\"hljs-attr\">assigned<\/span>: <span class=\"hljs-string\">\"Adam\"<\/span>, <span class=\"hljs-attr\">importance<\/span>: <span class=\"hljs-string\">\"Low\"<\/span> }),\n  &lt;em&gt;<span class=\"hljs-comment\">\/\/ and so on&lt;\/em&gt;<\/span>\n  <span class=\"hljs-keyword\">new<\/span> Task({ <span class=\"hljs-attr\">id<\/span>: <span class=\"hljs-number\">12<\/span>, <span class=\"hljs-attr\">title<\/span>: <span class=\"hljs-string\">\"Task L\"<\/span>, <span class=\"hljs-attr\">assigned<\/span>: <span class=\"hljs-string\">\"Adam\"<\/span>, <span class=\"hljs-attr\">importance<\/span>: <span class=\"hljs-string\">\"High\"<\/span> }),\n]);<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-9\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>I simplified the class a bit by taking a raw object with all the properties of the class, and assigning those properties with&nbsp;<code>Object.assign<\/code>. The object literal is typed in the constructor as&nbsp;<code>Task<\/code>, the same as the class, but that&#8217;s fine because of TypeScript&#8217;s&nbsp;<a href=\"https:\/\/css-tricks.com\/typescript-discriminated-unions\/\">structural typing<\/a>.<\/p>\n\n\n\n<p>When we run that, we&#8217;ll see the same exact thing as before, except clicking the button to change the <code>id<\/code> will not re-render anything at all in our Svelte component. To be clear, the&nbsp;<code>id<\/code>&nbsp;is still changing, but Svelte is not re-rendering. This demonstrates Svelte intelligently not wiring any kind of observability into that particular property.<\/p>\n\n\n\n<p>Side note: if you wanted to encapsulate \/ protect the&nbsp;<code>id<\/code>, you could declare <code>id<\/code> as&nbsp;<code>#id<\/code>&nbsp;to make it a&nbsp;<a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/JavaScript\/Reference\/Classes\/Private_properties\">private property<\/a>&nbsp;and then expose the value with a getter function.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"going-deeper\">Going deeper<\/h2>\n\n\n\n<p>What if you don&#8217;t want these tasks to be reactive at the individual property at all? What if we have a&nbsp;<em>lot<\/em>&nbsp;of these tasks coming down, and you&#8217;re not going to be editing them? So rather than have Svelte set up reactivity for each of the tasks&#8217; properties, you just want the array itself to be reactive.<\/p>\n\n\n\n<p>You basically want to be able to add or remove entries in your array, and have Svelte update the tasks that are rendered. But you don&#8217;t want Svelte setting up any kind of reactivity for each property on each task.<\/p>\n\n\n\n<p>This is a common enough use case that other state management systems support this directly, for example, MobX&#8217;s&nbsp;<a href=\"https:\/\/mobx.js.org\/observable-state.html#available-annotations\"><code>observable.shallow<\/code><\/a>. Unfortunately Svelte does not have any such helper, as of yet. That said, it&nbsp;<em>is<\/em>&nbsp;currently being debated, so keep your eyes open for a&nbsp;<code>$state.shallow()<\/code>&nbsp;that would do what we&#8217;re about to show. But even if it does get added, implementing it ourselves will be a great way to kick the tires of Svelte&#8217;s new reactivity system. Let&#8217;s see how.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"implementing-our-own-stateshallow-equivalent\">Implementing our own <code>$state.shallow()<\/code> equivalent<\/h3>\n\n\n\n<p>We already saw how passing class instances to an array shut off fine-grained reactivity by default, leaving you to opt-in, as desired, by setting class fields to&nbsp;<code>$state()<\/code>. But our data are likely coming from a database, as plain (hopefully typed) JavaScript objects, unrelated to any class; more importantly we likely have zero desire to cobble together a class just for this.<\/p>\n\n\n\n<p>So let&#8217;s simulate it. Let&#8217;s say that a database is providing our Task objects as JS objects. We (of course) have a type for this:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-10\" data-shcb-language-name=\"TypeScript\" data-shcb-language-slug=\"typescript\"><span><code class=\"hljs language-typescript\"><span class=\"hljs-keyword\">type<\/span> Task = {\n  id: <span class=\"hljs-built_in\">number<\/span>;\n  title: <span class=\"hljs-built_in\">string<\/span>;\n  assigned: <span class=\"hljs-built_in\">string<\/span>;\n  importance: <span class=\"hljs-built_in\">string<\/span>;\n};<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-10\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">TypeScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">typescript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>We want to put those instances into an array that itself is reactive, but not the individual properties on the tasks. With a tiny bit of cleverness we can make it mostly painless.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-11\" data-shcb-language-name=\"TypeScript\" data-shcb-language-slug=\"typescript\"><span><code class=\"hljs language-typescript\"><span class=\"hljs-keyword\">class<\/span> NonReactiveObjectGenerator {\n  <span class=\"hljs-keyword\">constructor<\/span>(<span class=\"hljs-params\">data: unknown<\/span>) {\n    <span class=\"hljs-built_in\">Object<\/span>.assign(<span class=\"hljs-keyword\">this<\/span>, data);\n  }\n}\n\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">shallowObservable<\/span>&lt;<span class=\"hljs-title\">T<\/span>&gt;(<span class=\"hljs-params\">data: T&#91;]<\/span>): <span class=\"hljs-title\">T<\/span>&#91;] <\/span>{\n  <span class=\"hljs-keyword\">let<\/span> result = $state(data.map(<span class=\"hljs-function\"><span class=\"hljs-params\">t<\/span> =&gt;<\/span> <span class=\"hljs-keyword\">new<\/span> NonReactiveObjectGenerator(t) <span class=\"hljs-keyword\">as<\/span> T));\n  <span class=\"hljs-keyword\">return<\/span> result;\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-11\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">TypeScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">typescript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Our&nbsp;<code>NonReactiveObjectGenerator<\/code>&nbsp;class takes in any object, and then smears all that object&#8217;s properties onto itself. And our&nbsp;<code>shallowObservable<\/code>&nbsp;takes an array of whatever, and maps it onto instances of our&nbsp;<code>NonReactiveObjectGenerator<\/code>&nbsp;class. This will force each instance to be a class instance, with nothing reactive. The&nbsp;<code>as T<\/code>&nbsp;is us forcing TypeScript to treat these new instances as whatever type was passed in. This is accurate, but something TypeScript needs help understanding, since it&#8217;s not (as of now) able to read and understand our call to&nbsp;<code>Object.assign<\/code>&nbsp;in the class constructor.<\/p>\n\n\n\n<p>If you closely read my&nbsp;<a href=\"https:\/\/frontendmasters.com\/blog\/introducing-svelte-5\/#state\">first post<\/a>&nbsp;on Svelte 5, you might recall that you can&#8217;t directly return reactive state from a function, since the state will be read and unwrapped right at the call-site, and won&#8217;t be reactive any longer. Normally you&#8217;d have to do this:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-12\" data-shcb-language-name=\"TypeScript\" data-shcb-language-slug=\"typescript\"><span><code class=\"hljs language-typescript\"><span class=\"hljs-keyword\">return<\/span> {\n  <span class=\"hljs-keyword\">get<\/span> value() {\n    <span class=\"hljs-keyword\">return<\/span> result;\n  },\n  <span class=\"hljs-keyword\">set<\/span> value(newData: T&#91;]) {\n    result = newData;\n  },\n};<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-12\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">TypeScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">typescript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Why wasn&#8217;t that needed here? It&#8217;s true, the&nbsp;<code>$state()<\/code>&nbsp;value will be read at the function&#8217;s call site. So with&#8230;<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-13\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-keyword\">let<\/span> tasks = shallowObservable(getTasks());<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-13\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>&#8230;the tasks&nbsp;<em>variable<\/em>&nbsp;will not be reactive. But the array itself will still be fully reactive. We can still call <code>push<\/code>, <code>pop<\/code>, <code>splice<\/code> and so on. If you can live without needing to re-assign to the variable, this is much simpler. But even if you do need to set the tasks variable to a fresh array of values, you still don&#8217;t even need to use variable assignment. Stay tuned.<\/p>\n\n\n\n<p>I changed the initial tasks array to help out in a minute, but the rest is what you&#8217;d expect.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-14\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-keyword\">const<\/span> getTasks = <span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> &#91;\n  { <span class=\"hljs-attr\">id<\/span>: <span class=\"hljs-number\">1<\/span>, <span class=\"hljs-attr\">title<\/span>: <span class=\"hljs-string\">\"Task A\"<\/span>, <span class=\"hljs-attr\">assigned<\/span>: <span class=\"hljs-string\">\"Adam\"<\/span>, <span class=\"hljs-attr\">importance<\/span>: <span class=\"hljs-string\">\"Low\"<\/span> },\n  &lt;em&gt;<span class=\"hljs-comment\">\/\/ ...&lt;\/em&gt;<\/span>\n  { <span class=\"hljs-attr\">id<\/span>: <span class=\"hljs-number\">12<\/span>, <span class=\"hljs-attr\">title<\/span>: <span class=\"hljs-string\">\"Task L\"<\/span>, <span class=\"hljs-attr\">assigned<\/span>: <span class=\"hljs-string\">\"Adam\"<\/span>, <span class=\"hljs-attr\">importance<\/span>: <span class=\"hljs-string\">\"High\"<\/span> },\n];\n\n<span class=\"hljs-keyword\">let<\/span> tasks = shallowObservable(getTasks());<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-14\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>And with that, rendering should now work, and none of our properties are reactive. Clicking the edit buttons do nothing.<\/p>\n\n\n\n<p>But we can now add a button to push a new task onto our array.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-15\" data-shcb-language-name=\"TypeScript\" data-shcb-language-slug=\"typescript\"><span><code class=\"hljs language-typescript\">&lt;button\n  onclick={<span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span>\n    tasks.value.push(\n      <span class=\"hljs-keyword\">new<\/span> NonReactiveObjectGenerator({\n        id: nextId++,\n        title: <span class=\"hljs-string\">'New task'<\/span>,\n        assigned: <span class=\"hljs-string\">'Adam'<\/span>,\n        importance: <span class=\"hljs-string\">'Low'<\/span>\n      }) <span class=\"hljs-keyword\">as<\/span> Task\n    )}\n&gt;\n  Add <span class=\"hljs-keyword\">new<\/span> task\n&lt;<span class=\"hljs-regexp\">\/button&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-15\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">TypeScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">typescript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>We can even add a delete button to each row.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-16\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">&lt;button onclick={() =&gt; tasks.value.splice(idx, <span class=\"hljs-number\">1<\/span>)}&gt;\n  Delete\n&lt;<span class=\"hljs-regexp\">\/button&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-16\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Yes, Svelte&#8217;s reactive array is smart enough to understand push and splice.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"editing-tasks-this-way\">Editing tasks this way<\/h3>\n\n\n\n<p>You might be wondering if we can still actually edit the individual tasks. We assumed the tasks would be read-only, but what if that changes? We&#8217;ve been modifying the array and watching Svelte re-render correctly. Can&#8217;t we edit an individual task by just cloning the task, updating it, and then re-assigning to that index? The answer is yes, with a tiny caveat.<\/p>\n\n\n\n<p>Overriding an array index (with a&nbsp;<em>new<\/em>&nbsp;object instance) does work, and makes Svelte update. But we can&#8217;t just do this:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-17\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">tasks&#91;idx] = { ...t, <span class=\"hljs-attr\">importance<\/span>: <span class=\"hljs-string\">\"X\"<\/span> + t };<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-17\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Since that would make the new object, which is an object literal, deeply reactive. We have to keep using our class. This time, to keep the typings simple, and to keep the code smell that is the <code>NonReactiveObjectGenerator<\/code> class hidden as much as possible, I wrote up a helper function.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-18\" data-shcb-language-name=\"TypeScript\" data-shcb-language-slug=\"typescript\"><span><code class=\"hljs language-typescript\"><span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">cloneNonReactive<\/span>&lt;<span class=\"hljs-title\">T<\/span>&gt;(<span class=\"hljs-params\">data: T<\/span>): <span class=\"hljs-title\">T<\/span> <\/span>{\n  <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-keyword\">new<\/span> NonReactiveObjectGenerator(data) <span class=\"hljs-keyword\">as<\/span> T;\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-18\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">TypeScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">typescript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>As before, the type assertion is unfortunately needed. This same function could also be used for the add function we saw above, if you prefer.<\/p>\n\n\n\n<p>To prove editing works, we&#8217;ll leave the entire template alone, except for the&nbsp;<code>importance<\/code>&nbsp;field, which we&#8217;ll modify like so<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-19\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">  &lt;div&gt;\n    <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">span<\/span>&gt;<\/span>{t.importance + getCounter()}<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">span<\/span>&gt;<\/span><\/span>\n    <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">button<\/span>\n      <span class=\"hljs-attr\">onclick<\/span>=<span class=\"hljs-string\">{()<\/span> =&gt;<\/span> {\n        const taskClone = cloneNonReactive(t);\n        taskClone.importance += 'X';\n        tasks&#91;idx] = cloneNonReactive(taskClone);\n      }}\n    &gt;\n      Update importance\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">button<\/span>&gt;<\/span><\/span>\n&lt;<span class=\"hljs-regexp\">\/div&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-19\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Now running shows everything as it&#8217;s always been.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"470\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/08\/svelte5-shallow.png?resize=1024%2C470&#038;ssl=1\" alt=\"\" class=\"wp-image-3471\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/08\/svelte5-shallow.png?resize=1024%2C470&amp;ssl=1 1024w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/08\/svelte5-shallow.png?resize=300%2C138&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/08\/svelte5-shallow.png?resize=768%2C352&amp;ssl=1 768w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/08\/svelte5-shallow.png?resize=1536%2C704&amp;ssl=1 1536w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/08\/svelte5-shallow.png?resize=2048%2C939&amp;ssl=1 2048w\" sizes=\"auto, (max-width: 1000px) 100vw, 1000px\" \/><\/figure>\n\n\n\n<p>If we click the button to change the id, title or assigned value, nothing changes, because we&#8217;re still mutating those properties directly (since I didn&#8217;t change anything) in order to demonstrate that they&#8217;re not reactive. But clicking the button to update the importance field runs the code above, and updates the&nbsp;<em>entire<\/em>&nbsp;row, showing any other changes we&#8217;ve made.<\/p>\n\n\n\n<p>Here I clicked the button to update the title, twice, and then clicked the button to update the importance. The former did nothing, but the latter updated the component to show all changes.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"457\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/08\/svelte5-shallow-updated.png?resize=1024%2C457&#038;ssl=1\" alt=\"\" class=\"wp-image-3472\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/08\/svelte5-shallow-updated.png?resize=1024%2C457&amp;ssl=1 1024w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/08\/svelte5-shallow-updated.png?resize=300%2C134&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/08\/svelte5-shallow-updated.png?resize=768%2C343&amp;ssl=1 768w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/08\/svelte5-shallow-updated.png?resize=1536%2C686&amp;ssl=1 1536w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/08\/svelte5-shallow-updated.png?resize=2048%2C915&amp;ssl=1 2048w\" sizes=\"auto, (max-width: 1000px) 100vw, 1000px\" \/><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"re-assigning-to-the-tasks-array\">Re-assigning to the tasks array<\/h3>\n\n\n\n<p>We saved a bit of convenience by returning our state value directly from our <code>shallowObservable<\/code> helper, but at the expense of not being able to assign directly to our array. Or did we?<\/p>\n\n\n\n<p>If you know a bit of JavaScript, you might know&#8230;<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-20\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">tasks.length = <span class=\"hljs-number\">0<\/span>;<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-20\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>&#8230;is the old school way to clear an array. That works with Svelte; the Proxy object Svelte sets up to make our array observable works with that. Similarly, we can set the array to a fully new array of values (after clearing it like we just saw) like this:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-21\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">tasks.push(...newArray);<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-21\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>It&#8217;s up to you which approach you take, but hopefully Svelte ships a&nbsp;<code>$state.shallow<\/code>&nbsp;to provide the best of both worlds: the array would be reactive, and so would the binding, since we don&#8217;t have to pass it across a function boundary; it would be built directly into&nbsp;<code>$state<\/code>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"sveltekit\">SvelteKit<\/h2>\n\n\n\n<p>Let&#8217;s wrap up by briefly talking about how data from SvelteKit loaders is treated in terms of reactivity. In short, it&#8217;s exactly how you&#8217;d expect. First and foremost, if you return a raw array of objects from your loader like this:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-22\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">const<\/span> load = <span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> {\n  <span class=\"hljs-keyword\">return<\/span> {\n    <span class=\"hljs-attr\">tasks<\/span>: &#91;\n      { <span class=\"hljs-attr\">id<\/span>: <span class=\"hljs-number\">1<\/span>, <span class=\"hljs-attr\">title<\/span>: <span class=\"hljs-string\">\"Task A\"<\/span>, <span class=\"hljs-attr\">assigned<\/span>: <span class=\"hljs-string\">\"Adam\"<\/span>, <span class=\"hljs-attr\">importance<\/span>: <span class=\"hljs-string\">\"Low\"<\/span> },\n      &lt;em&gt;<span class=\"hljs-comment\">\/\/ ...&lt;\/em&gt;<\/span>\n      { <span class=\"hljs-attr\">id<\/span>: <span class=\"hljs-number\">12<\/span>, <span class=\"hljs-attr\">title<\/span>: <span class=\"hljs-string\">\"Task L\"<\/span>, <span class=\"hljs-attr\">assigned<\/span>: <span class=\"hljs-string\">\"Adam\"<\/span>, <span class=\"hljs-attr\">importance<\/span>: <span class=\"hljs-string\">\"High\"<\/span> },\n    ],\n  };\n};<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-22\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Then none of that data will be reactive in your component. This is to be expected. To make data reactive, you need to wrap it in&nbsp;<code>$state()<\/code>. As of now, you can&#8217;t call&nbsp;<code>$state<\/code>&nbsp;in a loader, only in a universal Svelte file (something that ends in&nbsp;<code>.svelte.ts<\/code>). Hopefully in the future Svelte will allow us to have loaders named&nbsp;<code>+page.svelte.ts<\/code>&nbsp;but for now we can throw something like this in a&nbsp;<code>reactive-utils.svelte.ts<\/code>&nbsp;file.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-23\" data-shcb-language-name=\"TypeScript\" data-shcb-language-slug=\"typescript\"><span><code class=\"hljs language-typescript\"><span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">const<\/span> makeReactive = &lt;T&gt;(arg: T&#91;]): T&#91;] =&gt; {\n  <span class=\"hljs-keyword\">let<\/span> result = $state(arg);\n  <span class=\"hljs-keyword\">return<\/span> result;\n};<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-23\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">TypeScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">typescript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Then import it and use it in our loader.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-24\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-keyword\">import<\/span> { makeReactive } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\".\/reactive-utils.svelte\"<\/span>;\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">const<\/span> load = <span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> {\n  <span class=\"hljs-keyword\">return<\/span> {\n    <span class=\"hljs-attr\">tasks<\/span>: makeReactive(&#91;\n      { <span class=\"hljs-attr\">id<\/span>: <span class=\"hljs-number\">1<\/span>, <span class=\"hljs-attr\">title<\/span>: <span class=\"hljs-string\">\"Task A\"<\/span>, <span class=\"hljs-attr\">assigned<\/span>: <span class=\"hljs-string\">\"Adam\"<\/span>, <span class=\"hljs-attr\">importance<\/span>: <span class=\"hljs-string\">\"Low\"<\/span> },\n      &lt;em&gt;<span class=\"hljs-comment\">\/\/ ...&lt;\/em&gt;<\/span>\n      { <span class=\"hljs-attr\">id<\/span>: <span class=\"hljs-number\">12<\/span>, <span class=\"hljs-attr\">title<\/span>: <span class=\"hljs-string\">\"Task L\"<\/span>, <span class=\"hljs-attr\">assigned<\/span>: <span class=\"hljs-string\">\"Adam\"<\/span>, <span class=\"hljs-attr\">importance<\/span>: <span class=\"hljs-string\">\"High\"<\/span> },\n    ]),\n  };\n};<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-24\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Now those objects will support the same fine-grained reactivity we saw before. To customize which properties are reactive, you&#8217;d swap in class instances, instead of vanilla object literals, again just like we saw. All the same rules apply.<\/p>\n\n\n\n<p>If you&#8217;re wondering why we did this&#8230;<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-25\" data-shcb-language-name=\"TypeScript\" data-shcb-language-slug=\"typescript\"><span><code class=\"hljs language-typescript\"><span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">const<\/span> makeReactive = &lt;T&gt;(arg: T&#91;]): T&#91;] =&gt; {\n  <span class=\"hljs-keyword\">let<\/span> result = $state(arg);\n  <span class=\"hljs-keyword\">return<\/span> result;\n};<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-25\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">TypeScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">typescript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>&#8230;rather than this&#8230;<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-26\" data-shcb-language-name=\"TypeScript\" data-shcb-language-slug=\"typescript\"><span><code class=\"hljs language-typescript\"><span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">const<\/span> makeReactive = &lt;T&gt;(arg: T&#91;]): T&#91;] =&gt; {\n  <span class=\"hljs-keyword\">return<\/span> $state(arg);\n};<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-26\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">TypeScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">typescript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>&#8230; the answer is that the latter is simply disallowed. Svelte forces you to only put&nbsp;<code>$state()<\/code>&nbsp;calls into assignments. It cannot appear as a return value like this. The reason is that while returning $state variables directly across a function boundary works fine for objects and arrays, doing this for primitive values (strings or numbers) would produce a senseless result. The variable could not be re-assigned (same as we saw with the array), but as a primitive, there&#8217;d be no other way to edit it. It would just be a non-reactive constant.<\/p>\n\n\n\n<p>Svelte forcing you to take that extra step, and assign $state to a variable before returning, is intended to help prevent you from making that mistake.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"wrapping-up\">Wrapping up<\/h2>\n\n\n\n<p>One of the most exciting features of Svelte 5 is the fine-grained reactivity it adds. Svelte was already lightweight, and faster than most, if not all of the alternatives. These additions in version 5 only improve on that. When added to the state management improvements we&#8217;ve already covered in prior posts, Svelte 5 really becomes a serious framework option.<\/p>\n\n\n\n<p>Consider it for your next project.<\/p>\n\n\n<div class=\"box article-series\">\n  <header>\n    <h3 class=\"article-series-header\">Article Series<\/h3>\n  <\/header>\n  <div class=\"box-content\">\n            <ol>\n                      <li>\n              <a href=\"https:\/\/frontendmasters.com\/blog\/introducing-svelte-5\/\">Introducing Svelte 5<\/a>\n            <\/li>\n                      <li>\n              <a href=\"https:\/\/frontendmasters.com\/blog\/snippets-in-svelte-5\/\">Snippets in Svelte 5<\/a>\n            <\/li>\n                      <li>\n              <a href=\"https:\/\/frontendmasters.com\/blog\/fine-grained-reactivity-in-svelte-5\/\">Fine-Grained Reactivity in Svelte 5<\/a>\n            <\/li>\n                  <\/ol>\n        <\/div>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>Svelte is already quite lightweight and fast, but Svelte 5 still overs big improvements in fine-grained reactivity, meaning re-rendering as absolutely little as possible. <\/p>\n","protected":false},"author":21,"featured_media":3091,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"sig_custom_text":"","sig_image_type":"featured-image","sig_custom_image":0,"sig_is_disabled":false,"inline_featured_image":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[1],"tags":[3,28,161],"class_list":["post-3438","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-blog-post","tag-javascript","tag-reactivity","tag-svelte"],"acf":[],"jetpack_featured_media_url":"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/07\/Screenshot-2024-07-18-at-3.03.18%E2%80%AFPM.png?fit=1890%2C1122&ssl=1","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/3438","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/users\/21"}],"replies":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/comments?post=3438"}],"version-history":[{"count":16,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/3438\/revisions"}],"predecessor-version":[{"id":3493,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/3438\/revisions\/3493"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/media\/3091"}],"wp:attachment":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/media?parent=3438"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/categories?post=3438"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/tags?post=3438"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}