Subscribe on changes!

Add support for prerendering (SSG)

avatar
Jun 1st 2021

What problem does this feature solve?

I'm building a Vue 3 app and prerendering a few routes for SEO purposes, as recommended in the SSR guide. The meta tags are rendered using <teleport> via vue-meta@3.

From what I understand, prerendering is essentially starting an Express server in my /dist dir and using puppeteer to visit each route and saving the DOM as /dist/about/index.html etc.

Here's an example output file:

<!DOCTYPE html>
<html lang="en">
<head>
    <title>About</title>
</head>
<body>
    <div id="app"><!--teleport start--><!--teleport end--></div>
</body>
</html>

Currently, this DOM causes hydration mismatch with the client vnode being Symbol(Fragment) and "server rendered DOM" being <!--teleport start-->.

const app = createSSRApp(AppLoader)
app.mount('#app', true)

When I check the generated output for a real Vue 3 SSR app, I see that it's filled with <!--[--><!--]--> comment nodes instead. As a result, I can only speculate that it's currently impossible to prerender Vue 3 apps without building an entire SSR app since @vue/server-renderer::renderToString is required to generate the correct DOM.

What does the proposed API look like?

When createSSRApp is called on the client side and have nothing to hydrate, assume it's being prerendered (or use an explicit flag on app.mount) and generate the appropriate SSR tags

Alternatively, I think the prerender section in the SSR docs should be removed since it's no longer applicable/possible in Vue 3.

avatar
Jun 1st 2021

You don't need any SSR if you do pre-rendering, use a regular SPA. It is different from Static Site Generation BTW, which you can take a look at https://github.com/antfu/vitesse


Please, next time consider using the forum, the Discord server or StackOverflow for questions first. But feel free to come back and open an issue if it turns out to be a bug šŸ™‚

avatar
Jun 1st 2021

Maybe this title should omit "SSG" if my terminology is mixed up?

My issue is that prerendering seems to be impossible in Vue 3 since the DOM generated by an SPA cannot be hydrated later due to hydration mismatch.

This does not actually hydrate:

const app = createApp(App)
app.mount('#app', true) // This will delete my entire DOM and reconstruct the page and cause an unpleasant white flash.

The only way I've been able to find to hydrate in Vue 3 is:

const app = createSSRApp(App)
app.mount('#app', true)

Also <div id="app" data-server-rendered="true"> does not seem to do anything in Vue 3

avatar
Jun 1st 2021

Prerendering as you describe it is not meant to be hydrated. It's meant to create purely static pages.

If you want to hydrate, you should render the pages with an actual SSR setup. That will produce DOM that can actually be hydrated.

Edit: i misread. It seems you actually run a full Vue SSR setup from which you save the rendered output?

That should be hydratable. You should probably indeed ask for help in the community as it why that fails

About your comment concerning the mention of Prerendering in the SSR docs: It explicitly states that preerendering is used to create a fully static page (no client side JS).

avatar
Jun 1st 2021

No I don't have any SSR right now

Prerendering as you describe it is not meant to be hydrated. It's meant to create purely static pages.

Thanks for confirming my suspicions. I will be rewriting my app into SSR to fix my issue


The process that I've described for "prerendering" is exactly what the prerender-spa-plugin does (this plugin was recommended in the official docs)

About your comment concerning the mention of Prerendering in the SSR docs: It explicitly states that preerendering is used to create a fully static page (no client side JS).

The docs only states The advantage is setting up prerendering is much simpler and allows you to keep your frontend as a fully static site.. I interpreted this as referring to static html js css (aka JAMstack) and can be hosted in any static host (S3) rather than a full node environment.

avatar
Jun 1st 2021

Well, as I said, I think that if you prerender in the sense of saving the output of a SSR-rendered app, it should be hydratable.

If you have troubles with that, find help in the community. If you think you found a bug, please open a bug issue with a reproduction.

avatar
Jul 7th 2021

@Trinovantes Hi! So, do you resolve your issue? Iā€™m migration from vue2 to vue3 and have the same problem with prerender spa plugin and hydration.

avatar
Jul 7th 2021

Vue3 cannot be prerendered

You need to either:

  • Rewrite to SSR
  • Delete all <script> tags in the postProcess step
  • Delete all contents of your <body> in the postProcess step
  • Just accept the FOUC
avatar
Jul 16th 2021

We are facing the same problem: migration from vue2 to vue3, we were using prerender strategy instead of SSR. Our code is currently not ready without a lot of effort for SSR, and with vue3 migration, hydration from a prerender is impossible

  • if you use createApp, mount does not take into account the second param, and erase all the DOM no matter what, leading to performance issue on our part
  • if you use createSSRApp, hydration fail because it's expecting to have special markup only generated by renderToString
avatar
Sep 6th 2021

I have a question in the process of using it, can I avoid hydrate and only render the renderToString output?

avatar
Feb 6th 2022

This is such an essential feature. Would've been amazing if it was provided by the framework. It's very odd that Vue 3 SSR documentation points to prerender-spa-plugin when it clearly doesn't work.

avatar
Jun 28th 2023

I think the crux of the problem is that Vue3 will not hydrate prerendered HTML

In Vue2 there was a kind of hack to add data-server-rendered="true" to the dom element that Vue mounts to in your prerendered HTML. Then Vue2 would hydrate that prerendered HTML.

Now in Vue3 there is no such option. So static sites that use Vue3 and prerender their HTML have a flash as Vue3 takes over and rerenders the entire dom.

So I think the feature request here is to have Vue3 hydrate prerendered HTML similar to Vue2