As comparing two html manually can be rather cumbersome depending on the size of your page, it's advised to first assess what could be wrong rather than brute-forcing. From my experience in 99% of the cases an SSR mismatch occurs when you either:
- Included and rendered a Component which doesn't behave the same way on the client and the server (e.g they use global variables to determine where the code is being run and conditionally render elements based on that). For example there was a clipboard module that would only work on the client because it would use a variable of
window
.
- Rendering of data fetched from an asynchronous source which is only present on either the server or the client. You need to make the same data available for both during the initial render.
If nothing comes out to mind after this, you need to proceed by elimination. In case the error occurs on every page, it is likely to be a result of a misconfiguration of the server. For example, are you doing your own renderToString
? Double check you didn't add an extra nested div in there, the string should be right inside the element you mount React on.
If that is not the case, try to extract one by one the components you are rendering, and you should be able to narrow down pretty quickly which is causing your issue.
Also keep in mind that you would need to restart your server every-time you make a change (unless you have a nodemon or similar config reloading the server-side code when you modify your source) for it to be applied!
As a last resort, you could potentially make your own diff
between the server response and the client first render.
1) Open your console from your site, and paste the following:
console.log(document.documentElement.innerHTML)
2) Click on the Copy
button, and paste that in a client.html
file
3) Now run in your terminal:
curl YOUR_URL > server.html
4) It's likely the server will return you a minified version of your html, so you need to indent it in order to make it match with your client html, use something like this for that purpose.
5) Once you've done this, you can now run the actual diff in your terminal:
diff server.html client.html
This will list you every part of the files that differ between each other.
You can ignore diffs related to Javascript as the indenting will most likely be bad anyway, but concentrate on the html ones, where you might be able to spot differences and infer what is going wrong.
In your case, your translation system is likely to be the root cause of the issue. I would advice to follow more standard practices rather than next-i18next
which seem pretty new and more likely to have problems. Someone else apparently also has an issue with SSR, and to be honest stuff like this is quite scary.
I know it can look a bit troublesome to setup, but here is my own i18n config which can be required either on the server or the client provided you specify a global variable to determine one which environment you are (here __BROWSER__
).
import i18n from 'i18next'
import LanguageDetector from 'i18next-browser-languagedetector'
import { reactI18nextModule } from 'react-i18next'
i18n
.use(require(__BROWSER__ ? 'i18next-xhr-backend' : 'i18next-node-fs-backend'))
.use(LanguageDetector)
.use(reactI18nextModule)
.init({
fallbackLng: 'en',
ns: ['translations'],
defaultNS: 'translations',
interpolation: {
escapeValue: false,
},
react: {
wait: true,
},
backend: {
loadPath: __BROWSER__
? '/locales/{{lng}}/{{ns}}.json'
: require('path').join(__dirname, '/locales/{{lng}}/{{ns}}.json'),
},
})
export default i18n
You simply need to use the middleware, serve the locales from your server so the client can load them from xhr and have the I18nextProvider
require the i18n
instance. The full SSR docs are here.