DEV Community

Reinier Kaper
Reinier Kaper

Posted on

Debugging Vue Hydration Errors in Production

Intro

We've all been there, Hydration completed but contains mismatches in production and you're just screaming at your monitor: "Yes but WHY!?". 😡

The Problem

It can be (very) challenging to figure out why hydration errors occur, but there's a little bit of potential light at the end of the hydration tunnel!

Vue doesn't tell you anything about where the hydration error occurred by default. This is for performance reasons that I'm not at all qualified for to even understand, but I'm sure people much smarter than me could pitch in on this. 🤓

The Solution

Let's get straight to the point: you can get some info about where the hydration error might've occurred with these fairly simple steps.

VUE_PROD_HYDRATION_MISMATCH_DETAILS

You can instruct Vite to give you a little more info, by setting the __VUE_PROD_HYDRATION_MISMATCH_DETAILS__ flag in Vite's define property. This will output a console.warn message with a lot of detail in dev mode, and a little bit of detail in production builds.

Now you can leverage that to send the warning to your favorite reporting service. 🙌

Custom console.warn implementation

We use DataDog RUM for real-time user experience tracking and they allow you to send manual errors to their platform. console.error is picked up automatically in our integration, but this is a warning, so we need to do a little bit of manual work to send it to DD RUM.

You can probably use this implementation, or one very similar, for you own reporting service.

Here's the gist of what we're doing:

// We inject the script directly. Don't ask...
function injectDD() {
  return `
  (function(h,o,u,n,d) {
    h=h[d]=h[d]||{q:[],onReady:function(c){h.q.push(c)}}
    d=o.createElement(u);d.async=1;d.src=n
    n=o.getElementsByTagName(u)[0];n.parentNode.insertBefore(d,n)
  })(window,document,'script','https://www.datadoghq-browser-agent.com/us1/v5/datadog-rum-slim.js','DD_RUM');

  // Store the original console.warn implementation
  const originalWarn = window.console.warn;

  // Let's build our own!
  window.console.warn = (...args) => {
    // Filter out only those `[Vue warn]` ones.
    if (args[0] && typeof args[0] === 'string' && args[0].startsWith('[Vue warn]')) {
      // Very minimal way of "parsing" the warning.
      args.map((arg) => {
        if (typeof arg === 'string') {
          return arg
        }
        if (typeof arg === 'object') {
          return '<object>'
        }
        return arg
      });

      // Send it to DD RUM as an error!
      window?.DD_RUM?.addError(new Error(args.join('')));
    }

    // And just call the original implementation as well.
    originalWarn(...args);
  };

  // Other DD RUM config stuff.
`
}
Enter fullscreen mode Exit fullscreen mode

And that's pretty much it! This way we end up with some useful info. 🎉

For example:

Error: [Vue warn]: Hydration text mismatch in[object HTMLParagraphElement]
  - rendered on server: " (8.7 miles away) "
  - expected on client: " (8,7 miles away) "
Enter fullscreen mode Exit fullscreen mode

Aha! Locale-based formatting is different on the server vs the client. 🤔

Or this one:

Error: [Vue warn]: Hydration text content mismatch on[object HTMLHeadingElement]
  - rendered on server: Unternehmen
  - expected on client: Company
Enter fullscreen mode Exit fullscreen mode

Looks like somehow the server is instructed to render things in German, but the client expects English. 💬

Conclusion

I hope this might help someone out, as it certainly will help us in figuring out the why that we all so desperately crave behind hydration errors. 👍

Top comments (0)