yaquawa
yaquawa

Reputation: 7338

How to make `teleport` work in SSR in vue3?

It seems like by default teleport won't get rendered by using renderToString.

Does any one know how to render and hydrate the teleport component in Vue3?

I've found some tests here, but couldn't figure out how to apply this on a real world example, and couldn't find any information on this topic.

Upvotes: 1

Views: 2403

Answers (2)

Deniz
Deniz

Reputation: 21

As far as I know, teleport in Vue 3 SSR needs special handling: https://vuejs.org/guide/scaling-up/ssr.html#teleports

You can check Evan's answer here:https://github.com/vuejs/core/issues/3869

To hydrate the teleport content, you need to use teleports property of the SSR context. Then, you need to inject them in the final html. console.log(context) to see teleports object.

This example may help to solve your problem:

App.vue:

  <div class="container">
    <teleport to="#teleports">
      <toast-notification></toast-notification>
    </teleport>
  </div>

server.js:

  <!DOCTYPE html>
        <html lang="en">
          <head>
          </head>
          <body>
            <div id="app">${page}</div>
            <div id="teleports">${Object.values(context.teleports)</div>
            </body>
        </html>

Upvotes: 0

AidOnline01
AidOnline01

Reputation: 737

Well you can load teleport in ssr, but I am not sure you can hydrate it. Consider this example

html.js
   <html>
   <head>${context.teleports.head}</head>
   <body></body>
   </html>

App.vue
   <template>
      <teleport to="head">
        <title>Hello world</title>
        <meta name="viewport" content="width=device-width">
      </teleport>
      <div id="#app">This is app</app>
   </template>

Output (html)
   <html>
   <head>
     <title>Hello world</title>
     <meta name="viewport" content="width=device-width">
   </head>
   <body></body>
   </html>

Here we are rendering our teleport, so we can set some data even without js. But we cannot hydrate it, as it has nothing to connect to. Instead teleport will be appended another time, and we will get duplicate teleport content.

So all I could do with it, is using this workaround. Before App.vue mounted, we remove old teleport, so new one can replace it. It's not hydration, so it is slower for big components, but it is working well.

App.vue
  <script>
    export default {
      name: 'App.vue',
      created() {
        if(window !== undefined) document.head.innerHTML = '';
      }
    }
  </script>

Upvotes: 0

Related Questions