Coding with Jesse

7 Svelte features that bring me joy

May 6th, 2020

When you learn Svelte for the first time, there are lots of little things that put a smile on your face. Some of the little shortcuts are so elegant, they make things so much easier with cleaner, more succinct code. I recorded a video for YouTube with a demo of seven of my favourite features put together.

1. bind:property

In Svelte, you can very easily bind a property of a DOM element to a local variable. For example, you can bind a variable to the value of an input.

<script>
let name = '';
</script>

<input bind:value={name}/>

2. Directive shortcuts

There's a bunch of really cool shortcuts in Svelte that make using directives and passing props even more succinct. If the property name and variable name are the same, you can write them like this:

<script>
let value = '';
let active = false;
</script>

<input bind:value class:active/>

It's shortcuts like this that make Svelte a real pleasure to work with.

3. Scoped CSS

In your Svelte components, any CSS inside a <style> block will be scoped to that component. That means, a lot of the time you won't need to add classes or IDs for styling. You can often just use the tag name for styling, without any risk of messing up the CSS of the rest of the page.

<style>
  /* No class needed! */
  button {
    font-size: 200%;
    background: red;
  }
</style>

<button>Click me</button>

4. DOM Event Modifiers

If you've ever added a submit handler to a form, or a click handler to a link, and you wanted to handle the event without having the page refresh, you've had to call event.preventDefault() in the handler function. Svelte makes this super easy using a "modifier".

<script>
function handleSubmit() {
  // don't need to call preventDefault in here anymore!
}
</script>

<form on:submit|preventDefault={handleSubmit}>
  <input type="submit"/>
</form>

5. Loop "else" clause

How many times have you written an if statement to check if an array is empty, so that you can display a special "nothing here" message? Svelte makes this very easy by providing an "else" clause to the {#each} block:

<script>
let results = [];
</script>

{#each results as result}
    <p>{result}</p>
{:else}
    <p>Sorry, no results found!</p>
{/each}

6. Transitions

Whenever things appear and disappear on your page, it's really nice to use CSS transitions to have a bit of animation to make the state change feel more natural. Normally, you have to write a bunch of CSS and JavaScript to pull this off, but Svelte makes it super simple. All you have to do is import the effect you want and use the transition: directive on an element, and whenever the element appears or disappears, the transition will play.

<script>
import { fade } from 'svelte/transition';
</script>

<div transition:fade>
  This will fade in and out.
</div>

7. Slot props and the let:variable directive

My absolute favourite Svelte feature is the ability to pass data down from a component to its children. In React, the common way to achieve this is either with a function child or a render prop, and this often makes the code harder to read and understand.

Svelte builds this functionality right into the template syntax. You add a <slot/> element to the parent component, and pass props to it. Then, you receive the data using the let:prop directive:

<!-- ColorManager.svelte -->
<script>
let colors = ['red', 'green', 'blue'];
</script>

<slot {colors} />
<!-- Colors.svelte -->
<script>
import ColorManager from './ColorManager.svelte';
</script>

<ColorManager let:colors>
  <ul>
    {#each colors as color}
      <li>{color}</li>
    {/each}
  </ul>
</ColorManager>

This makes it really easy to write components that are solely responsible for data fetching and management. This allows you to separate out your rendering logic from your state logic, and end up with much simpler code.

Conclusion

Svelte has a ton of really cool features and syntax that make your web components simpler than ever. I've outlined a few of my favourites here. If you're interested in learning more, check out The Joy of Svelte.


Know when to fold 'em

February 4th, 2020

The last few weeks on Twitch, I've been working on the user authentication to use for my side projects, including my upcoming course The Joy of Svelte.

I wrote it in a generic way as Express middleware. It uses a MySQL database with express-session and express-mysql-session. It doesn't have any HTML, it just adds a REST API to your server with some routes like /auth/signup, /auth/login, /auth/forgot and /auth/reset. This way, each of my sites can have a different UI, and use fetch to do everything.

Oh man, I wasted so much time when I was building it. I wanted to try building the whole thing with pure ES modules. ES modules are now supported in Node without a flag, so I figured it's finally time to use them without a build process.

I wasted hours trying to get it to work. Of course Mocha doesn't work with ES modules yet. Eventually, I gave up and went with using a library called esm that gets things working somehow. When I thought I was done, and tried to add it to a Sapper project. Since it was now in my node_modules, and being transpiled by Rollup, it all broke. Sigh.

I was facing another never-ending black hole of googling and debugging. So you know what I did? I went and rewrote the ES modules to CommonJS syntax. It took three minutes.

Lesson is, just because a new feature is available, you still have to wait for your entire tool chain to catch up and adapt as well. It's fun to push the envelope, but it can get exhausting too. Sometimes it's faster to cut your losses and take another route.

As Kenny Rogers warns, "You got to know when to hold 'em, know when to fold 'em, know when to walk away and know when to run."

If you're curious about the authentication middleware, you can see the source code here. It's not perfect for everybody, but if you want to use it and think I should publish it to npm and document it, let me know.

If you're excited about The Joy of Svelte, it'll be launching soon. Sign up for the Coding with Jesse newsletter, and you'll get a subscriber discount as soon as it's ready.


Statically generating a blog with Svelte + Sapper

December 18th, 2019

I've been working on rewriting my blog since forever. In fact, here's a video I made back in 2015 introducing codingwithjesse.com and my plans to rebuild my PHP blog using the latest and greatest web technologies. In 2015, this meant I was going to make a REST API with Node.js, and build a React frontend. So that's where I started.

Fast-forward three-and-a-half years, and the site still wasn't done. I hadn't spent that much time on it really, so it just had a REST API and an administration area for writing and editing blog posts. I had done a tiny bit of the public side using React but it was still in rough shape.

That's about the time I fell in love with Svelte and decided I wanted to use Svelte for everything. In July, I started migrating my blog from React to Svelte + Sapper. (I enjoy rewriting React code using Svelte so much, I would do it all day if I could!)

Static Site Generation

Sapper by default comes with a Node.js web server, which serves dynamic server-side rendered markup that gets re-hydrated in the browser. Alternatively, you can choose to use the Sapper "export" feature to generate a static web site that works with any web hosting, no Node.js needed.

My administration area using the REST API is not part of this static website; the admin will only run on my local computer, using a local database. The site does not need user authentication or any kind of session state, and it only changes when I write new posts, so I decided that a static website would be perfect, at least for now.

What was easy & awesome?

My experience with Sapper was mostly positive. Often I was surprised at how easy things were. Here are some of those surprises.

1. Getting started

Getting started with Sapper is really easy. The Sapper sample template already has a blog as its example code, and comes with all the build and testing infrastructure that you'll need to get a Sapper website up and running.

2. Rollup

I really enjoyed working with Rollup, also created by Rich Harris, the creator of Svelte & Sapper. If you don't want to use Rollup, you can also choose to use Webpack or another build tool, if that's what you're into.

3. Static site generation

The static generation worked great! It starts at your homepage and crawls your site like a spider, looking for new links in any <a> tags it can find. This meant that my secret administration area was excluded, which was exactly what I wanted anyway. It creates directories and index.html files, to create all the URLs you've defined.

4. Static websites are fast!

Once the static site was live, it didn't take long to achieve a perfect lighthouse score! I honestly did not think that was possible, but here we are:

Just redesigned my blog as a static-generated site using Svelte + Sapper, ended up with a perfect lighthouse score!

5. Routes without a router

The way routes work in Sapper is really easy and powerful. You put Svelte components inside the src/routes/ folder to define new routes. If you want a URL like /blog/my-post, you can make a Svelte component in src/routes/blog/[slug].svelte and use the slug to dynamically look up the blog contents in order to render the page. This syntax for dynamic routes is so awesome that even Next.js was inspired to do the same.

I wasn't sure if I'd be able to keep the search box on my blog, since there would be no database to search. Turns out all I needed to do was have the search page use the /blog/all.json route as a data source. I passed the search terms as a query parameter like /blog/search?terms=example The search page parsed the URL to get the search terms, then filters the blog posts client-side to render the results. Might seem ridiculous to have a single JSON file with all the blog posts in it, but on my blog the all.json is only 142kb which is smaller than some JavaScript frameworks! I might write a blog post going into more detail about this client-side search, if anybody is interested?

7. Deployment

Deploying a static site is easy. I use shared hosting so I wrote a bash script that does the following: 1) npm run export to generate the static site, 2) zip up the static files into a zip file, 3) upload the zip file to my web server with scp, 4) ssh into the server and unzip the zip file into the correct folder, 5) delete the zip file. I don't need a complex CI system, though maybe I'll set that up down the road. For now, running a bash script after each blog post is fine for me.

What was hard & confusing?

Learning any new tool has its ups and down. There were some concepts that I didn't understand correctly, and that led me to make mistakes, causing a few bugs and broken pages. Here are some of the things I learned in the process.

1. JSON API

It took me a while to figure out that all the API calls needed to be "JSON API" calls inside server routes that would later generate .json files. Confused already? Let me walk through an example.

When you're viewing a page of the blog, and you click a link to another article, the Sapper client-side code will fetch the contents for that page asynchronously. It can't access the actual REST API so it needs to get the data from a static file, and the best approach for that is to have static .json files in your static site.

For the src/routes/blog/[slug].svelte component I mentioned above, I created a related src/routes/blog/[slug].json.js file that acted as a "server route" that will cause Sapper to export a /blog/my-post.json file for each post.

For more on this, including code examples, check out the Sapper documentation about Server routes.

2. Every page needs to be discovered with a crawler

As I mentioned above, Sapper uses a web crawler to start at your homepage, and dig through looking for links to pages. This means that any pages you might have needs to be linked from an <a> tag. You cannot have any truly secret pages.

To achieve this, I made a single route /blog/all that acts as a site index, with a link to every blog post, plus some extra links at the bottom just so Sapper will know about them. For example, I needed to include an extra link to my RSS feed and my Newsletter signup that weren't linked to with <a> tags anywhere else.

3. Vendor CSS was awkward

Of course I needed to have beautiful looking code examples on my blog, so I integrated Prism.js. I couldn't figure out how to import the prismjs-monokai.css vendor stylesheet into the Svelte component that needed it, so I ended up just using a <link> tag to load it from the template.html, similar to the global.css example file that comes with the Sapper template.

Seems there is a solution that uses a Rollup plugin to allow you to import stylesheets from the Svelte <script> block but I didn't go down this road (yet). Maybe doing an @import in a Svelte <style> block will be something we can do one day, but not today.

Conclusion

Unlike Svelte v3, which is very much ready for production, Sapper is technically still in early development, and hasn't yet reached version 1.0. Still, it was a joy to use, and for something like a blog I think it's perfect. I'm already using Sapper in two other production web applications, as I feel Sapper is mature enough for my needs.

Further reading


React Hooks vs. Svelte

November 13rd, 2019

I get asked a lot on my Twitch channel to show a comparison of React and Svelte. I thought I'd record a short video to show everyone how to take a basic React example and rewrite it using Svelte:

Let's look at a basic example from the React Hooks documentation. Here we have a simple component with a button, and some text showing you how many times you've clicked the button. We're using some state in our component, to keep track of how many times the button was clicked. It's a nice, simple example of using a stateful component:

import React, { useState } from 'react';

function Example() {
    // Declare a new state variable, which we'll call "count"
    const [count, setCount] = useState(0);

    return (
        <div>
            <p>You clicked {count} times</p>
            <button onClick={() => setCount(count + 1)}>Click me</button>
        </div>
    );
}

What would this look like written with Svelte? Turns out, most of the code above is React boilerplate that we can do without. Let's start by commenting out all the boilerplate:

// import React, { useState } from 'react';

// function Example() {
// Declare a new state variable, which we'll call "count"
const [count, setCount] = useState(0);

// return (
<div>
    <p>You clicked {count} times</p>
    <button onClick={() => setCount(count + 1)}>Click me</button>
</div>;
// );
// }

That gets us some of the way, but that's still not valid Svelte. Svelte is an extension of HTML, so we need to put our JavaScript code in a <script> block, and change it to use a local state variable instead of React's useState function:

<script>
    // Declare a new state variable, which we'll call "count"
    let count = 0;
</script>

<div>
    <p>You clicked {count} times</p>
    <button onClick={() => setCount(count + 1)}>Click me</button>
</div>

This is very close to Svelte, but we have to change one more thing. We need to change React's onClick attribute to Svelte's on:click, and then change the click handler so it simply increments count:

<script>
    // Declare a new state variable, which we'll call "count"
    let count = 0;
</script>

<div>
    <p>You clicked {count} times</p>
    <button on:click={() => count++}>Click me</button>
</div>

All done! When you change React code into Svelte code, you spend most of your time deleting code, and deleting code feels amazing!

The major difference here is that your state is kept in local JavaScript variables instead of being tied up inside useState. This means you can set your state variable to a new value without calling a function, and that makes it possible to keep your component code very clean and succinct.

In fact, if you came up to me and said you had a new framework that was even simpler than Svelte, I'd have a hard time believing it! I mean, what could we remove from that Svelte component? Even Vanilla JavaScript would be way more complicated than this basic Svelte example. Svelte makes our web components as simple as possible, but no simpler.


How to use Node.js Streams (And how not to!)

October 30th, 2019

When I first started to understand Node.js streams, I thought they were pretty amazing. I love JavaScript Promises, but they only resolve to one result. Streams, however, can provide a constant stream of data, as you might expect!

Functional Reactive Programming is all the rage these days. Libraries like MobX, RxJS and Highland.js make it easy to structure your front-end application as data flowing in one direction down through a chain of pipes.

You can pipe a stream to another stream so that the output of the first becomes the input to the next. Sounds like a really neat way to structure an application, right?

I've already rewritten a lot of my JavaScript code to use Promises. Are streams the next step in the evolution? Is it time to rewrite all our applications to use Node streams? (Spoiler: NO!)

Unix pipes are the best

I love working with pipes in Linux (or Unix). It's really nice to be able to take a text file, pipe that into a command, pipe the output to another command, and pipe the output from that into a final text file.

Here's an example of using the power of pipes on the command line. It takes a text file with a list of words, sorts the list, counts how many times each word appears, then sorts the counts to show the top 5 words:

$ cat words.txt | sort | uniq -c | sort -nr | head -n5

It's not important for you to understand these commands, just understand that data is coming in to each command as "Standard Input" (or stdin), and the result is coming out as "Standard Output" (or stdout). The output of each command becomes the input to the next command. It's a chain of pipes.

So can we use Node.js in the middle of this chain of pipes? Of course we can! And Node streams are the best way to do that.

Going down the pipe

Node.js streams are a great way to be able to work with a massive set of data, more data than could possible fit into memory. You can read a line of data from stdin, process that data, then write it to stdout.

For example, how would we make a Node CLI application that capitalizes text? Seems simple enough. Let's start with an application that just takes stdin and pipes directly to stdout. This code does almost nothing (similar to the cat unix command):

process.stdin.pipe(process.stdout);

Now we can start using our Node.js application in the middle of our pipeline:

$ cat words.txt | node capitalize.js | sort | uniq -c | sort -nr | head -n5

Pretty simple, right? Well, we're not doing anything useful yet. So how do we capitalize each line before we output it?

npm to the rescue

Creating our own Node streams is a bit of a pain, so there are some good libraries on npm to make this a lot easier. (I used to heavily use a package called event-stream, until a hacker snuck some code into it to steal bitcoins!)

First, we'll use the split package, which is a stream that splits an input into lines, so that we can work with the data one line at a time. If we don't do this, we might end up with multiple lines, or partial lines, or even partial Unicode characters! It's a lot safer to use split and be sure we are working with a single, complete line of text each time.

We can also use a package called through which lets us easily create a stream to process data. We can receive data from an input stream, manipulate the data, and pipe it to an output stream.

const split = require('split');
const through = require('through');

process.stdin
    .pipe(split())
    .pipe(
        through(function(line) {
            this.emit('data', line.toUpperCase());
        })
    )
    .pipe(process.stdout);

There is a bug in the code above, because the newline characters are stripped out by split, and we never add them back in. No problem, we can create as many reusable streams as we want, to split our code up.

const through = require('through');
const split = require('split');

function capitalize() {
    return through(function(data) {
        this.emit('data', data.toUpperCase());
    });
}

function join() {
    return through(function(data) {
        this.emit('data', data + '\n');
    });
}

process.stdin
    .pipe(split())
    .pipe(capitalize())
    .pipe(join())
    .pipe(process.stdout);

Isn't that lovely? Well, I used to think so. There's something satisfying about having the main flow of your application expressed through a list of chained pipes. You can pretty easily imagine your data coming in from stdin, being split into lines, capitalized, joined back into lines, and streamed to stdout.

Down the pipe, into the sewer

For a few years, I was really swept up in the idea of using streams to structure my code. Borrowing from some Functional Reactive Programming concepts, it can seem elegant to have data flowing through your application, from input to output. But does it really simplify your code? Or is it just an illusion? Do we really benefit from having all our business logic tied up in stream boilerplate?

It's worse than it looks too. What if we emit an error in the middle of our pipeline? Can we just catch the error by adding an error listener to the bottom of the pipeline?

process.stdin
    .pipe(split())
    .pipe(capitalize())
    .pipe(join())
    .pipe(process.stdout)
    .on('error', e => console.error(e)); // this won't catch anything!

Nope! It won't work because errors don't propagate down the pipe. It's not anything like Promises where you can chain .then calls and throw a .catch at the end to catch all the errors inbetween. No, you have to add an error handler after each .pipe to be sure:

process.stdin
    .pipe(split())
    .pipe(capitalize())
    .on('error', e => console.error(e))
    .pipe(join())
    .on('error', e => console.error(e))
    .pipe(process.stdout);

Yikes! If you forget to do this, you could end up with an "Unhandled stream error in pipe." with no stack trace. Good luck trying to debug that in production!

Conclusions and recommendations

I used to love streams but I've had a change of heart recently. Now, my advice is to use data and error listeners instead of through streams, and write to the output instead of piping. Try to keep the number of streams to a minimum, ideally just an input stream and an output stream.

Here's a different way we can write the same example from above, but without all the hassle:

const split = require('split');
const input = process.stdin.pipe(split());
const output = process.stdout;

function capitalize(line) {
    return line.toUpperCase();
}

input.on('data', line => {
    output.write(capitalize(line));
    output.write('\n');
});

input.on('error', e => console.error(e));

Notice I'm still piping to the split library, because that's straightforward. But after that, I'm using a listener to the data event of the input to receive data. Then, I'm using write() to send the result to the stdout output.

And notice that my capitalize() function no longer has anything to do with streams. That means I can easily reuse it in other places where I don't want to use streams, and that's a really good thing!

I still think Node streams are interesting but they are not the future of JavaScript. If used carefully, you can make pretty powerful command-line tools with Node.js. Just be careful not to overdo it!


<< older posts newer posts >> All posts