Wrong styles applied during SSR hydration
javiertury opened this issue · 9 comments
Styled components is applying wrong styles during SSR hydration. The problem is that styles in production builds are swapped across components. For instance, the class sc-16rshtk
is applied to a ToolbarContainer but contains the styles of a FooterLink. I'm using gatsby with gatsby-theme-material-ui and gatsby-plugin-styled-components.
This didn't happen for client side rendering, only for SSR. Since it only affected components styled with styled-components, I tried to collect more information by assigning a displayName to each component, but it did nothing, classnames remained the same.
Then I found #261 and saw this comment. It says that styled-components should not be re-exported to make displayName work.
import styled from '../lib/styled-components'; // Does not work :(
import styled from 'styled-components'; // Works
After applying that change, I made a new production build and then discovered that SSR hydration was now working correctly. I suspect that babel-plugin-styled-components does not work if the package is re-exported.
This issue was very hard to debug.
If you were re-exporting the module to add theme type definitions, please use module augmentation instead.
// src/types/styled-components.ts
import type { Theme } from '@material-ui/core';
declare module 'styled-components' {
export interface DefaultTheme extends Theme {
}
}
You probably should use topLevelImportsPath
to import styled()
from different location. Based on the tests this is supported, see https://github.com/styled-components/babel-plugin-styled-components/blob/main/test/fixtures/add-identifier-with-top-level-import-paths-and-named-import/.babelrc and https://github.com/styled-components/babel-plugin-styled-components/blob/main/test/fixtures/add-identifier-with-top-level-import-paths/.babelrc
Should be confirmed by some of the maintainers.
@javiertury This is the first time I've seen issues that speak very closely to the super annoying issues we are also having.
However, we still can't find a fix in any of the comments.
Env:
@ourcompany/react-ui-components
@ourcompany/projectx
We too are noticing that our projects (@ourcompany/projectx) that consume our ui lib (@ourcompany/react-ui-components) are experiencing this same issue with the wrong styles being applied.
The way we import
We're not using typescript, and never have imported this way in either our repos. import styled from '../lib/styled-components';
Not sure why anyone would do that, so I think I'm missing something important? We've always imported as import styled from 'styled-components';
in both our @ourcompany/react-ui-components
and @ourcompany/projectx
.
Version of babel-plugin-styled-components
We've had to lock babel-plugin-styled-components
to v1.10.3
in order to get it working right. Even tried updated everything to latest v2.0.2
and still broken.
Please Help!
It sounds like many people have solved this issue and have wrapped there head around the cause, but the solution doesn't seem to apply to us?
@iDVB if the symptoms are similar to mine then it's probably because this babel plugin is not transforming your source files.
We've always imported as
import styled from 'styled-components';
Even if you are importing the package correclty, there can be other reasons why your source files are not being transformed.
From the description of your issue, I suspect that the problem lies in your babel configuration with respect to packages @ourcompany/react-ui-components
and @ourcompany/projectx
. Usually babel is configured to transform only the source code of the current package and to ignore node_modules
. Additionally, bundlers like webpack, vite or rollup could also be preventing node_modules
from being transformed by babel.
Please check that both babel and your bundler are configured to transform your node_modules
, or at least node_modules/@ourcompany/react-ui-components
and node_modules/@ourcompany/projectx
.
https://babeljs.io/docs/en/options#exclude
https://babeljs.io/docs/en/options#ignore
@javiertury Thanks! Ya I bet it's something like that. But can you clarify one thing that has been nagging us.
As styled-component ui lib authors, are we supposed to apply the babel-styled-components
plugin when packaging/publishing the lib AND in the consuming repo?
Currently, when publishing the ui lib we ARE using that plugin, however when we are bundling the consuming project (gatsby) we are ONLY targeting things NOT in node_modules. So this lib would not be processed this second time.
Also, keep in mind everything works fine AS-IS so long as we keep to babel-styled-components@v 1.10.5
Beyond the scope of my setup, I can only provide ideas for configuration changes but not much more.
Even though in many cases it's recommended to do transpilation at the library level, I have a gut feeling this plugin is different because there are reports of bugs when duplicated packages for styled-components or the babel plugin are used. I would say try to apply this babel plugin only on your main project(gatsby) and take the plugin out of your libraries (but just this plugin, you can keep the rest of your babel config). Try also to bundle those libraries as ES6 modules (that's the preferred format for rollup and webpack 5).
Finally, take a look at other issues that may be related with other aspects of your setup, for instance this bug with duplicated imports because of symlinked node_modules.
@javiertury makes sense. But the larger question I'm thinking.... is why did this work fine before with previous versions?
@javiertury I'm a little confused about the conclusion of this issue. Are you reporting a bug or just confusion? Did @mnajdova's comment help?
Are you reporting a bug or just confusion?
Now just confusion. Although at first I didn't know about topLevelImportsPath
and I thought it was a bug.
Also I would like to draw attention to how painful it was to find out that re-exporting the package was breaking SSR. Perhaps the debugging experience could be improved somehow or the documentation for the plugin could include a reference to topLevelImportsPath
.
Did @mnajdova's comment help?
I replicated the original problem in my project, used @mnajdova topLevelImportsPath
to solve it and it worked.
I had the same issue when using nx dev with NextJS and what worked for me was to
- Make sure you have .babelrc file in your project with the following
{
"plugins": [["styled-components", { "ssr": true }]],
"presets": ["next/babel"]
}
- Correctly configured _document.tsx file
import Document, { Html, Head, Main, NextScript } from 'next/document';
import { ServerStyleSheet } from 'styled-components';
export default class MyDocument extends Document {
static async getInitialProps(ctx) {
const sheet = new ServerStyleSheet();
const originalRenderPage = ctx.renderPage;
try {
ctx.renderPage = () =>
originalRenderPage({
enhanceApp: (App) => (props) =>
sheet.collectStyles(<App {...props} />),
});
const initialProps = await Document.getInitialProps(ctx);
return {
...initialProps,
styles: (
<>
{initialProps.styles}
{sheet.getStyleElement()}
</>
),
};
} finally {
sheet.seal();
}
}
render() {
return (
<Html>
<Head>{this.props.styles}</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
}