gridsome: doesn't actually change pages on build - Hydration issue

Description

<g-link> updates the page title (in the browser tabs) and loads things like the console log but it does not change what I see on the page without reloading. In gridsome develop this is not an issue but gridsome build it fails.

I’m assuming I might have invalid HTML somewhere, but perhaps it could be something else, any suggestions?

I’ve looked at a lot of other similar g-link issues like https://github.com/gridsome/gridsome/issues/337 but to no prevail.

Steps to reproduce

https://github.com/element-softworks/atobtyres

Expected result

<g-link> should hydrate the pages without having to reload the page manually

Actual result

The page does visually not change unless I reload the page, I do think the code is loaded, just the view doesn’t seem to change.

Environment


Libs:
- gridsome version: 0.6.6
- @gridsome/cli version: 0.2.0


Browser:
- [x] Chrome (desktop) version
- [x] Chrome (iOS) version
- [x] Safari (desktop) version
 
For Tooling issues:
- Node version: 10.13  
- Platform: Mac

About this issue

  • Original URL
  • State: open
  • Created 5 years ago
  • Reactions: 8
  • Comments: 58 (7 by maintainers)

Commits related to this issue

Most upvoted comments

I’ve added another minimalistic repository to reproduce the failure with valid routes.

You can see that once you change the HTML from

<div>
	This is: INDEX
</div>

to

<div>
	This is: <strong>INDEX</strong>
</div

the page loads successfully again. It looks like gridsome is trying to access child nodes of the page which do not exist (my assumption is based on the fact that I saw Uncaught (in promise) DOMException: Failed to execute 'appendChild' on 'Node': This node type does not support this method on my production site).

For both sample repos no error message is shown and they work flawlessly in develop mode.


Generally I’ve seen (and read about) hydration issues pop up around:

  • A component producing HTML and wrapping the component in a tag which is not allowed (e.g. <p><Component /></p> or <p><Component><p><template name="test"></p></Component></p> where the component wraps its slot around <p> already (nested paragraphs are not allowed))
    • Using v-html to dynamically parse HTML: <p v-html="var" /> where var = <p>Text</p> (nested paragraphs are not allowed)
  • Nested <g-link>
  • Using data-netlify="true" in forms
  • Using beforeMount
  • Using dynamically generated content
  • Using <g-link> as root component of a page
  • Using <g-link> without a trailing slash
  • Using HTML entities such as <div>&nbsp;</div> instead of <div class="..."></div>
  • Having a comment that contains a component’s markup - e.g. <!-- <Social /> --> as this will show up as <!----> and cause a appendChild() exception
  • <transition> tags generate <span> which may not have <div> as children - use <transition tag='div'>
  • Disable HTML minification e.g. in netlify settings
  • Make sure all routes via <g-link> lead to valid paths
  • ? Having no data() method or one returning nothing instead of return {}
  • ? Invalid code like having an object returning something instead of a method (e.g. metaInfo: {return{}} instead of metaInfo() {return{}})

Two options I think could be acceptable would be that it breaks during development in the same way it breaks during production build. Or it at least gives a console warning if there’s invalid HTML. I mean, ideally a developer knows how to avoid invalid HTML, but machines are way better than humans at that.

I am also facing this issue right now. Links works very well in all browsers on Windows, but fail to change in some browsers on Mac and iOS.

I had some headline tags (<h1>) which contained <div>s. I had to remove them to be HTML compliant and make it work on my iPad. But it keeps failing in Safari on Mac.

Like @LukeXF said, it is really hard to identify the cause of the problem(s?). Do you have any hint like verbose logs or something that could help us on this?

Hello!! I’ve had the same issue. I’ve read the whole thread and in my case, the issue was that I had a <g-link> inside another <g-link>

Here an example (PostList.vue component):

Initial code:

**<g-link :to="post.path">**
     <div class="card-style rounded overflow-hidden shadow-lg">
        <g-image v-if="post.img" class="w-full" :src="post.img" alt="Sunset in the mountains" />
        <div class="px-6 py-4">
          <div class="font-bold text-xl text-center" v-html="post.title"></div>
          <div class="text-center text-xs text-gray-600">
            <span v-html="post.date" />
            <b> {{post.timeToRead}} min lectura </b>
          </div>      
          <p class="text-gray-800 text-base pt-3 pb-3" v-html="post.description" />
          <p :to="post.path">Leer más...</p>
        </div>
      <div class="px-6 py-4">
        **<g-link** class="inline-block bg-gray-200 rounded-full px-3 py-1 text-xs font-semibold text-gray-700 mr-2" v-for="tag in post.tags" :key="tag.id" :to="tag.path">
          <span>#</span> {{ tag.title }}
        **</g-link>**
      </div>
    </div>
**</g-link>**

Solution:

<div class="card-style rounded overflow-hidden shadow-lg">
      **<g-link :to="post.path">**
        <g-image v-if="post.img" class="w-full" :src="post.img" alt="Sunset in the mountains" />
        <div class="px-6 py-4">
          <div class="font-bold text-xl text-center" v-html="post.title"></div>
          <div class="text-center text-xs text-gray-600">
            <span v-html="post.date" />
            <b> {{post.timeToRead}} min lectura </b>
          </div>      
          <p class="text-gray-800 text-base pt-3 pb-3" v-html="post.description" />
          <p :to="post.path">Leer más...</p>
        </div>
      **</g-link>**
      <div class="px-6 py-4">
        **<g-link** class="inline-block bg-gray-200 rounded-full px-3 py-1 text-xs font-semibold text-gray-700 mr-2" v-for="tag in post.tags" :key="tag.id" :to="tag.path">
          <span>#</span> {{ tag.title }}
        **</g-link>**
      </div>
    </div>

NOTE: I’ve added the ** symbol into the code to find the <g-link> tags easier.

I too am hitting a frustrating issue where routing and/or hydration is failing (silently) on build but not on development. Clicking on G-link links updates the URL, the meta info (the title in the browser tab changes, too), however the view is not updated.

Slowly through a process of trial and error and rebuilding the production site each time I’m working on narrowing it down to some sort of issue with one of my components, but it would be very helpful to have some sort of error in the console to indicate why this might be happening. Adding config.mode('development') to chainWebpack inside grime.config.js hasn’t changed anything in terms of helpful messages or notices.

@timchambers we also solved our issue by trial and error of rebuilding the site in production each time. It was very time consuming and caused massive frustration for all of our developers because there was no help/error to where the actual issue was.

I think the Gridsome team should have a check on if there are hydration issues, and perhaps provide more detailed information to what could be causing it. Gridsome: I understand that hydration issues may be hard to find/show errors for, but my team and I spent over 6 hours trying to debug this issue by slowly rebuilding the whole site.

Anyway, I hope that helps, Tim.

I came across this recently. I was nesting a g-link within a g-link. Removing that nesting fixed this for me. It’d be great if some warning could be displayed to let people know about this issue in their components.

For those having issues caused by Netlify forms, here’s how I use them.

Note that we need the html served to the client to match the html generated by Vue on hydration, otherwise it can be the cause of the issues.

During build (i.e. isServer == true) we need the attribute (data-netlify, also netlify-honeypot if you use that so use the same approach) to be specified so that Netlify properly recognises the form. During post build, Netlify will strip the attribute and also inject a hidden input containing the form name.

So we ensure that the data-netlify attribute exists during build, but is not present on client side rendering. We also make sure the hidden input will be present in the hydrated component.

<form
  id="contact-form"
  ref="form"
  name="contact-form"
  method="POST"
  :data-netlify="$isServer ? true : false"
>
  <input type="hidden" name="form-name" value="contact-form" />
  <!-- Include your honeypot field, actual form fields, etc. after the hidden form name field. -->
</form>

If you use a PWA and have issues with the post url being cached, either manage the cacheing on this in your PWA config or this quick workaround on the component can also ensure the form submits by enforcing a unique url:

mounted() {
    this.$refs.form.action += `?t=${Date.now()}`
  },

Has anyone found any good debugging tools for this? Been trying to get this bug fixed for a few hours, confirmed everything is valid html in W3, checked all g-links to make sure nothing was nested and inspected html to make sure nothing was invalid. Also tried to add the webpack dev config to gridsome.config but that did nothing (haven’t seen in any of the many thread of this issue where it has done something tho). Had some v-for’s running on a g-link, it was working properly but read that could have some interference so I removed but no luck 🥲 here is the production link.

Symptoms point at hydration issue, just can’t seem to pinpoint, just running out of boxes to check on this one

I see a form, did you use netlify form by any change? If so, that’s the issue. No workaround yet.

Thanks for the repo, @csabaxyz. I’m beginning to suspect that the unresponsive g-links are not the problem, but merely the symptom of a variety of errors which make the hydration crash. Maybe the most important thing to fix here is the debuggability (is that a word?) of the hydration.

I was dealing with the same issue 3 days ago till today. After reading the comments i decided to forget my assumptions, relax and debug my website. And I ended descovering that the issue was being caused by netlify’s form attribute that I added on other day. I removed it and my site is now working.

Know I have to found out how to properly integrate my website with netlify. I hope this saves someone hours.

I wanted to give everyone an update to this issue.

We managed to fix the issue on the 24th December:

  • The issue was with our Services.vue file
  • We had to switch mounted() to computed() to set title and text
  • Perhaps updating this.text and this.title onmounted() broken it

Not really sure how we fixed it but the below commit fixed the hydration issue. https://github.com/element-softworks/atobtyres/compare/6afde94cbe00...b8cf43151638

I will leave this open for the Gridsome team to explain what I was doing wrong, or confirm whether or not an actual bug exists.

Side note: We have since started building in Gatsby (React) because we kept running into obscure issues with Gridsome (like this issue).