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:

  1. import app from './app'
  2. const router = app.$router
  3. const meta = app.$meta() // here
  4. export default (context) => {
  5. router.push(context.url)
  6. context.meta = meta // and here
  7. return app
  8. }

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:

  1. app.get('*', (req, res) => {
  2. const context = { url: req.url }
  3. renderer.renderToString(context, (error, html) => {
  4. if (error) return res.send(error.stack)
  5. const {
  6. title, htmlAttrs, headAttrs, bodyAttrs, link,
  7. style, script, noscript, meta
  8. } = context.meta.inject()
  9. return res.send(`
  10. <!doctype html>
  11. <html ${htmlAttrs.text(true)}>
  12. <head ${headAttrs.text()}>
  13. ${meta.text()}
  14. ${title.text()}
  15. ${link.text()}
  16. ${style.text()}
  17. ${script.text()}
  18. ${noscript.text()}
  19. </head>
  20. <body ${bodyAttrs.text()}>
  21. <!-- prepended metaInfo properties -->
  22. ${style.text({ pbody: true })}
  23. ${script.text({ pbody: true })}
  24. ${noscript.text({ pbody: true })}
  25. <!-- app -->
  26. ${html}
  27. <!-- webpack assets -->
  28. <script src="/assets/vendor.bundle.js"></script>
  29. <script src="/assets/client.bundle.js"></script>
  30. <!-- appended metaInfo properties -->
  31. ${style.text({ body: true })}
  32. ${script.text({ body: true })}
  33. ${noscript.text({ body: true })}
  34. </body>
  35. </html>
  36. `)
  37. })
  38. })

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

  1. <head>
  2. {{{ meta.inject().title.text() }}}
  3. {{{ meta.inject().meta.text() }}}
  4. </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

  1. app.get('*', (req, res) => {
  2. const context = { url: req.url }
  3. const renderStream = renderer.renderToStream(context)
  4. renderStream.once('data', () => {
  5. const {
  6. title, htmlAttrs, headAttrs, bodyAttrs, link,
  7. style, script, noscript, meta
  8. } = context.meta.inject()
  9. res.write(`
  10. <!doctype html>
  11. <html data-vue-meta-server-rendered ${htmlAttrs.text()}>
  12. <head ${headAttrs.text()}>
  13. ${meta.text()}
  14. ${title.text()}
  15. ${link.text()}
  16. ${style.text()}
  17. ${script.text()}
  18. ${noscript.text()}
  19. </head>
  20. <body ${bodyAttrs.text()}>
  21. `)
  22. })
  23. renderStream.on('data', (chunk) => {
  24. res.write(chunk)
  25. })
  26. renderStream.on('end', () => {
  27. res.end(`
  28. <script src="/assets/vendor.bundle.js"></script>
  29. <script src="/assets/client.bundle.js"></script>
  30. ${script.text({ body: true })}
  31. </body>
  32. </html>
  33. `)
  34. })
  35. renderStream.on('error', (error) => {
  36. res.status(500).end(`<pre>${error.stack}</pre>`)
  37. })
  38. })