# Understanding Svelte reactivity: Advanced techniques Unlike traditional frameworks that rely on a virtual DOM, [Svelte](https://www.educative.io/answers/what-is-svelte) takes a unique approach to reactivity. By analyzing code during compilation, it discerns the precise updates required, sidestepping the need for a [virtual DOM](https://www.geeksforgeeks.org/reactjs-virtual-dom/) and resulting in enhanced speed and streamlined code. Leveraging reactive statements marked with `let`, Svelte efficiently manages application data. Any alterations to reactive values trigger automatic [UI](https://www.interaction-design.org/literature/topics/ui-design) updates, eliminating manual intervention and simplifying state management. This approach empowers developers to concentrate on crafting core functionalities, rather than grappling with intricate state management. Mastery of advanced techniques in Svelte not only boosts performance and facilitates smoother management but also enables the construction of sophisticated features, propelling developers beyond basic coding proficiency to creators of exceptional user experiences. In this article, we won't just elucidate advanced methodologies; we'll exemplify them through the construction of a checkout page. This hands-on approach promises a profound grasp of Svelte's capabilities, ensuring a transformative understanding of its potential. ## Setting our project Before diving into our project, it's crucial to ensure that we have all the essential tools and packages installed on our system. This primarily includes [Node.js](https://www.w3schools.com/nodejs/nodejs_intro.asp) and [npm](https://www.w3schools.com/whatis/whatis_npm.asp). To set them up, we'll head over to the official Node.js website. You can find the installation instructions [here](https://nodejs.org/en/download). Once we have Node.js and npm up and running, we're ready to create our project. This can be accomplished by executing the following command: ```bash npm create vite@latest checkout-page -- --template svelte cd checkout-page npm install ``` Executing `npm create vite@latest checkout-page -- --template svelte` initiates the creation of a new directory named `checkout-page` in the current directory. Navigating into this directory is facilitated by the `cd checkout-page`, which brings us to the project's root. Subsequently, `npm install` is executed to install all required packages. For styling our components, we've opted to utilize [Tailwind CSS](https://www.geeksforgeeks.org/introduction-to-tailwind-css/). To integrate Tailwind CSS into our project, we'll execute the following command in the project's root directory: ```bash npm install -D tailwindcss postcss autoprefixer npx tailwindcss init -p ``` To ensure Tailwind CSS functions smoothly within our project, additional steps are required. You can refer to the [Tailwind installation guide](https://tailwindcss.com/docs/guides/vite#svelte) for detailed instructions. As for incorporating icons into our project, we'll need the `svelte-icons` package. To install it, execute the following command: ```bash npm install svelte-icons ``` Now, we are ready to go !!!!. ## Basic reactivity Imagine the data in your application as a flowing river, constantly changing. This river represents the dynamic nature of your app, with new information flowing through it like water. Each piece of data, like a floating leaf or pebble, carries meaning and contributes to the overall state of your application. Svelte, like an observer standing by the river, helps you react to these changes effortlessly. Just as the observer notices fluctuations in the water's flow, it detects alterations in the data stream. How does Svelte achieve this? One method is through reactive statements. Reactive statements are any statement that can trigger a re-render when the data they depend on, changes. This includes: - **Reactive assignments** These are similar to the riverhead. They are the fundamental source of data that can change. It is defined by using the `let` keyword. We will explain this further with our project `checkout-page`. After creating our project by following the steps listed above, we should have a file structure that looks like this: ![File structure](https://hackmd.io/_uploads/HJxMPmwaa.png) To bring our project to life, we'll introduce several components that necessitate modification: `Checkout`, `Nav`, and `ProductInfo`. These components reside within the `lib` directory. Notably, each component name ends with `.svelte`, denoting it as a Svelte component. Upon inclusion of these components, our updated file structure appears as follows: ![File structure](https://hackmd.io/_uploads/rJ3QD7wT6.png) Now, let's proceed to modify our `ProductInfo` component to expound upon the reactive assignment. `ProductInfo` component. ```svelte <script> import MdAdd from "svelte-icons/md/MdAdd.svelte"; import MdRemove from "svelte-icons/md/MdRemove.svelte"; let quantity = 1; const handleQty = (add) => { if (add) { quantity++; } else { quantity--; } }; </script> <div class="flex my-4 items-center"> <div class="flex px-3"> <button disabled={quantity === 1} class="flex items-center px-1 text-sm border-2 border-gray-300" on:click={() => handleQty(false)} > <span class="w-4"><MdRemove /></span> </button> <span class="px-2 font-semibold text-xm border-y-2 border-gray-300" >{quantity}</span > <button disabled={quantity === 100} class="flex items-center px-1 border-2 border-gray-300" on:click={() => handleQty(true)} > <span class="w-4"><MdAdd /></span> </button> </div> </div> ``` In the `ProductInfo` component, we've imported `MdAdd` and `MdRemove` from `svelte-icons`, which we'll utilize later in our code to adjust the `quantity` value. Additionally, we've established a reactive assignment: `let quantity = 1`. As previously mentioned, reactive assignments serve as the fundamental source of data that can change. When the value of a reactive assignment, such as `quantity`, is modified, Svelte reacts by re-rendering the component to reflect the updated value. Next, we have the `handleQty` function, which serves as an event handler for the `on:click` event within this component. This function is responsible for modifying the `quantity` value based on user interactions. To make this code available in the browser, we need to make modifications to the `App` component. `App` component ```svelte <script> import ProductInfo from "./lib/ProductInfo.svelte"; </script> <main> <div class="h-[91vh] flex justify-center overflow-y-scroll remove-scrollbar border-t-2 pt-2 border-gray-300" > <ProductInfo /> </div> </main> ``` Here we imported the `ProductInfo` component and defined the UI to be rendered under the `main` element. The styling was done using Tailwind CSS. When we start the code by running `npm run dev` This is what we should see. ![Product info](https://hackmd.io/_uploads/HkPbOqLJ0.png) Clicking on the `+` icon increases the number while clicking on the `-` icon decreases it. - **Expressions involving reactive data** Any operation using reactive variables, can if not re-render the component. An example of expression involving reactive data is the increment and decrement of `quantity` inside the `handleQty` function. ```svelte const handleQty = (add) => { if (add) { quantity++; } else { quantity--; } }; ``` Inside the `handleQty` function we altered reactive data which in our case is `quantity`. This is the reason behind the behavior we see on the browser when we click on either the `-` button or the `+` button. When you modify a reactive value (like increasing `quantity`), Svelte automatically detects the change and updates the UI elements that depend on it, akin to downstream changes observed by the observer. ## Going beyond the basics While basic reactivity is great for simple data manipulation, advanced techniques provide more efficient and powerful tools. ### Reactive declarations These are values that dynamically update themselves based on changes to other reactive values they rely on. This functionality enables you to: - **Optimize updates:** Only the UI elements that truly need to change are updated, improving performance. - **Reduce complexity:** Break down complex logic into smaller, reusable units, keeping your code clean and maintainable. To provide a clearer explanation, let's modify our `ProductInfo` component. We aim to augment `ProductInfo` to display essential details for the user, including the subtotal amount, estimated shipping fee, and the total purchase amount. `ProductInfo` component ```svelte <script> import MdAdd from "svelte-icons/md/MdAdd.svelte"; import MdRemove from "svelte-icons/md/MdRemove.svelte"; let quantity = 1; let price = 100.0; const handleQty = (add) => { if (add) { quantity++; } else { quantity--; } }; $: subtotal = parseFloat((price * quantity).toFixed(2)); $: fee = subtotal * 0.005; $: total = subtotal + fee; </script> <div class="flex flex-col justify-center"> <div class="flex justify-center"> <div class="flex justify-between w-[80vw]"> <h2 class="font-bold text-xl text-black">${price}</h2> <div class="flex px-3"> <button disabled={quantity === 1} class="flex items-center px-1 text-sm border-4 border-gray-300" on:click={() => handleQty(false)} > <span class="w-6"><MdRemove /></span> </button> <span class="px-2 font-bold text-xl border-y-4 border-gray-300" >{quantity}</span > <button disabled={quantity === 100} class="flex items-center px-1 border-4 border-gray-300" on:click={() => handleQty(true)} > <span class="w-6"><MdAdd /></span> </button> </div> </div> </div> <div class="mt-4"> <div class="flex my-4 justify-between"> <h2 class="font-bold text-xm text-black md:text-xl">Subtotal</h2> <h2 class="font-bold text-xm md:text-xl">${subtotal}</h2> </div> <div class="flex my-4 justify-between"> <h2 class="font-bold text-xm text-black md:text-xl"> Estimated Shipping Fee </h2> <h2 class="font-bold text-xl">${fee}</h2> </div> <div class="flex my-4 justify-between"> <h2 class="font-bold text-xl text-black">Estimated Tax</h2> <h2 class="font-bold text-xl">To be determined</h2> </div> <div class="flex my-4 justify-between"> <h2 class="font-bold text-xl text-black">Total</h2> <h2 class="font-bold text-xl">${total}</h2> </div> </div> </div> ``` Upon close examination of our code, you'll notice some unfamiliar syntax marked by the dollar sign `$`. This is where the magic unfolds. The `$:` syntax signals to Svelte that the variable relies on other reactive data, prompting updates whenever these dependencies alter. It's crucial to note that only UI elements requiring modification are updated, such as `<h2 class="font-bold text-xm md:text-xl">${subtotal}</h2>`, `<h2 class="font-bold text-xl">${fee}</h2>`, or `<h2 class="font-bold text-xl">${total}</h2>`. This optimization streamlines our code by targeting specific UI elements for modification, reducing complexity, and enhancing readability, a vital aspect of proficient coding. A well-written code should be comprehensible to other developers without undue effort. Visualizing our browser, it should resemble the following: ![Product info](https://hackmd.io/_uploads/HJgrO5Iy0.png) Clicking on the `+` icon increases the `price`, `Subtotal`, `Estimated Shipping Fee`, and `Total` while clicking on the `-` icon decreases them. ### Computed properties Computed properties epitomize the encapsulation of intricate calculations and logic within reusable functions, thereby optimizing performance and enhancing code organization. A prime example within our codebase is illustrated by the `handleQty` function. ```svelte const handleQty = (add) => { if (add) { quantity++; } else { quantity--; } }; ``` In the `handleQty` exists the calculation to either increase or decrease `quantity`. We can write even more complex calculations if there is a need. `handleQty` is reusable. In our case, we used it twice. i:e ```svelte <button disabled={quantity === 100} class="flex items-center px-1 border-4 border-gray-300" on:click={() => handleQty(true)} > <span class="w-6"><MdAdd /></span> </button> ``` and ```svelte <button disabled={quantity === 1} class="flex items-center px-1 text-sm border-4 border-gray-300" on:click={() => handleQty(false)} > <span class="w-6"><MdRemove /></span> </button> ``` ## Mastering complex scenarios As applications evolve, managing intricate scenarios necessitates the utilization of advanced techniques, such as: ### Custom hooks These are reusable components that encapsulate common reactivity logic, allowing you to: - **Share functionality:** Implement reusable patterns across your application, reducing code duplication and improving maintainability. - **Manage side effects:** Effectively handle asynchronous operations and external interactions without cluttering your components. To enhance clarity, we'll refine our code by modifying several components: `Nav`, `ProductInfo`, `Checkout`, and `App`. Breaking down the `checkout-page` into smaller components improves maintainability and debuggability, mimicking a real-world checkout page structure. First `Nav` component. ```svelte <script> import MdCheckBox from "svelte-icons/md/MdCheckBox.svelte"; </script> <div> <div class="flex m-2 justify-between"> <div class="flex items-center"> <a href="/" class="w-7"><MdCheckBox /></a> <a href="/" class="font-bold text-xl px-3">OpenReplay</a> </div> </div> </div> ``` `Nav` component is used to add a navbar in our project. Second `ProductInfo` component. ```svelte <script> import MdRemove from "svelte-icons/md/MdRemove.svelte"; import MdAdd from "svelte-icons/md/MdAdd.svelte"; import MdDelete from "svelte-icons/md/MdDelete.svelte"; import { createEventDispatcher } from "svelte"; export let detail; $: src = detail.src; $: name = detail.name; $: price = detail.price; $: desc = detail.desc; $: quantity = detail.quantity; const dispatch = createEventDispatcher(); let handleQty = (add) => { if (add) { quantity++; detail = { ...detail, quantity }; } else { quantity--; detail = { ...detail, quantity }; } dispatch("updateQuantity", detail); }; let handleRemove = () => dispatch("removeItem", detail); </script> <div class="flex my-4 items-center"> <div class="overflow-hidden h-[12vh] w-20 md:h-20 rounded-lg"> <img {src} alt="" class="h-full w-full" /> </div> <div class="ml-4 w-full"> <div class="flex justify-between"> <h2 class="font-semibold text-xm md:text-xl">{name}</h2> <button class="w-5 mr-3 md:w-6" on:click={() => handleRemove()} ><MdDelete /></button > </div> <h2 class="font-normal text-sm text-gray-500 md:text-xm">{desc}</h2> <div class="flex justify-between w-full"> <h2 class="font-normal text-sm text-gray-500 md:text-xm">${price}</h2> <div class="flex px-3"> <button disabled={quantity === 1} class="flex items-center px-1 text-sm border-2 border-gray-300" on:click={() => handleQty(false)} > <span class="w-4"><MdRemove /></span> </button> <span class="px-2 font-semibold text-xm border-y-2 border-gray-300" >{quantity}</span > <button disabled={quantity === 100} class="flex items-center px-1 border-2 border-gray-300" on:click={() => handleQty(true)} > <span class="w-4"><MdAdd /></span> </button> </div> </div> </div> </div> ``` Within the `ProductInfo` component, we've expanded the elements to replicate an authentic checkout page. It now encompasses an image, providing users with a visual representation of the product, along with a description section elucidating its features. Additionally, we've included a remove icon, granting users the ability to eliminate unwanted items, alongside existing elements such as the price and buttons facilitating quantity adjustments. The presence of `export let detail;` in the code denotes that the `ProductInfo` component anticipates receiving a value (props) from the parent component. To avoid overcrowding a single component with all functionalities, we've distributed tasks across different components. However, seamless communication between these components is essential. To address this, we've imported `createEventDispatcher` from Svelte. This function enables the emission of custom events. In the `ProductInfo` component, we emit two events: `updateQuantity` and `removeItem`. The `updateQuantity` event signals to the parent component the user's intent to modify the number of items to be purchased, while the `removeItem` event signifies the user's desire to eliminate a product. Subsequently, the parent component is responsible for executing relevant tasks based on these events and returning a new value (`detail`) to the `ProductInfo` component. Next `Checkout` component. ```svelte <script> import ProductInfo from "./ProductInfo.svelte"; let details = [ { id: 0, src: "https://m.media-amazon.com/images/I/61zaC+-OF+L.jpg", name: "Women's Cargo Utility Jogger", price: 78.0, desc: "Size, Medium Color: Black", quantity: 3, }, { id: 1, src: "https://m.media-amazon.com/images/I/61zaC+-OF+L.jpg", name: "Women's Cargo Utility Jogger", price: 780.0, desc: "Size, Medium Color: Black", quantity: 3, }, { id: 2, src: "https://m.media-amazon.com/images/I/61zaC+-OF+L.jpg", name: "Women's Cargo Utility Jogger", price: 8.0, desc: "Size, Medium Color: Black", quantity: 3, }, { id: 3, src: "https://m.media-amazon.com/images/I/61zaC+-OF+L.jpg", name: "Women's Cargo Utility Jogger", price: 230.0, desc: "Size, Medium Color: Black", quantity: 3, }, { id: 4, src: "https://m.media-amazon.com/images/I/61zaC+-OF+L.jpg", name: "Women's Cargo Utility Jogger", price: 38.0, desc: "Size, Medium Color: Black", quantity: 3, }, { id: 5, src: "https://m.media-amazon.com/images/I/61zaC+-OF+L.jpg", name: "Women's Cargo Utility Jogger", price: 4800.0, desc: "Size, Medium Color: Black", quantity: 3, }, ]; const updateQuantity = (e) => { console.log(e.detail); for (var i = 0; i < details.length; i++) { if (details[i].id === e.detail.id) { details[i] = e.detail; break; } } }; const removeItem = (e) => { for (var i = 0; i < details.length; i++) { if (details[i].id === e.detail.id) { const index = details.indexOf(details[i]); details.splice(index, 1); details = details; break; } } }; $: subtotal = details.reduce((a, b) => a + b.price * b.quantity, 0); $: fee = parseFloat((subtotal * 0.005).toFixed(2)); $: total = subtotal + fee; </script> <div class="w-[98vw] overflow-y-scroll remove-scrollbar md:w-[80vw]"> <h1 class="font-bold text-xl md:text-2xl">Checkout</h1> {#if details.length > 0} {#each details as detail (detail.id)} <ProductInfo {detail} on:updateQuantity={updateQuantity} on:removeItem={removeItem} /> {/each} <div class="mt-4"> <div class="flex my-4 justify-between"> <h2 class="font-normal text-xm text-gray-500 md:text-xl">Subtotal</h2> <h2 class="font-normal text-xm md:text-xl">${subtotal}</h2> </div> <div class="flex my-4 justify-between"> <h2 class="font-normal text-xm text-gray-500 md:text-xl"> Estimated Shipping Fee </h2> <h2 class="font-normal text-xm md:text-xl">${fee}</h2> </div> <div class="flex my-4 justify-between"> <h2 class="font-normal text-xm text-gray-500 md:text-xl"> Estimated Tax </h2> <h2 class="font-normal text-xm md:text-xl">To be determined</h2> </div> <div class="flex my-4 justify-between"> <h2 class="font-normal text-xm text-gray-500 md:text-xl">Total</h2> <h2 class="font-normal text-xm md:text-xl">${total}</h2> </div> <div class="flex justify-center"> <button class="text-white bg-blue-700 p-2 rounded-lg w-[40vw] font-semibold mt-2" >Secure Checkout</button > </div> </div> {:else} <div class="flex h-[85vh] justify-center items-center text-gray-300 text-3xl" > No item in the cart </div> {/if} </div> ``` In the `Checkout` component, we've imported the `ProductInfo` component. The `details` variable holds the data intended to be passed to other components and elements. Assuming an API call has been made, the `details` variable contains the data retrieved from the API response. Two functions, `updateQuantity` and `removeItem`, serve as event handlers, listening for `updateQuantity` and `removeItem` events emitted by the `ProductInfo` component. The `updateQuantity` function adjusts the quantity of goods, while `removeItem` removes the product from the `details` list. A conditional statement has been added: if the length of `details` exceeds zero, display the necessary elements and components; otherwise, display `No item in the cart`. This ensures that the `No item in the cart` text is shown when all products have been removed. Finally `App` component. ```svelte <script> import Checkout from "./lib/Checkout.svelte"; import Nav from "./lib/Nav.svelte"; </script> <main> <div class="h-[7vh]"> <Nav /> </div> <div class="h-[91vh] flex justify-center overflow-y-scroll remove-scrollbar border-t-2 pt-2 border-gray-300" > <Checkout /> </div> </main> ``` Here, we've imported the necessary components, namely `Checkout` and `Nav`, and styled them to ensure the navbar remains fixed at the top while the remaining content scrolls. Combining all these components yields the completed project: ![Completed project](https://lh3.googleusercontent.com/drive-viewer/AKGpihZZ6LtpATeyQg_r1VmayquS00dSdVVvzF4K7UbPsgjT95k-Y2__-ni7xgc1MNVC7tFxiFyZ36aqCRz97IUdpC-mOkl_Pw=s1600-v0) Through the development of this checkout page, we've witnessed the significant advantage offered by Svelte reactivity in building dynamic and efficient web applications. Compared to traditional approaches, managing data, and UI updates becomes much simpler and more streamlined with Svelte's reactive programming model. This project has provided a valuable learning experience, showcasing the power of Svelte for creating responsive and interactive web interfaces. Beyond the functionalities demonstrated here, the checkout page can be further enhanced by integrating features such as user authentication, payment processing, and order history management. Exploring these areas in future iterations will further demonstrate the comprehensive capabilities of Svelte in building robust and engaging web experiences. ## Conclusion By embracing advanced reactivity techniques, you unlock a new level of development efficiency: - **Performance:** Optimized updates and calculations lead to smoother user experiences. - **Maintainability:** Modular code and reusable patterns enhance long-term project management. - **Complexity management:** Tackle intricate scenarios with confidence using powerful techniques. As you explore further, remember that Svelte's reactivity system offers a vast landscape of techniques to empower you to craft exceptional web applications. Embrace the journey, and unlock the full potential of this powerful framework!