Create a blog with Sapper & Markdown
Tags: svelte, javascript, showdev, webdev
Published: 2020-03-28 00:55:20

---
title: Create a blog with Sapper & Markdown
published: true
description: A step-by-step setup guide to configure & deploy a blog with Svelte.js
tags: svelte, javascript, showdev, webdev
series: Create a blog with Sapper & Markdown
---

[Sapper](https://sapper.svelte.dev) is a toolkit for creating [Svelte](https://svelte.dev) apps. It comes with a bunch of conventions baked in to help you get your project up and running quickly.

It can be deployed as a static site or as as a node.js server+SPA that does both server-side rendering and client-side rendering.

NOTE: You can find a screencast of this at the end of the article.

# Concept

We're going to store blog posts as markdown `.md` files inside repo. That way we can use `git` as the workflow for editing posts. That means Pull Requests for reviewing posts, `git log` to view history of changes, and forking/branching if we have multiple authors.

Since our data comes from static files (no databases), we don't need a server side component, we can deploy this using Sapper's static site generator.

# Setup

Start by scaffolding the app with `degit`.

You can use the official [`svelte/sapper-template`](https://github.com/svelte/sapper-template), but it includes a lot of demo code. I've going to use my fork [`joshnuss/sapper-template`](https://github.com/joshnuss/sapper-template) which is a blank slate without demo code.
 
```bash
npx degit joshnuss/sapper-template blog
cd blog
yarn install
```

# Posts data

Each post will be saved in the `posts` directory and include `yaml` metadata the top of the file (aka front matter).

Here's what a post `posts/example.md` would look like:

```markdown
---
title: Everything you wanted to know
summary: A short post about ...
date: 2020-04-01
---

- this
- is
- markdown
```

# Plugins

We could load these `.md` files using `fs.readFile()` at build time, but there is an even easier way, using `import` statements.

To configure `rollup` for `.md` imports, we'll use the plugin [`@jackfranklin/rollup-plugin-markdown`](https://www.npmjs.com/package/@jackfranklin/rollup-plugin-markdown).

That makes it possible to:

```javascript
import post1 from 'posts/example1.md'
import post2 from 'posts/example2.md'
// ...
```

Of course importing each post one-by-one will get tedious fast. 😅 

It would be easier to import a bunch of files at once based on a wildcard search pattern, like `posts/*.md`. The plugin [`rollup-plugin-glob`](https://www.npmjs.com/package/rollup-plugin-glob) does exactly this. 🚀 

Add the NPM packages:

```bash
yarn add -D @jackfranklin/rollup-plugin-markdown rollup-plugin-glob
```

Then, tell `rollup` to use these plugins. Update `rollup.config.js`:

```javascript
// import plugins
import markdown from '@jackfranklin/rollup-plugin-markdown'
import glob from 'rollup-plugin-glob'

// ....

// remember rollup is creating multiple builds
// make sure to add the new plugins to both the server *and* client builds
export {
  client: {
    plugins: [
      markdown(),
      glob(),
      ...
    ],
    ...
  },

  server: {
    plugins: [
      markdown(),
      glob(),
      ...
    ],
    ...
  }
}
```

# Reading Posts

Now that we can import `.md`, let's centralize the logic for accessing posts inside `src/posts.js`:

```javascript
import all from '../posts/*.md'

export const posts = all
```

If we `console.log(posts)`, the data for posts currently looks like this: 

```javascript
[
  {
    metadata: {title: 'the title', summary: '...', date: '2020-01-02'},
    html: '<h1>...</h1>',
    filename: 'example.md'
  }
]
```

Let's reshape it a bit, just to make it easier for our UI to use.

We're going to make these improvements:

- Put the `metadata` (`title`, `summary`, `date`) at the top level.
- Add a `permalink` field. It will be based on the `filename`
- Sort the list of posts by `date` in descending order (newest posts first) 

Makes these changes to `src/posts.js`:

```javascript
import _ from 'lodash'
import all from '../posts/*.md'

export const posts = _.chain(all) // begin a chain
                      .map(transform) // transform the shape of each post
                      .orderBy('date', 'desc') // sort by date descending
                      .value() // convert chain back to array

// function for reshaping each post
function transform({filename, html, metadata}) {
  // the permalink is the filename with the '.md' ending removed
  const permalink = filename.replace(/\.md$/, '')

  // convert date string into a proper `Date`
  const date = new Date(metadata.date)

  // return the new shape
  return {...metadata, filename, html, permalink, date}
}

// provide a way to find a post by permalink
export function findPost(permalink) {
  // use lodash to find by field name:
  return _.find(posts, {permalink})
}
```

# Index page

Now that we have our posts, we can move on to the UI.

Open the `src/routes/index.svelte` and display an `<article>` tag for each post:

```html
<script>
  // import the list of posts
  import {posts} from '../posts'
</script>

<h1>My Weblog</h1>

<!-- iterate through each post -->
{#each posts as post}
  <article>
    <!-- link article to /posts/$permalink -->   
    <a href={`/posts/${post.permalink}`}>
      <h2>{post.title}</h2>
      <p>{post.summary}</p>
    </a>
  </article>
{/each}
```

# Blog details page

The index page now shows summaries of each posts, to see the entire post add a page/route called `src/routes/posts/[permalink].svelte`.

Notice we're using square brackets around `[permalink]`? That tells sapper that the `permalink` is a dynamic parameter. Sapper will provide all parameters to our `preload()` function. 

```html
<script context="module">
  // import the logic for finding a post based on permalink
  import {findPost} from '../../posts'

  // sapper calls this to load our data
  export function preload(page) {
    // find the post based on the permalink param
    const post = findPost(page.params.permalink)

    // return a list of props
    return { post }
  }
</script>

<script>
  // this prop is filled from the result of the `preload()`
  export let post
</script>

<!-- display the post -->
<h1>{post.title}</h1>

{@html post.html}
```

# Deploy

To deploy our site we can generate the static site with `yarn export`.
You can also 🛳 it with `zeit` while you're at it: 

```bash
yarn export
now
```

That's it, all done! 💃

# Summary

Building static sites with Sapper takes very little effort.
There are many helpful rollup plugins that can convert static data into importable formats, that means we don't even have to write a parser for our data in many cases.

Another good thing about this approach is its' versatility. The same concept will work for project pages, wikis, news sites, books, landing pages, etc. Any data you can put in a `git` repo, can be the driver of a sapper site.

You can find example code here:
https://github.com/joshnuss/sample-blog

Happy coding! ✌

PS. This is part of my upcoming course on svelte: http://svelte.video

# Screencast

{% youtube 9dSrsDdHyHg %}