Server Side Rendering
✏️ Edit this pageServer side rendering works out of the box in Emotion 10 and above if you’re only using @emotion/core
and @emotion/styled
. This means you can call React’s renderToString
or renderToNodeStream
methods directly without any extra configuration.
import { renderToString } from 'react-dom/server'
import App from './App'
let html = renderToString(<App />)
Using Emotion 10 with the old SSR APIs
It’s also possible to use emotion 10 with the SSR APIs for vanilla Emotion. It should only be used for compatibility and migration purposes.
import { renderStylesToString } from 'emotion-server'
import { cache } from 'emotion'
import { CacheProvider } from '@emotion/core'
import { renderToString } from 'react-dom/server'
let element = (
<CacheProvider value={cache}>
<App />
</CacheProvider>
)
let html = renderStylesToString(renderToString(element))
API
renderStylesToString
This returns a string of html that inlines the critical css required right before it’s used.
import { renderToString } from 'react-dom/server'
import { renderStylesToString } from 'emotion-server'
import App from './App'
const html = renderStylesToString(renderToString(<App />))
renderStylesToNodeStream
This returns a Node Stream Writable that can be used to insert critical css right before it’s required. This can be used with React’s streaming API.
import { renderToNodeStream } from 'react-dom/server'
import { renderStylesToNodeStream } from 'emotion-server'
import App from './App'
const stream = renderToNodeStream(<App />).pipe(renderStylesToNodeStream())
extractCritical
This returns an object with the properties html
, ids
and css
. It removes unused rules that were created with emotion(it still includes rules that were inserted with injectGlobal
).
import { renderToString } from 'react-dom/server'
import { extractCritical } from 'emotion-server'
import App from './App'
const { html, ids, css } = extractCritical(renderToString(<App />))
hydrate
hydrate
should be called on the client with the ids
that extractCritical
returns. If you don’t call it then emotion will reinsert all the rules. hydrate
is only required for extractCritical
, not for renderStylesToString
or renderStylesToNodeStream
, hydration occurs automatically with renderStylesToString
and renderStylesToNodeStream
.
import { hydrate } from 'emotion'
hydrate(ids)
Next.js
To use emotion’s SSR with Next.js you need a custom Document
component in pages/_document.js
that renders the styles and inserts them into the <head>
. An example of Next.js with emotion can be found in the Next.js repo.
Note:
This only applies if you’re using vanilla Emotion or a version of Emotion prior to v10. For v10 and above, SSR just works in Next.js.
Gatsby
To use emotion’s SSR with Gatsby, you can use gatsby-plugin-emotion
or you can do it yourself with emotion and Gatsby’s various APIs but it’s generally recommended to use gatsby-plugin-emotion
. There’s an example available in the Gatsby repo or you can look at this site which is built with Gatsby!
yarn add gatsby-plugin-emotion
gatsby-config.js
module.exports = {
plugins: [...otherGatsbyPlugins, 'gatsby-plugin-emotion']
}
If using a custom cache, ensure you are creating a new cache per server render within gatsby-ssr.js
. This will differ from the implementation within gatsby-browser.js
.
create-emotion-cache.js
import createCache from '@emotion/cache'
export const createMyCache = () =>
createCache({
key: 'my-prefix-key',
stylisPlugins: [
/* your plugins here */
],
// prefix based on the css property
prefix: key => {
switch (key) {
case 'flex':
return false
case 'transform':
default:
return true
}
}
})
export const myCache = createMyCache()
gatsby-ssr.js
import { CacheProvider } from '@emotion/core'
import { createMyCache } from './create-emotion-cache'
export const wrapRootElement = ({ element }) => (
<CacheProvider value={createMyCache()}>{element}</CacheProvider>
)
gatsby-browser.js
import { CacheProvider } from '@emotion/core'
import { myCache } from './create-emotion-cache'
export const wrapRootElement = ({ element }) => (
<CacheProvider value={myCache}>{element}</CacheProvider>
)
Note:
While Emotion 10 and above supports SSR out of the box, it’s still recommended to use gatsby-plugin-emotion as gatsby-plugin-emotion will enable babel-plugin-emotion and other potential future optimisations.
Puppeteer
If you are using Puppeteer to prerender your application, emotion’s speedy
option has to be disabled so that the CSS is rendered into the DOM.
index.js
// This has to be run before emotion inserts any styles so it's imported before the App component
import './disable-speedy'
import ReactDOM from 'react-dom'
import App from './App'
const root = document.getElementById('root')
// Check if the root node has any children to detect if the app has been prerendered
if (root.hasChildNodes()) {
ReactDOM.hydrate(<App />, root)
} else {
ReactDOM.render(<App />, root)
}
disable-speedy.js
import { sheet } from 'emotion'
// Check if the root node has any children to detect if the app has been preprendered
// speedy is disabled when the app is being prerendered so that styles render into the DOM
// speedy is significantly faster though so it should only be disabled during prerendering
if (!document.getElementById('root').hasChildNodes()) {
sheet.speedy(false)
}
Note:
The
sheet.speedy
call has to be run before anything that inserts styles so it has to be put into it’s own file that’s imported before anything else.