3 Ways to Define Templates in Vue

May 24, 2019

There are numerous ways to define templates in Vue, but many of them don't work so well in production. The docs even recommend avoiding some of them. Here's 3 ways to define templates for Vue components that work well with server-side rendering:

String Templates

You can define a template in vanilla JavaScript as a string literal or template literal. This approach means a component's HTML and JavaScript stay in the same file, which is generally considered a positive. The downside is that you don't get HTML syntax highlighting.

Vue.component('app', {
  template: '<h1>{{message}}</h1>',
  data: () => ({ message: 'Hello World' })
});

const app = new Vue({
  template: '<app></app>'
});

const data = await renderToString(app);
// <h1 data-server-rendered="true">Hello World</h1>
data;

You can use this approach but use a function to load the string. This splits HTML and JavaScript into separate files to get better syntax highlighting and separation of concerns.

Vue.component('app', {
  template: await load('app-template'),
  data: () => ({ message: 'Hello World' })
});

const app = new Vue({
  template: '<app></app>'
});

const data = await renderToString(app);
// <h1 data-server-rendered="true">Hello World</h1>
data;

async function load(template) {
  if (typeof window !== 'undefined') {
    return fetch(template + '.html').then(res => res.text());
  }

  const fs = require('fs');
  return new Promise((resolve, reject) => {
    fs.readFile(`${__dirname}/${template}.html`, 'utf8', (err, res) => {
      if (err != null) {
        return reject(err);
      }
      resolve(res);
    });
  });
}

Inline Templates

Another approach that is similar to string templates is inline templates. Instead of defining an HTML template for every component, you define all your component HTML in one top-level template. This is similar to slots.

Vue.component('app', {
  template: '<h1>{{message}}</h1>',
  data: () => ({ message: 'Hello World' })
});

const app = new Vue({
  template: '<app></app>'
});

const data = await renderToString(app);
// <h1 data-server-rendered="true">Hello World</h1>
data;

The advantage of this approach is you can define all your app's HTML in one template, but still break up business logic into separate components.

Single File Components

Single file components give you the best of both worlds between putting your HTML template in a separate file and putting your HTML template as a string literal. Like string literals, everything about a component is in one place. Like separate HTML files, you can get decent syntax highlighting.

The tradeoff is that single file components are typically declared in a separate .vue file with its own special syntax. Below is an example of .vue syntax. Depending on your IDE, you may need to install an additional package for .vue file syntax highlighting.

<template>
  <h1>{{message}}</h1>
</template>

<script>
  module.exports = {
    data: () => ({ message: 'Hello World' })
  };
</script>

So in order to compile your component, you need a build step. There are numerous tools that can do this for you, like the Vueify transform for Browserify and vue-loader for Webpack. Under the hood, both of these tools use vue-template-compiler. Here's an example of using vue-template-compiler directly to compile Vue syntax into a component:

const compiler = require('vue-template-compiler');
const parsed = compiler.parseComponent(`
  <template>
    <h1>{{message}}</h1>
  </template>
  <script>
    module.exports = {
      data: () => ({ message: 'Hello World' })
    };
  </script>
`);

// Contains `template`, `data` properties
const appComponent = Object.assign({ template: parsed.template.content },
  eval(parsed.script.content));
Vue.component('app', appComponent);
const app = new Vue({
  template: '<app></app>'
});

const data = await renderToString(app);
// <h1 data-server-rendered="true">Hello World</h1>
data;

More Vue Tutorials