Yet another JAM stack blog

remotelydev profile image Bartosz Trzos Updated on ・4 min read

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 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 - 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!
  • 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 πŸ€“


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 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

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

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 };

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>

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);

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.

Posted on by:

remotelydev profile

Bartosz Trzos


I solve problems.. with JavaScript. Remote Frontend developer from Poland makeing web safer and more accessible bit by bit.


markdown guide