DEV Community

Reason Digital.com
Reason Digital.com

Posted on • Updated on

Server Side Rendering With Vue JS

Rendering engines

NodeJS has a variety of rendering engines like Handlebars and Nunjucks, but using Vue to render templates has a couple of advantages:

  • Only need to learn one template system
  • Can hydrate in the browser
  • Has an intuitive and expandable data store

Server side rendering VueJS

Let's render some VueJS on the server:

const renderer = require("vue-server-renderer").createRenderer();
const Vue = require("vue");

async function render(template, data){
    var app = new Vue({
      template,
      data,
    });
    return await renderer.renderToString(app);
}
Enter fullscreen mode Exit fullscreen mode

This is a simple function that takes a VueJS template such as:

<div>{{title}}</div>
Enter fullscreen mode Exit fullscreen mode

And some data:

{
   title : "My page title"
}
Enter fullscreen mode Exit fullscreen mode

And renders it into static HTML.

<div data-server-rendered="true">My page title</div>
Enter fullscreen mode Exit fullscreen mode

Hydration

The data used in this example is static so no hydration is needed on the client. If the element needed hydration you can do it with the VueJS library:

<div id="app-id">...SSR VUE OUTPUT HTML</div>

var app = new Vue({
  template,
  data,
});
app.$mount('app-id', true);
Enter fullscreen mode Exit fullscreen mode

The final argument to $mount forces VueJS to hydrate the content of app-id (when you server side render VueJS it adds a data-server-rendered="true" attribute to the wrapper so no forcing is needed in that case).

Breaking hydration down

Imagine you have three VueJS components in the template below:

<page-title></page-title>
<current-time></current-time>
<current-weather location=""></current-weather>
Enter fullscreen mode Exit fullscreen mode

There are three components:

The first will only need to change when the page title changes so SSR will work fine (but will need re-rendering when it changes).

The second will not lend itself well to SSR as it will render the current time when it was rendered - this component needs hydration.

The third component cannot be SSR as it will depend on the location of the user and will need hydrating all the time.

Too much VueJS

The first component does not need to be hydrated, and the third always needs hydration, but the second is more interesting.

Imagine you had only the <current-time> on a page. Do you really want to load in the entire VueJS library and boot up the VDOM just to render the time which can be done in a few lines of Vanilla JS?

How do you solve the problem of simple VueJS components that need hydration but don't need the full power of VueJS?

Enter custom elements

For components that do not need the power and reactivity of VueJS we can use the custom elements API. This requires no libraries to be imported as it is done in the browser:

customElements.define('current-time', class extends HTMLElement {
    constructor(){
        super();
    }
    connectedCallback(){
        setInterval(() => {
            this.innerHTML = (new Date()) + '';
        },1000);
    }
});
Enter fullscreen mode Exit fullscreen mode

This JS allows us to use a new HTML element we have created called <current-time> anywhere in our HTML body and the browser will upgrade the tag when it has parsed the JS.

(NB: We have not used the ShadowDOM in this example so we can mimic the CSS injection you would get with a VueJS component)

Take aways

  • VueJS has powerful templating and reactivity that is useful for SSR pages and also has built in hydration.

  • When using simple components the custom elements API can be used alongside VueJS to reduce the amount of JS needed.

  • We can't use custom elements all the time as they do not have templating built in like VueJS does (but LitHTML does).

  • We can combine the templating of VueJS with custom elements to have VueJS manipulate them.

  • We don't need to load the whole VueJS library every time when we only need simple hydration!

(NB: this article uses VueJS 2)

Discussion (0)