title |
---|
Server Side Rendering |
Server side rendering works out of the box in emotion 10 and above
import { renderToString } from 'react-dom/server'
import App from './App'
let html = renderToString(<App />)
It's still possible to use emotion 10 with the SSR api's in previous versions of emotion. It primarily exists for compatibility reasons.
import createEmotionServer from 'create-emotion-server'
import createCache from '@emotion/cache'
import { renderToString } from 'react-dom/server'
let cache = createCache()
let { renderStylesToString } = createEmotionServer(cache)
let element = (
<Provider value={cache}>
<App />
</Provider>
)
let html = renderStylesToString(renderToString(element))
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 />))
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()
)
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
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)
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.
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 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.