Introduction to Svelte SSR with code examples

SSR is a good feature to have if you care about SEO or if you desire to improve the initial page load experience for users.

I decided to find out how to implement SSR using Svelte. If you are interested in this topic as well and want to see my step by step SSR implementation with Svelte then this is the right article for you.

On your server, you would need to install the “svelte” npm package and use the “register” js file. On your front-end, use the “hydrate” property on your App component.

Now, let’s see how to translate this recipe into the code.

Set up the project

cd ~/dev && mkdir svelte-ssr

In this example, both server and front-end implementations are located in one project folder for convenience.

The code can be found in my Github repository here

A svelte app can be created using the next command:

npx degit sveltejs/template svelte-ssr

More information on getting started with Svelte can be found in the official Svelte’s blog post

degit – is an npm package that helps you to clone a git repository to a specific folder on your machine, it can be found as an npm package

After the command above is processed the project with the following file structure appears to be the result:

If you want you can run the following command and navigate to localhost:5000 in your browser to see how the app looks at this point.

npm run dev

Setup front-end to be SSR compatible

The resulting changes to implement SSR on front-end can be found below:

// src/main.js

import App from './App.svelte';

const app = new App({
	target: document.getElementById('svelte_app'),
	hydrate: true,
});

export default app;

Now, let’s break it down into a few steps with an explanation.

Here is the initial main.js file cloned from Svelte’s repository:

import App from './App.svelte';

const app = new App({
	target: document.getElementById('svelte_app'),
	name: 'World',
});

export default app;

The name property should be removed since we don’t need to render an App component.

Instead, we should somehow instruct the component to wait for HTML that will be generated by a server, find this HTML and only after that make a Svelte component from the raw markup that was received from the server.

According to Svelte documentation:

The hydrate option instructs Svelte to upgrade existing DOM (usually from server-side rendering) rather than creating new elements

That’s exactly what we need. Let’s imagine a possible HTML that can be served by a server.

It could be something like this:

<!DOCTYPE html>
<html lang="en">
<head>
  <script defer src='/static/build/bundle.js'></script>
</head>
<body>
 <div id="svelte_app">
   <h1>Hello World!</h1>
   <p>The paragraph</p>
 </div>
</body>
</html>
 <div id="svelte_app">
   <h1>Hello World!</h1>
   <p>The paragraph</p>
 </div>

As I understand the process, the slice of a raw HTML would later be found by the App component and Svelte would make a fully functional component from it without adding any new elements to the DOM.

More information about the hydrate property can be found in Svelte’s official documentation

Setup server to respond with the initial HTML

require('svelte/register')

const fs = require('fs')
const path = require('path')
const express = require('express')
const app = express()

const App = require('./src/App.svelte').default

const { html } = App.render({
   name: 'letconst'
})

app.use('/static', express.static(path.resolve(__dirname, 'public')))

app.get('/', (req, res) => {
    const indexFileContent = fs.readFileSync(path.resolve(__dirname, 'public', 'index.html'))

    res.send(indexFileContent.toString().replace('<div id="svelte_app"></div>', `<div id="svelte_app">${html}</div>`))
})

app.listen(3030, () => console.log('The server started at localhost:3000'))

You can import a Svelte component directly into Node using svelte/register.

Since App component needs to be rendered on the server, require('svelte/register') is the first thing needed to be capable of doing so.

A server-side component exposes a render method that can be called with optional props. It returns an object with headhtml, and css properties, where head contains the contents of any svelte:head elements encountered.

const App = require('./src/App.svelte').default

const { html } = App.render({
   name: 'letconst'
})

You can easily get the css and head properties from the component in the way I fetched html and then use it to append to the index.html template if needed.

You would need to add more replace statements. Or you could use a templating library like handlebars.js or pug for this purpose.

indexFileContent.toString()
  .replace('<div id="svelte_app"></div>', `<div id="svelte_app">${html}</div>`)
  .replace('<style type="my-own-svelte-stylesheet"></style>', `<style>${css}</style>`)
  .replace('{{headInnerContent}}', head)

The resulting html after the App.render({ name: 'letconst' }) call can be seen below:

<main>
	<h1>Hello letconst!</h1>
	<p>Visit the <a href="https://svelte.dev/tutorial">Svelte tutorial</a> to learn how to build Svelte apps.</p>
</main>

After all the steps described above in the article a GET request to localhost:3030/ will be followed by the response:

And the initial application UI:

Conclusion

In this article, I’ve described the way of how I implemented SSR using Svelte, what I’ve learned while creating it, and highlighted a few attendant topics related to the Svelte project set up.

One more thing before we finish.

Fun fact is if we would not provide a hydrate property to the App component on the front-end than we would see 2 same components rendered on the UI.

As we’ve seen before a component with the hydrate property doesn’t create any elements but searches for the existing DOM element and makes a Svelte component from it.

And without it, the component creates elements and put them into the existing markup generated by a server.

I’ve learned much while developing this SSR example project and I hope this article was helpful to you as it was to me. Was it?

Contact me at letconstportal@gmail.com and share your feedback. If you would like to see a particular topic highlighted on letconst, please, let me know about it.