Server Side Rendering

If you have an isomorphic/universal web application, you'll likely want to render your metadata on the server side as well. Here's how.

Add vue-meta to the context

You'll need to expose the results of the $meta method that vue-meta adds to the Vue instance to the bundle render context before you can begin injecting your metadata. You'll need to do this in your server entry file:

server-entry.js:

import app from './app'

const router = app.$router
const meta = app.$meta() // here

export default (context) => {
  router.push(context.url)
  context.meta = meta // and here
  return app
}

Inject metadata into page string

Probably the easiest method to wrap your head around is if your Vue server markup is rendered out as a string using renderToString:

server.js:

app.get('*', (req, res) => {
  const context = { url: req.url }
  renderer.renderToString(context, (error, html) => {
    if (error) return res.send(error.stack)
    const {
      title, htmlAttrs, headAttrs, bodyAttrs, link,
      style, script, noscript, meta
    } = context.meta.inject()
    return res.send(`
      <!doctype html>
      <html ${htmlAttrs.text(true)}>
        <head ${headAttrs.text()}>
          ${meta.text()}
          ${title.text()}
          ${link.text()}
          ${style.text()}
          ${script.text()}
          ${noscript.text()}
        </head>
        <body ${bodyAttrs.text()}>
          <!-- prepended metaInfo properties -->
          ${style.text({ pbody: true })}
          ${script.text({ pbody: true })}
          ${noscript.text({ pbody: true })}

          <!-- app -->
          ${html}

          <!-- webpack assets -->
          <script src="/assets/vendor.bundle.js"></script>
          <script src="/assets/client.bundle.js"></script>

          <!-- appended metaInfo properties -->
          ${style.text({ body: true })}
          ${script.text({ body: true })}
          ${noscript.text({ body: true })}
        </body>
      </html>
    `)
  })
})

If you are using a separate template file, edit your head tag with

<head>
  {{{ meta.inject().title.text() }}}
  {{{ meta.inject().meta.text() }}}
</head>

Notice the use of {{{ to avoid double escaping. Be extremely cautious when you use {{{ with __dangerouslyDisableSanitizersByTagID.

Inject metadata into page stream

A little more complex, but well worth it, is to instead stream your response. vue-meta supports streaming with no effort (on it's part 😜) thanks to Vue's clever bundleRenderer context injection:

server.js

app.get('*', (req, res) => {
  const context = { url: req.url }
  const renderStream = renderer.renderToStream(context)
  renderStream.once('data', () => {
    const {
      title, htmlAttrs, headAttrs, bodyAttrs, link,
      style, script, noscript, meta
    } = context.meta.inject()
    res.write(`
      <!doctype html>
      <html data-vue-meta-server-rendered ${htmlAttrs.text()}>
        <head ${headAttrs.text()}>
          ${meta.text()}
          ${title.text()}
          ${link.text()}
          ${style.text()}
          ${script.text()}
          ${noscript.text()}
        </head>
        <body ${bodyAttrs.text()}>
    `)
  })
  renderStream.on('data', (chunk) => {
    res.write(chunk)
  })
  renderStream.on('end', () => {
    res.end(`
          <script src="/assets/vendor.bundle.js"></script>
          <script src="/assets/client.bundle.js"></script>
          ${script.text({ body: true })}
        </body>
      </html>
    `)
  })
  renderStream.on('error', (error) => {
    res.status(500).end(`<pre>${error.stack}</pre>`)
  })
})