Coding with Jesse

Sapper is dead! What's next in Svelte?

October 28th, 2020

In case you missed it, Rich Harris gave a presentation at Svelte Summit 2020, where he announced that Sapper v1 will never be released! Instead, he showed what's coming next in Svelte itself.

Be aware that at the time of me writing this blog post, none of this is officially released yet, and very likely will change in the near future. Nonetheless, it's exciting to see a sneak preview of what the future of Svelte will look like.

Getting started

To get started today, you can run this command in your terminal, assuming you have npm installed: npm init [email protected]

In the future, it'll probably just be npm init svelte, which is super clean and easy to remember. This will be a nice change from having to run npx degit svelte/template my-template.

Here's what you'll see if you run this command today:

█████████  ███████████    ███████    ███████████  ███
███░░░░░███░█░░░███░░░█  ███░░░░░███ ░░███░░░░░███░███
░███    ░░░ ░   ░███  ░  ███     ░░███ ░███    ░███░███
░░█████████     ░███    ░███      ░███ ░██████████ ░███
░░░░░░░░███    ░███    ░███      ░███ ░███░░░░░░  ░███
███    ░███    ░███    ░░███     ███  ░███        ░░░
░░█████████     █████    ░░░███████░   █████        ███
░░░░░░░░░     ░░░░░       ░░░░░░░    ░░░░░        ░░░

Pump the brakes! A little disclaimer...

[email protected] is not ready for use yet. It definitely can't
run your apps, and it might not run at all.

We haven't yet started accepting community contributions,
and we don't need people to start raising issues yet.

Given these warnings, please feel free to experiment, but
you're on your own for now. We'll have something to show
soon.

It'll go on to ask you if you want to use TypeScript, which is really nice for those who like to use TypeScript, and nice that it's optional for those who don't.

Here's the full directory structure you will get with an initial installation:

├── .gitignore
├── package.json
├── README.md
├── snowpack.config.js
├── src
│   ├── app.html
│   ├── components
│   │   └── Counter.svelte
│   └── routes
│       └── index.svelte
├── static
│   ├── favicon.ico
│   └── robots.txt
└── svelte.config.js

Starting the dev server

Once it's done setting up files, you need to run npm install and then npm run dev to spin up the dev server. Here's what you'll see:

snowpack

  http://localhost:3001 • http://10.0.0.180:3001
  Server started in 643ms.

▼ Console

[snowpack] installing dependencies...
[snowpack] ✔ install complete! [0.59s]
[snowpack] 
  ⦿ web_modules/                                size       gzip       brotli   
    ├─ svelte-hmr/runtime/hot-api-esm.js        22.08 KB   7.4 KB     6.29 KB    
    ├─ svelte-hmr/runtime/proxy-adapter-dom.js  5.17 KB    1.65 KB    1.38 KB    
    ├─ svelte.js                                0.18 KB    0.15 KB    0.11 KB    
    ├─ svelte/internal.js                       52.36 KB   13.16 KB   11.36 KB   
    └─ svelte/store.js                          3.3 KB     1 KB       0.88 KB    


[snowpack] > Listening on http://localhost:3000

What is happening under the hood? This is very different from the Svelte and Sapper templates that came before. There is no longer a rollup.config.js nor a webpack.config.js, because it does not use Rollup nor Webpack, at least not during development.

Instead, it uses Snowpack to handle compiling and serving client-side resources. Snowpack does not bundle your resources, and relies heavily on JavaScript’s native module system, which means development is much faster. There is even a snowpack.config.js file which gives you a place to configure Snowpack to some degree:

// Consult https://www.snowpack.dev to learn about these options
module.exports = {
    extends: '@sveltejs/snowpack-config'
};

Building your application

There is now also a new svelte.config.js file, which lets you define an "adapter", used with npm run build to build your application into a production web site:

module.exports = {
    // By default, `npm run build` will create a standard Node app.
    // You can create optimized builds for different platforms by
    // specifying a different adapter
    adapter: '@sveltejs/adapter-node'
};

The default adapter will use Rollup to build your site into a Node.js web server. It seems that this web server doesn't use Express.js, though that might change as well, or maybe there will be a special adapter for Express.

If you want to have a purely static export, you can currently replace @sveltejs/adapter-node with @sveltejs/adapter-static, but be sure to run npm install @sveltejs/adapter-static as well.

In the future, there will be many other adapters, for example, building specifically for certain web hosting platforms, serverless architectures, and who knows what else? The cool thing about this adapter approach, is that you can build your web site without necessarily knowing how it will be built or deployed. You'll be able to change the adapter without changing your code.

Dependencies

Let's have a look at the package.json:

{
    "name": "demo",
    "version": "0.0.1",
    "scripts": {
        "dev": "svelte dev",
        "build": "svelte build"
    },
    "devDependencies": {
        "@sveltejs/adapter-node": "0.0.12",
        "@sveltejs/kit": "0.0.23",
        "@sveltejs/snowpack-config": "0.0.4",
        "svelte": "^3.29.0"
    }
}

Note that there are very few dependencies here. I really like how minimal this is. Both of the scripts are using the new svelte CLI from @sveltejs/kit, though that name might change, and it's not even available on GitHub yet. For now, you can look at the npm package.

Routes

You'll notice a folder src/routes/ where you can define your routes similar to how Sapper (or Next.js, etc.) let you define routes. Basically, your folder and file structure in here will map one-to-one with the routes on your web site. This is really nice, and easy to work with, especially if you're used to using PHP or other similar web development platforms.

If you're not building a static-only web site, you can also define server-side routes, similar to what you can do with Sapper. For example, you can create a file at src/routes/api.js:

export async function get(req) {
    return {
        status: 200,
        body: {
            hello: 'world'
        }
    }
}

If you're familiar with Sapper, you might notice that you have to return an object with status and body properties, instead of using an Express res object for your response. This is because it is not Express middleware. It uses an internal Node web server, with an API similar to what you may have used with some serverless cloud functions.

To create a layout component, to provide a consistent header and footer wrapped around all your routes, you can create a file called $layout.svelte, similar to Sapper's _layout.svelte.

You can also make an error handler route called $error.svelte, to handle 404s and other programming errors. It receives a status prop and also an error prop, so you can decide how to display the error to your users.

Migrating

Rich Harris notes that migrating from Sapper or other similar frameworks should be fairly straightforward, since most of the folder structure and other concepts are pretty similar. You'll probably just need to rename some files, and change how your server-side routes work, because they will no longer be written as Express middleware.

For fetching data for both server-side and client-side rendering, the Sapper approach of having a <script context="module"> block currently still works, though it's possible that will change.

Conclusion

If you're excited about all this stuff, it's definitely too early to start building your applications using it, but I'm willing to bet that it'll be a good choice to get started by using Sapper today, with the expectation that it'll be easy enough to migrate to this in the future, once it's ready.

To see a demo, check out Rich Harris' video Futuristic Web Development

If you're interested in learning more about Svelte, check out my video course The Joy of Svelte.

Finding time for side projects

August 1st, 2020

In many ways, time has been going very slowly amidst this pandemic, as we've all waited for case numbers to come down, lockdowns to lift, and vaccines to be tested. In other ways, time has been flying by. It's hard to believe I bought joyofsvelte.com almost seven months ago, and my initial Spring 2020 launch date is already months behind us.

I've never launched a product on my own before. I didn't realise how different it would be recording videos for a paid video course compared to making free videos for YouTube. If I'm going to expect people to pay, these videos have to be absolutely perfect. That means when I record them, I need to have a large block of uninterrupted time, completely free of distractions, and I need to be well-rested, in a good mood, full of joyful energy and inspiration.

Well, I have a four-year-old at home. I absolutely love being with my son all day, but there's definitely not much stillness around here, and any extra energy is either spent on getting some freelance work done, cleaning up the house, or trying to fit in a grownup movie or game before bed.

Last weekend, my wife took my son to a socially distanced birthday party, which marked the first time I had the house to myself in four months. I got to sleep in too, so I was well-rested, in a quiet house, full of joyful energy and inspiration. It was time to finally record. And I did! I recorded two more videos. Well, one and a half before they came back home. But I had momentum, so while we let my son quietly play Mario Kart, I went and finished the second video!

It's hard to find time for side projects. It's even harder when you have such high standards for that time. They say that perfect is the enemy of good, and I'm coming to realise that something like a video course can never be perfect anyway. I can only do my best with the time and energy I have available. I'd rather finish this course and share my experience and insights on using Svelte with the world, than to plan it forever and never launch.

If you keep calling it a side project, it'll keep getting pushed to the side.

I wrote that tweet back in May and obviously I've failed to take my own advice. But I'm not giving up. I'm going to keep at it and keep trying to find the time. We're probably going to keep my son home from school this fall, so it doesn't look like I'll have a quiet house for a long while. I'm going to have to get creative and find a way to make it work.

I hope you're able to find some time for your own side projects too, and know that you're not alone in that struggle. We usually only see people launching projects once they're already done. I'm sure there are countless more unfinished and unlaunched side projects that the world will never know about. Don't let your side project become one of them.

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

<< older posts newer posts >> All posts