Yet another JAM stack blog
Tags: helloworld, sapper, tailwindcss, blog
Published: 2020-01-02 17:52:30

*This is my first post 🎉 please leave a comment on it, and my site and don't be gentle. Cheers!*
## The purpose
I needed a site where I could publish some posts to [learn in public](https://www.swyx.io/writing/learn-in-public/) and keep my knowledge in one place. I often find myself googling stuff I used to develop something while I have to do something similar. I want to write about my process and keep it on [trzos.dev](https://trzos.dev/), but first I need an MVP, therefore the blog.
## The stack
I choose, Sapper, Tailwind, Dev.to. Let's delve a bit into each of them.
* **[Sapper](https://sapper.svelte.dev/)** - It's Svelte universal app framework. After I watched [Rich Harris talk](https://www.youtube.com/watch?v=gJ2P6hGwcgo) I thought I'd love to work with it. The concept is great, it offers amazing (and fun) developer experience, and I love to experiment with something new. I chose Sapper over Svelte for better SEO and static site generation.
* **[Tailwind](https://tailwindcss.com/)** - It's a utility first CSS framework from Adam Wathan. It looks a bit ugly in the code at first, but it really makes it easier to maintain and it's just amazing how well it works with component-based frameworks. Utilities for animations and transitions coming soon as well. Can't wait!
* **[Dev.to](https://dev.to/)** - I choose this to have a platform to write on, and I like their policies. I guess you already know that they are better than others though, as you're probably a dev.to reader 😅. Bonus points for Markdown editor and API. Feels like a good place for developers 🤓

## Setup
First, we need to create an app with Sapper. Fortunately someone prepared a script for us that generates it automatically. Just go to your projects folder and run one of below.
```
# for Rollup
npx degit "sveltejs/sapper-template#rollup" my-app
# for webpack
npx degit "sveltejs/sapper-template#webpack" my-app
```
You can choose [Rollup](https://rollupjs.org/guide/en/) or [Webpack](https://webpack.js.org/). I choose Webpack because I'm more familiar with it, but it's totally up to you. They are both good module bundlers.

Next, we need to run the project.
```
cd my-app

npm install
npm run dev & open http://localhost:3000
```
You're good if you can see Borat on the main page. I told you Svelte is fun 😄 

Next, we would like to add Tailwind. I used [this](https://github.com/tailwindcss/setup-examples/tree/master/examples/sapper) tutorial, and it works well. Just keep in mind that you can use 
```
npx tailwind init
```
instead of the command that runs a script from `/node_modules/`. Does the same thing, it's just easier to type in. PostCSS config file in this example looks for `tailwind.js` instead of `tailwind.config.js`. Watch out and fix one or the other.
I went with PurgeCSS and I recommend you to do so as well. It saves some kB on the app as it shaves off unused classes. That's brilliant with Tailwind as you can have all generated utility classes and drop unused ones on production 💪🏻
You will know that Tailwind is installed first by the fact that Borat image went left for some reason, and that you can actually use Tailwind classes. I'd recommend to remove the `global.css` file from `/static` folder and from `template.html`. You probably won't need this when you use Tailwind.

## The blog

Sapper boilerplate comes with a blog example. In the `routes` folder, you will find the `blog` directory, which represents your `/blog` route. I removed all `.js` files there as their purpose is to mock API calls for posts and we will use `dev.to` API. You should be left with `blog/index.svelte` and `blog/[slug].svelte`, which are for the blog feed and each blog post accordingly.
Let's focus on `index.svelte`. First we need to call `dev.to` API instead of the local mock.

```
<script context="module">
  export function preload({ params, query }) {
    return this.fetch(`https://dev.to/api/articles/?username=remotelydev`)
      .then(r => r.json())
      .then(posts => {
        return { posts };
      });
  }
</script>
```
Replacing first script tag with one above should do the trick. Notice that I target my publushed posts. Make sure to use you username.

Now on the blog page, you should see a list of my/your posts __published__ on `dev.to`. The link is broken though, let's fix that!

Still in `blog/index.svelte` update the link in the list to pass id in the query like this
```
<a rel="prefetch" href="blog/{post.slug}?id={post.id}">
  <h2 class="text-black">{post.title}</h2>
</a>
```
Now clicking the link will also pass the `post.id` which is necessary to fetch the exact post. It's not the best solution, but it was first and working one I could think of. Leave a comment with a better idea, and I'll update the post. 👌🏻

Let's go to `blog/[slug].svelte` and update the first script tag with
```
<script context="module">
  export async function preload({ params, query }) {
    // the `slug` parameter is available because
    // this file is called [slug].svelte
    // const res = await this.fetch(`blog/${params.slug}.json`);
    const res = await this.fetch(`https://dev.to/api/articles/${query.id}`);
    const post = await res.json();

    if (res.status === 200) {
      return { post };
    } else {
      this.error(res.status, post.message);
    }
  }
</script>
```
See how I used the post `id` to get proper post? It's needed to get exact your post. Sapper is cool enough to make the call on server side.

All left to do is to use proper content property from the response. For `dev.to` API it's `post.body_html` instead of `post.html` from the boilerplate. And you have your blog with Svelte and Dev.to 🎉 Just add some personal info, styles and write your posts! Cheers! 👋🏻

P.S.: I use Sapper generate functionality. It walks through all your routes and links and generates static site from it. Pretty neat! I like this particularly because you can have a static site if you don't need to keep users logged in, but if you'll ever decide that you want to you can serve the app as usual.