Home >Web Front-end >JS Tutorial >NgSysV.Creating a simple Reactive Svelte webapp
This post series is indexed at NgateSystems.com. You'll find a super-useful keyword search facility there too.
Last reviewed: Nov '24
The previous post created a Svelte webapp made up entirely of simple HTML. It didn't contain any Javascript and thus didn't use any of Svelte's sophisticated dynamic HTML generation facilities. This post will only scratch the surface of Svelte features but should give you a feel for the power of the language.
Viewed objectively, a webapp is a complex hierarchy of HTML pages, popups, tables, forms and buttons. This dizzying assembly of visual elements is "informed" by data. Tables are inflated with JavaScript array content. Popups appear and vanish at the behest of flag fields. Forms blossom with data when "poked" by mouse and keyboard input. In short - it's complicated. How can some sort of order be established over this situation?
Collectively, the body of data defining the appearance of a webapp at any moment in time is referred to as the webapp's state. Much effort has been expended on software development technology designed to make it easier to write webapps that react efficiently when state changes. Platforms like Svelte are expressly designed to deliver good reactivity.
Let's look at Svelte's approach to the definition of State and its implementation of Reactivity.
A common feature on a web page is a pop-up window that is displayed (usually annoyingly) when you start it up, but which then, obligingly, disappears when you click on it. Let's see how Svelte would let you use State and Reactivity to implement this in a webapp.
In Javascript, you need to define a variable before you can use it. So for example, a command like
console.log(myDataItem);
will throw an error unless somewhere earlier in the code you had defined it using a statement like
let myDataItem;
As an aside, you'll find it useful to have access to a "playground" that lets you quickly try out bits of JavaScript like this. The "inspect" view of any browser page provides one such playground - open the inspector on a random page and select the "console" tab on its menu bar. Alternatively, you might try the javascript console at PlayCode.
There's lots to say about JavaScript's let keyword (try asking chatGPT about "scope") but let has an additional significance in Svelte because this is how you define State. In a Svelte program, any variable defined with let becomes part of the program's State.
So what? Well, I said earlier that State variables - variables that define the appearance of a webapp - are "reactive". This means that when their value changes, the appearance of the webapp is directed automatically to change accordingly. Suppose you use a popupVisible boolean variable to define the state of the popup. Could Svelte - a reactive platform - use the variable's value to determine whether or not the popup is visible? Let's try.
Here's the code I propose to use. I'll explain what it does in a minute:
//src/routes/ page.svelte - remove this line before running <script> let popupVisible = true; function togglePopup() { popupVisible = !popupVisible; } </script> <div> {#if popupVisible} <button > <p>The code here is divided into two sections. The upper "script" section, defined by the <script></script> tags, declares a popupVisible javascript variable and a togglePopup() function. The lower "template" section, specifies HTML "moderated" by a Svelte {#if} {/if} logic block. Svelte code can reference variables and functions defined in the <script> section to guide HTML code generation. References are enclosed by curly {} brackets, except when they're used directly within a logic block - see Svelte docs at Basic Markup and Logic Blocks for full details.</p> <p>The code displayed above is very crude. A "pop-up" is normally defined by a <div> tag formatted with a border and some positioning CSS. Here I've used a <button> tag - something that would be more commonly used to define the "submit" element on a form. I've done this simply because it enables me to avoid some technicalities that might distract you. I've coloured the button "popup" blue so that you can see it.</p> <p>If you still have a copy of the skeleton svelte-dev Svelte project created in Post 2.1, try using this code to replace the entire content of src/routes/ page.svelte. When you open a terminal on the project and start the dev server as before, your browser should now leap into life and display the screen shown below:</p> <p><img src="https://img.php.cn/upload/article/000/000/000/173277643775221.jpg" alt="NgSysV.Creating a simple Reactive Svelte webapp"></p> <p>The blue rectangle is the "pop-up". It is displayed at startup because at this point the popupVisible variable is set to true and the Svelte logic driving the page's HTML generation is instructed to create a <button> tag in this case.</p> <p>Now try clicking the pop-up. Magic! It disappears. This is because the <button> tag specifying the popup contains an onClick clause that makes a click on the element run the togglePopup function. This in turn sets the popupVisible variable to false.</p><p>I said earlier that changing a Svelte "state" variable causes Svelte to refresh the screen by re-running the page's template section. So, the template section re-runs and now, since the value of popupVisible is false, the <button> code displaying the pop-up is bypassed leaving an empty page.</p> <p>Take a moment now to let this sink in. If you'd still been working with the primitive DOM-manipulation code I introduced in Post 1.1 this effect could only have been achieved by writing a good deal of fairly technical (and probably rather inefficient) low-level code. Here, you've just had to change the value of a variable. Sveltekit has handled the intricate consequences <strong>automatically</strong> (and, rest assured, efficiently). </p> <p>In short, Svelte gives you a high-level language that lets you leave the tedious details of browser screen management to the framework. </p> <p><em>In October '24, Svelte 5 introduced a series of refinements to its reactivity-definition arrangements. The let construct described above still works fine for everything in this post series, but a new runes concept is now available to handle more complex requirements. See What are Runes for details.</em></p> <h4> 2.2 Data Input </h4> <p>Let's make the pop-up a bit more interesting. Imagine that you had the job of maintaining a "products" list for a manufacturing company. You'd want a utility that displays the current list and enables you to add new products. In practice, of course, you'd also want it to be able to edit and delete entries, but let's keep things simple for now. </p> <p>Have a look at the following code. It will probably stretch your JavaScript knowledge but the internal comments should help.<br> </p> <pre class="brush:php;toolbar:false">//src/routes/ page.svelte - remove this line before running <script> let popupVisible = false; let newProductNumber = ""; let products = []; // An array of product objects {productNumber: productNumber} </script> <div> <p>The "template" section here begins by checking the value of popupVisible. If this is false (which it will be on startup because the variable has just been initialised in the "script" section), the webapp displays the webapp's current list of "products" (an array of productNumbers stored in a products array). </p> <p>The code now move on and displays an "Add another Product"button - a normal one now, not the freakish version used in the previous example. But, as before, it has an associated on:click function, and this one sets the value of popupVisible to true.</p> <p>What will happen if the button is clicked? Why, since popupVisible is a reactive variable, the webapp will refresh its screen.</p> <p>This time, the products-display section of the form is ignored and control will move straight to the second part of the template section.</p><p>Here, you'll see something more like a conventional popup. This one uses a <form> tag within a container div> to collect input for a newProductNumber state variable. A special Svelte "bind" qualifier" is employed here here to synchronize newProductNumber with the user's keyboard typing. Every time the user types a character into the form's <input> field the newProductNumber is updated. <p>When the user finally clicks the form's "Register" button, its on:click function is thus able to push a fully-updated newProductNumber into the products array and reset the popupVisible field.</p> <p>Finally, since popupVisible is a reactive variable, the "template" section of the code re-runs and the popup is replaced by the updated list of product numbers.</p> <p>One or two other things may puzzle you. In the previous example, anon:click qualifier referenced a function defined in the webapp's <script> section. In this new version of page.svelte, however, the function is defined explicitly within the <button> field's HTML spec. Both approaches are perfectly valid. The version used here probably looks a bit strange to you, but it is a widely-used arrangement that has the advantage that the function's logic is defined at its point of invocation. This makes it much easier to see what's going on. </p> <p>For reference, the expression:<br> "() => { .... }" is saying "here's a function defined by the following JavaScript statements: "{ .... }". There are many variants. Ask chatGPT to give you a tutorial on "arrow" functions .</p> <p>Moving on, you'll also note that I've been much more adventurous with my "styling". There's a heading, for example, and everything is nicely centred (CSS "inheritance" ensures that the> </p><p>But that's enough background. Let's see the webapp in action. Paste the new code over the current content of your page.svelte file, save it and (if necessary) open a terminal on your project and restart your dev server with the npm run dev -- --open command. Here's what you should see in your browser::</p> <p><img src="https://img.php.cn/upload/article/000/000/000/173277643810144.jpg" alt="NgSysV.Creating a simple Reactive Svelte webapp"></p> <p>And here's what you should see when you click the button:<br><br></p> <p><img src="https://img.php.cn/upload/article/000/000/000/173277643952736.jpg" alt="NgSysV.Creating a simple Reactive Svelte webapp"></p> <p>Since there's no validation on the newProductNumber field, you will find that you can enter characters as well as numbers - we'll fix this in a future post. Even so, when you click the "Register" button, you should see the popup replaced by the original Inventory display page, with your new product "number" added to the list.</p> <p>This scrap of Svelte is almost starting to look like an information system! </p><h3> 3. Onward and Upward </h3> <p>This post has given you a brief glimpse of the key concepts embodied in the Svelte language. Unfortunately, there's a snag. As I'm sure you will have noticed, the "Inventory" example you've just seen is effectively useless because every time you close the webapp its product list disappears! The next step, therefore, is to introduce you to Google's <strong>Firestore</strong> database technology. This will let you create <strong>persistent</strong> storage on a server host. </p> <h3> Postscript (a): When things go wrong - Investigating Layout issues with the Chrome Inspector </h3> <p>"When things go wrong" in Post 2.1 described how to handle some of the grosser problems you'll encounter when developing a webapp. But now that you're working at a more advanced level you need a more sophisticated tool. This section introduces you to the "Chrome Inspector", an invaluable aid when correcting problems with your webapp's screen layout and logic errors in your JavaScript (and much more besides, as you'll see later in this series).</p> <p>The Inspector is launched on a webapp Page by right-clicking on the page and selecting the "Inspect" option. Here's an example of its output:</p> <p><img src="https://img.php.cn/upload/article/000/000/000/173277644070139.jpg" alt="NgSysV.Creating a simple Reactive Svelte webapp"></p> <p>In this screenshot, I have:</p>
If you try this yourself, you'll note that the heading has acquired some coloured graphics. These show you the size and position of the margin, border and padding that have been added to your text. The right-hand inspection panel provides more detail. If you click the computed tab in its menu, you'll get a graphic that explains the colour coding and detailed sizings for the various elements. If you click the "styles" tab you'll see details of the styles attached to the text. The first set of entries in the list confirms those styles set explicitly with the tag's>
The beauty of this panel is that you can use the inspector to see the effect of changing and adding styles. For example, suppose you wanted to try out a red font on the heading, click somewhere in the element.style entry at the top of the panel and enter color: red. The heading, obligingly, now turns red. Note the opportunity to use autocompletion to try out different colours.
I don't want to labour the point, but I think you should be starting to see how this facility can give you a precise and intuitive tool for investigating and fixing layout issues. For more information check out Google's documentation on the inspector at DevTools.
Be prepared to spend quite some time on this. The Inspector is a complex tool so it will be a while before you can use it confidently. But this will be time well spent. The Inspector has certainly saved me countless hours of blind experimentation with source-code settings. The inspector's graphic display makes visible all the hidden properties of element widths, margins and padding.
The Inspector can also help you find logic errors by enabling you to view your source code and set "breakpoints" on lines where you would like execution to halt. After refreshing the webapp (with the Inspector still open), you can inspect field values at each breakpoint. Here's a screenshot of the Inspector in action:
In this screenshot, I've:
The effect of all this has been to put the webapp into "code debug" mode. Right now, I'm debugging the section of the webapp, which is a bit pointless since it only runs once and you know exactly what it does. But let's go through the motions because they'll help you get a feel for what the debugger can do for you in JavaScript source code.
Currently, the webapp is halted before execution of the first statement and so hasn't done anything useful. But advance to the next statement by clicking the "Step over next" button and note that the highlight has now moved to the second line. Mouse over the popupVisible field in the first line and note that a tooltip is now displayed showing its current value - false, of course. Repeat the trick to confirm the settings of newProductNumber and products.
The value of the Inspector is more clearly seen if you set breakpoints in Javascript functions defined in the