DEV Community

Bartosz Trzos
Bartosz Trzos

Posted on • Updated on

Yet another JAM stack blog

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 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, but first I need an MVP, therefore the blog.

The stack

I choose, Sapper, Tailwind, Let's delve a bit into each of them.

  • Sapper - It's Svelte universal app framework. After I watched Rich Harris talk 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 - 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!
  • - 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 reader 😅. Bonus points for Markdown editor and API. Feels like a good place for developers 🤓


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
Enter fullscreen mode Exit fullscreen mode

You can choose Rollup or Webpack. 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
Enter fullscreen mode Exit fullscreen mode

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 tutorial, and it works well. Just keep in mind that you can use

npx tailwind init
Enter fullscreen mode Exit fullscreen mode

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 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 API instead of the local mock.

<script context="module">
  export function preload({ params, query }) {
    return this.fetch(``)
      .then(r => r.json())
      .then(posts => {
        return { posts };
Enter fullscreen mode Exit fullscreen mode

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 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={}">
  <h2 class="text-black">{post.title}</h2>
Enter fullscreen mode Exit fullscreen mode

Now clicking the link will also pass the 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(`${}`);
    const post = await res.json();

    if (res.status === 200) {
      return { post };
    } else {
      this.error(res.status, post.message);
Enter fullscreen mode Exit fullscreen mode

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 API it's post.body_html instead of post.html from the boilerplate. And you have your blog with Svelte and 🎉 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.

Top comments (0)