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.
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
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
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 withhead
,html
, andcss
properties, wherehead
contains the contents of anysvelte: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:
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.