Reputation: 1051
I'm very excited about styled components and would love to use it if it wasn't for this...
I've prepared two example projects using next.js universal rendering library.
The first example is using styled-components as a solution, and the second one is using their default solution for css which is styled-jsx.
Both examples include exactly the same code with a minimum level of complexity.
As you will soon see for yourself - in the styled-components example there is a disturbing delay between DOMContentLoaded event and Load event inwhich the user actually sees the un-styled html markup, while in the second example using styled-jsx this is not the case.
Both demos are hosted online using Zeit now:
1 - https://01-styled-components-sqprkdqeft.now.sh
2 - https://02-styled-jsx-nhrynpsdox.now.sh
Source available on github:
1 - https://github.com/Ajar-Ajar/next-demo--styled-components
2 - https://github.com/Ajar-Ajar/next-demo--styled-jsx
I would very much appreciate any insights regarding why does it happen in one and not the other, and of course any way to amend this behavior as I would love to use styled-components for its many features and advantages.
Thank you
Ajar
:)
Upvotes: 2
Views: 2006
Reputation: 957
Here is the recommended way for using styled-components with next to avoid the issue: https://github.com/vercel/next.js/blob/master/examples/with-styled-components/pages/_document.js
import Document 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>
<title>My page</title>
</Head>
<body>
<Main />
<NextScript />
</body>
</html>
)
}
}
Upvotes: 1
Reputation: 11477
What is missing here is the style injection on the server. Basically, when you write styles in JavaScript you have to get the generated styles on the server and inject them as a style
tag into the generated HTML.
The built-in solution for Next does this automatically for you, with styled-components
you have to do a tiny bit of manual work and add a pages/_document.js
file that looks like this:
import Document, { Head, Main, NextScript } from 'next/document'
import { styleSheet } from 'styled-components'
export default class MyDocument extends Document {
static async getInitialProps ({ renderPage }) {
const page = renderPage()
const styles = (
<style dangerouslySetInnerHTML={{ __html: styleSheet.rules().map(rule => rule.cssText).join('\n') }} />
)
return { ...page, styles }
}
render () {
return (
<html>
<Head>
<title>My page</title>
</Head>
<body>
<Main />
<NextScript />
</body>
</html>
)
}
}
Notice how we inject a style tag with the styles from styled-components
. That's all there is to it, now that flash of unstyled content is gone! 🎉 (this is taken from the official example)
Note: With v2 of styled-components
(coming soon, you can get it right now with `npm i --save styled-components@next) there'll be an official API for SSR so it'll look more like this:
import Document, { Head, Main, NextScript } from 'next/document'
import styleSheet from 'styled-components/lib/models/StyleSheet'
export default class MyDocument extends Document {
static async getInitialProps ({ renderPage }) {
const page = renderPage()
const styles = (
<style dangerouslySetInnerHTML={{ __html: styleSheet.getCSS() }} />
)
return { ...page, styles }
}
render () {
return (
<html>
<Head>
<title>My page</title>
</Head>
<body>
<Main />
<NextScript />
</body>
</html>
)
}
}
Hope that helps!
Upvotes: 4