Svelte Prerendering
Tags: webdev, svelte, snowpack, javascript
Published: 2020-06-02 14:24:11

---
title: Svelte Prerendering
published: true
description: Why and how to prerender a svelte App with snowpack and rollup by example 
tags: webdev,svelte,snowpack,js
cover_image: https://unsplash.com/photos/HtBlQdxfG9k/download?force=true&w=1920
---

## tldr (repo)
https://github.com/gobeli/svelte-prerender

## Why Prerendering?
Prerendering is a process in which an application (usually a SPA or otherwise statically generated site) is rendered as HTML during build time. The static HTML is then shipped to the user along with JavaScript to "hydrate" the HTML. Hydrate meaning attaching itself to the DOM elements which are already there.

This is done in production builds, mainly for SEO and performance purposes.

## Prerendering with Svelte
The [svelte compiler](https://svelte.dev/docs#svelte_compile) is able to output code which can be used for ssr (server side rendering), the same output is useful for prerendering. Instead of creating a web server and using the ssr output there, we can create a script to evaluate the ssr code to HTML during build-time. This script can look something like this:
```js
import { existsSync, promises as fs } from 'fs'
import { join } from 'path'

import App from '../src/app/App.svelte'

async function main() {
  const templatePath = join(process.cwd(), 'src', 'index.template')
  const publicPath = join(process.cwd(), 'public')

  const template = await fs.readFile(templatePath)
  const app = App.render()

  if (!existsSync(publicPath)) {
    await fs.mkdir(publicPath)
  }

  await fs.writeFile(
    join(publicPath, 'index.html'),
    template.toString().replace('%svelte.head%', app.head).replace('%svelte.html%', app.html)
  )
}

main()
```

A few things are still required for this to work: 
- A bundler to ensure we can import `.svelte` files in our node script
- A HTML template to render the App into

### Bundling the prerender-script
To bundle the prerender-script we are using rollup. We don't really need the output of the bundling process but would like to run the output immediately, that's why we are using the `@rollup/plugin-run` to execute the bundle. 
```js
import run from '@rollup/plugin-run'
import svelte from 'rollup-plugin-svelte'

export default {
  input: 'bin/prerender.js',
  output: {
    format: 'cjs',
    file: 'bin/dist/prerender.js',
  },
  plugins: [
    svelte({
      dev: false,
      generate: 'ssr',
      hydratable: true,
      css: function (css) {
        css.write('public/_dist_/main.css')
      },
    }),
    run(),
  ],
}
```

Notice, that we are using the svelte compiler option "generate" to ensure we generate ssr code. 

### HTML template
The HTML template is pretty simple, it provides a skeleton with placeholders for the prerendered app:

```html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>A Prerendered Svelte App</title>
    <link rel="stylesheet" href="/_dist_/main.css" />
    %svelte.head%
  </head>
  <body>
    <div id="app">
      %svelte.html%
    </div>
    <script type="module" src="/_dist_/client.js"></script>
  </body>
</html>
```
### Prerendered app
If we run the script a `index.html` file is created with the prerendered app:
```html
...
    <div id="app">
      <div class="svelte-tzjjzo"><h1>A Svelte App</h1></div>
    </div>
...
```

### Client side bundling 
In this example the client side bundling is done with snowpack/parcel. It is important to use the `hydratable` svelte compiler option for the build of the client code, since we want the client code to hydrate the HTML and not overwrite it. Furthermore we can safely disable the `css` option, since we are writing the css out during prerendering: 
```json
["@snowpack/plugin-svelte", { "hydratable": true, "css": false }]
``` 

*Photo by [Daniel McCullough](https://unsplash.com/@d_mccullough) on [Unsplash](https://unsplash.com/)*