In @builder.io/react@1.1.46
and above we introduced SSR support for a/b tests variations by default, those variations are rendered under template
html tags so they don’t have any effect on rendering performance, the issue with using emotion
css lib is that it doesn’t hoist the resulting styles all the way to the body tag, which could cause a flashing of unstyled content if you’re assigned to one of the tests groups of the content. To fix this issue we need to post process the rendered html to pluck all those styles tags that are tucked under the template tag up to the body tag.
In Next.js you can do that by extracting the styles into a custom document
To do this, you will first create a file at ./pages/_document.js
, this will replace the default Document normally provided by Next.js
then we’re going to need a library to make it possible to extract the necessary styles, we recommend cheerio
npm install cheerio
Import cheerio into your _document file and then we can create a function to extract all the necessary styles
const extractABTestingStyles = (body) => {
let globalStyles = ''
if (body.includes('<template')) {
const $ = cheerio.load(body)
const templates = $('template')
templates.toArray().forEach((element) => {
const str = $(element).html()
const styles = cheerio.load(String(str))('style')
globalStyles += styles
.toArray()
.map((el) => $(el).html())
.join(' ')
})
}
return globalStyles
}
From here, you will run extractABTestStyles
within the MyDocument component to pull all styles, and then set them inside the body
using
<style dangerouslySetInnerHTML={{ __html: this.props.globalStyles }}></style>
class MyDocument extends Document {
static async getInitialProps(ctx) {
const originalRenderPage = ctx.renderPage
let globalStyles = ''
ctx.renderPage = async (options) => {
const render = await originalRenderPage(options)
globalStyles = extractABTestingStyles(render.html)
return render
}
const initialProps = await Document.getInitialProps(ctx)
return {
...initialProps,
globalStyles,
}
}
render() {
return (
<Html>
<Head />
<body>
<style
dangerouslySetInnerHTML={{
__html: this.props.globalStyles,
}}
></style>
<Main />
<NextScript />
</body>
</Html>
)
}
}
export default MyDocument
And that should do it. You can see an example of how we have achieved the same in our Next.js/Shopify starter using the technique outlined above.