Gatsby: HTML different between build and develop - develop works ok - strange workaround

Created on 9 Mar 2020  Â·  6Comments  Â·  Source: gatsbyjs/gatsby

Please excuse if this is a duplicate, I tried to search over open issues, but didn't immediately see one that matched.

I am seeing a difference between HTML develop and build output. The bad repo creates the correct HTML in develop but bad HTML when in build. The good repo implements a workaround discovered by @stefantrinh1 and documented here- https://github.com/gatsbyjs/gatsby/issues/8560#issuecomment-592666085
the 'good' repo seems to creates the correct HTML in both build and develop

bad repo / depolyment:
https://github.com/funkfinger/blog/tree/bad
https://bad.jaywiggins.com

good repo / deployment:
https://github.com/funkfinger/blog/tree/good
https://good.jaywiggins.com

here is a diff between the two repos:

git diff good..bad
diff --git a/src/components/MdxPostTemplate/MdxPostTemplate.jsx b/src/components/MdxPostTemplate/MdxPostTemplate.jsx
index 83ca274..c32ae54 100644
--- a/src/components/MdxPostTemplate/MdxPostTemplate.jsx
+++ b/src/components/MdxPostTemplate/MdxPostTemplate.jsx
@@ -16,14 +16,14 @@ export const PageTemplatePure = ({
     <HeroImage img={heroImage.childImageSharp.fluid} />
   ) : null;
   return (
-    <article className={`single-post${obsolete ? ' obsolete' : ''}`}>
+    <div className={`single-post${obsolete ? ' obsolete' : ''}`}>
       {img}
       <div className="single-post-body">
         <h1>{title}</h1>
         <div className="post-date">{date}</div>
         <MDXRenderer>{body}</MDXRenderer>
       </div>
-    </article>
+    </div>
   );
 };

diff --git a/src/components/PostExcerpt/PostExcerpt.jsx b/src/components/PostExcerpt/PostExcerpt.jsx
index 85cb0d5..939be27 100644
--- a/src/components/PostExcerpt/PostExcerpt.jsx
+++ b/src/components/PostExcerpt/PostExcerpt.jsx
@@ -10,7 +10,7 @@ const PostExcerpt = ({ title, children, img, slug, first, obsolete }) => {
     obsolete ? ' obsolete' : ''
   }`;
   return (
-    <article className={c}>
+    <div className={c}>
       <Link to={slug}>{i}</Link>
       <div className="post-excerpt-text">
         <Link to={slug}>
@@ -21,7 +21,7 @@ const PostExcerpt = ({ title, children, img, slug, first, obsolete }) => {
           <div className="more-link">more...</div>
         </Link>
       </div>
-    </article>
+    </div>
   );
 };

Description

differences in outputted HTML in deployed (build) versions - left has weird <article> tag workaround...

see:

issue-b

and:

issue-a

Steps to reproduce

See good vs bad examples : the only difference is the use of the <article> tag creates correct HTML while the <div> tag produces incorrect HTML. Develop produces correct output with the <div> tag.

Expected result

Same HTML output between develop and build or with different tags (article vs div)

https://good.jaywiggins.com

Actual result

the <div class="layout"> gets replaced with <div class="post-excerpt-first first-post-in-list"> only difference is <div> tag replaced with <article> tag.

https://bad.jaywiggins.com

Environment

  System:
    OS: macOS 10.15.3
    CPU: (4) x64 Intel(R) Core(TM) i7-7Y75 CPU @ 1.30GHz
    Shell: 3.2.57 - /bin/bash
  Binaries:
    Node: 12.16.0 - ~/.nvm/versions/node/v12.16.0/bin/node
    Yarn: 1.22.0 - ~/.yarn/bin/yarn
    npm: 6.13.4 - ~/.nvm/versions/node/v12.16.0/bin/npm
  Languages:
    Python: 2.7.16 - /Library/Frameworks/Python.framework/Versions/2.7/bin/python
  Browsers:
    Chrome: 80.0.3987.132
    Safari: 13.0.5
  npmPackages:
    gatsby: ^2.19.7 => 2.19.7 
    gatsby-image: ^2.2.42 => 2.2.42 
    gatsby-plugin-eslint: ^2.0.8 => 2.0.8 
    gatsby-plugin-manifest: ^2.2.39 => 2.2.39 
    gatsby-plugin-mdx: ^1.0.77 => 1.0.77 
    gatsby-plugin-offline: ^3.0.32 => 3.0.32 
    gatsby-plugin-react-helmet: ^3.1.21 => 3.1.21 
    gatsby-plugin-react-helmet-async: ^1.0.15 => 1.0.15 
    gatsby-plugin-sass: ^2.1.29 => 2.1.29 
    gatsby-plugin-sharp: ^2.4.5 => 2.4.5 
    gatsby-plugin-web-font-loader: ^1.0.4 => 1.0.4 
    gatsby-remark-images: ^3.1.44 => 3.1.44 
    gatsby-source-filesystem: ^2.1.46 => 2.1.46 
    gatsby-transformer-sharp: ^2.3.16 => 2.3.16 
bug

Most helpful comment

OK, so apparently the problem is exactly because you wrap your page in gatsby-browser but do not do so in gatsby-ssr which leads to a mismatch during hydration. I just made your example work locally by copying everything from gatsby-browser.js to gatsby-ssr.js. You should do the same.

As stated in docs about wrapPageElement:

There is an equivalent hook in Gatsby’s SSR API. It is recommended to use both APIs together. 

https://www.gatsbyjs.org/docs/browser-apis/#wrapPageElement

All 6 comments

Sometimes, it could be due to a mismatch between the server-rendered and client-rendered HTML. Since your classNames depend on some logic, did you make sure that logic is identical between client and server? Do you see any warnings in console like Warning: Prop `className` did not match.?

Thanks for the reply - gatsby-ssr.js is not in play at all. My gatsby-browser.js file is as follows:

const { createFilePath } = require('gatsby-source-filesystem');
const path = require('path');

exports.createPages = async ({ actions, graphql, reporter }) => {
  const { createPage } = actions;

  const MdxPostTemplate = path.resolve(
    'src/components/MdxPostTemplate/MdxPostTemplate.jsx'
  );

  const result = await graphql(`
    query MdxPageBuilderQuery {
      mdxPageBuilderQuery: allMdx(
        sort: { order: ASC, fields: [frontmatter___date] }
        limit: 1000
      ) {
        edges {
          node {
            fields {
              slug
            }
          }
          next {
            frontmatter {
              title
            }
            fields {
              slug
            }
          }
          previous {
            frontmatter {
              title
            }
            fields {
              slug
            }
          }
        }
      }
    }
  `);

  if (result.errors) {
    reporter.panicOnBuild('Error while running GraphQL query.');
    return;
  }

  result.data.mdxPageBuilderQuery.edges.forEach(({ node, next, previous }) => {
    createPage({
      path: node.fields ? node.fields.slug : null,
      component: MdxPostTemplate,
      context: {
        slug: node.fields.slug,
        next,
        previous,
      },
    });
  });
};

exports.onCreateNode = ({ node, actions, getNode }) => {
  const { createNodeField } = actions;
  if (node.internal.type === 'Mdx') {
    const value = createFilePath({ node, getNode });
    createNodeField({
      name: 'slug',
      node,
      value,
    });
  }
};

the code for the good and bad repos that contains the missing element are exactly the same:

    <div className="layout">
      <Seo title={t} />
      <Header />
      <main>{children}</main>
      <Footer />
    </div>

and

    <div className="layout">
      <Seo title={t} />
      <Header />
      <main>{children}</main>
      <Footer />
    </div>

in the bad version the posts are wrapped in <div> tags, but in the 'good' (workaround) version, the posts are wrapped in <article> tags.

when the posts are wrapped in <article> tags the output looks like this - please note the highlighted line - <div class="layout">

Screen Shot 2020-03-10 at 4 19 04 PM

when the posts are wrapped in <div> tags, the output looks like this - note the highlighted line is incorrect

Screen Shot 2020-03-10 at 4 17 21 PM

when the bad version is run in develop the output is correct and looks like this:

Screen Shot 2020-03-10 at 4 23 50 PM

Here is the output from the build:

Jays-MacBook:blog jayw$ yarn build
yarn run v1.22.0
$ gatsby build
success open and validate gatsby-configs - 0.090s
success load plugins - 2.771s
success onPreInit - 0.009s
success delete html and css files from previous builds - 0.115s
success initialize cache - 0.039s
success copy gatsby files - 0.158s
success onPreBootstrap - 0.040s
success createSchemaCustomization - 0.039s
success source and transform nodes - 0.969s
success building schema - 0.849s
success createPages - 0.184s
success createPagesStatefully - 0.106s
success onPreExtractQueries - 0.004s
success update schema - 0.076s
success extract queries from components - 0.443s
success write out requires - 0.023s
success write out redirect data - 0.003s
success Build manifest and related icons - 1.364s
success onPostBootstrap - 1.411s
â €
info bootstrap finished - 12.379 s
â €
success Building production JavaScript and CSS bundles - 19.424s
success Rewriting compilation hashes - 0.007s
success run queries - 19.711s - 4/4 0.20/s
[                            ]   0.003 s 0/102 0% Building static HTML for pages
Component Gist was not imported, exported, or provided by MDXProvider as global scope
Component Gist was not imported, exported, or provided by MDXProvider as global scope
Component Gist was not imported, exported, or provided by MDXProvider as global scope
Component Gist was not imported, exported, or provided by MDXProvider as global scope
Component Gist was not imported, exported, or provided by MDXProvider as global scope
Component Gist was not imported, exported, or provided by MDXProvider as global scope
Component Gist was not imported, exported, or provided by MDXProvider as global scope
Component Gist was not imported, exported, or provided by MDXProvider as global scope
Component YouTubeVideo was not imported, exported, or provided by MDXProvider as global scope
Component YouTubeVideo was not imported, exported, or provided by MDXProvider as global scope
Component Gist was not imported, exported, or provided by MDXProvider as global scope
Component YouTubeVideo was not imported, exported, or provided by MDXProvider as global scope
Component Gist was not imported, exported, or provided by MDXProvider as global scope
Component Gist was not imported, exported, or provided by MDXProvider as global scope
Component Gist was not imported, exported, or provided by MDXProvider as global scope
Component Gist was not imported, exported, or provided by MDXProvider as global scope
success Building static HTML for pages - 1.866s - 102/102 54.66/s
info Done building in 34.154782359 sec
✨  Done in 35.03s.

OK, so apparently the problem is exactly because you wrap your page in gatsby-browser but do not do so in gatsby-ssr which leads to a mismatch during hydration. I just made your example work locally by copying everything from gatsby-browser.js to gatsby-ssr.js. You should do the same.

As stated in docs about wrapPageElement:

There is an equivalent hook in Gatsby’s SSR API. It is recommended to use both APIs together. 

https://www.gatsbyjs.org/docs/browser-apis/#wrapPageElement

Thanks, and you're absolutely correct. I didn't understand how gatsby-ssr.js and gatsby-browser.js were in play (and still don't) but I know now that they are more related than I believed. I would like to make a PR for updating some of the documentation on this - which states something to the order of:

If you are seeing output differences between gatsby develop and gatsby build please check that wrapPageElement and wrapRootElement are similar in both gatsby-browser.js and gatsby-ssr.js

I have had the same issue with ThemeProvider, solved by doing kind of the same thing explained here https://github.com/gatsbyjs/gatsby/tree/master/examples/using-redux

[...] you'll need to hook into two of Gatsby's extension points.

Once in wrapRootElement which runs during Gatsby's server rendering process, and once in wrapRootElement which is part of Gatsby's browser APIs.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

kuworking picture kuworking  Â·  115Comments

KyleAMathews picture KyleAMathews  Â·  97Comments

cusspvz picture cusspvz  Â·  128Comments

Jivings picture Jivings  Â·  112Comments

omrllm picture omrllm  Â·  83Comments