Skip to content

Server rendering

The most common use case for server-side rendering is to handle the initial render when a user (or search engine crawler) first requests your app.

When the server receives the request, it renders the required component(s) into an HTML string, and then sends it as a response to the client. From that point on, the client takes over rendering duties.

Material UI on the server

Material UI was designed from the ground-up with the constraint of rendering on the server, but it's up to you to make sure it's correctly integrated. It's important to provide the page with the required CSS, otherwise the page will render with just the HTML then wait for the CSS to be injected by the client, causing it to flicker (FOUC). To inject the style down to the client, we need to:

  1. Create a fresh, new emotion cache instance on every request.
  2. Render the React tree with the server-side collector.
  3. Pull the CSS out.
  4. Pass the CSS along to the client.

On the client-side, the CSS will be injected a second time before removing the server-side injected CSS.

Setting up

In the following recipe, we are going to look at how to set up server-side rendering.

The theme

Create a theme that will be shared between the client and the server:

theme.js

<span class="token keyword">import</span> <span class="token punctuation">{</span> createTheme <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@mui/material/styles'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> red <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@mui/material/colors'</span><span class="token punctuation">;</span>

<span class="token comment">// Create a theme instance.</span>
<span class="token keyword">const</span> theme <span class="token operator">=</span> <span class="token function">createTheme</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
  <span class="token literal-property property">palette</span><span class="token operator">:</span> <span class="token punctuation">{</span>
    <span class="token literal-property property">primary</span><span class="token operator">:</span> <span class="token punctuation">{</span>
      <span class="token literal-property property">main</span><span class="token operator">:</span> <span class="token string">'#556cd6'</span><span class="token punctuation">,</span>
    <span class="token punctuation">}</span><span class="token punctuation">,</span>
    <span class="token literal-property property">secondary</span><span class="token operator">:</span> <span class="token punctuation">{</span>
      <span class="token literal-property property">main</span><span class="token operator">:</span> <span class="token string">'#19857b'</span><span class="token punctuation">,</span>
    <span class="token punctuation">}</span><span class="token punctuation">,</span>
    <span class="token literal-property property">error</span><span class="token operator">:</span> <span class="token punctuation">{</span>
      <span class="token literal-property property">main</span><span class="token operator">:</span> red<span class="token punctuation">.</span><span class="token constant">A400</span><span class="token punctuation">,</span>
    <span class="token punctuation">}</span><span class="token punctuation">,</span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">export</span> <span class="token keyword">default</span> theme<span class="token punctuation">;</span>

The server-side

The following is the outline for what the server-side is going to look like. We are going to set up an Express middleware using app.use to handle all requests that come into the server. If you're unfamiliar with Express or middleware, know that the handleRender function will be called every time the server receives a request.

server.js

<span class="token keyword">import</span> express <span class="token keyword">from</span> <span class="token string">'express'</span><span class="token punctuation">;</span>

<span class="token comment">// We are going to fill these out in the sections to follow.</span>
<span class="token keyword">function</span> <span class="token function">renderFullPage</span><span class="token punctuation">(</span><span class="token parameter">html<span class="token punctuation">,</span> css</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token comment">/* ... */</span>
<span class="token punctuation">}</span>

<span class="token keyword">function</span> <span class="token function">handleRender</span><span class="token punctuation">(</span><span class="token parameter">req<span class="token punctuation">,</span> res</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token comment">/* ... */</span>
<span class="token punctuation">}</span>

<span class="token keyword">const</span> app <span class="token operator">=</span> <span class="token function">express</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// This is fired every time the server-side receives a request.</span>
app<span class="token punctuation">.</span><span class="token function">use</span><span class="token punctuation">(</span>handleRender<span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">const</span> port <span class="token operator">=</span> <span class="token number">3000</span><span class="token punctuation">;</span>
app<span class="token punctuation">.</span><span class="token function">listen</span><span class="token punctuation">(</span>port<span class="token punctuation">)</span><span class="token punctuation">;</span>

Handling the request

The first thing that we need to do on every request is to create a new emotion cache.

When rendering, we will wrap App, the root component, inside a CacheProvider and ThemeProvider to make the style configuration and the theme available to all components in the component tree.

The key step in server-side rendering is to render the initial HTML of the component before we send it to the client-side. To do this, we use ReactDOMServer.renderToString().

Material UI uses Emotion as its default styled engine. We need to extract the styles from the Emotion instance. For this, we need to share the same cache configuration for both the client and server:

createEmotionCache.js

<span class="token keyword">import</span> createCache <span class="token keyword">from</span> <span class="token string">'@emotion/cache'</span><span class="token punctuation">;</span>

<span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token keyword">function</span> <span class="token function">createEmotionCache</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">return</span> <span class="token function">createCache</span><span class="token punctuation">(</span><span class="token punctuation">{</span> <span class="token literal-property property">key</span><span class="token operator">:</span> <span class="token string">'css'</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

With this we are creating a new Emotion cache instance and using this to extract the critical styles for the html as well.

We will see how this is passed along in the renderFullPage function.

<span class="token keyword">import</span> express <span class="token keyword">from</span> <span class="token string">'express'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token operator">*</span> <span class="token keyword">as</span> React <span class="token keyword">from</span> <span class="token string">'react'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token operator">*</span> <span class="token keyword">as</span> ReactDOMServer <span class="token keyword">from</span> <span class="token string">'react-dom/server'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> CssBaseline <span class="token keyword">from</span> <span class="token string">'@mui/material/CssBaseline'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> ThemeProvider <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@mui/material/styles'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> CacheProvider <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@emotion/react'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> createEmotionServer <span class="token keyword">from</span> <span class="token string">'@emotion/server/create-instance'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> App <span class="token keyword">from</span> <span class="token string">'./App'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> theme <span class="token keyword">from</span> <span class="token string">'./theme'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> createEmotionCache <span class="token keyword">from</span> <span class="token string">'./createEmotionCache'</span><span class="token punctuation">;</span>

<span class="token keyword">function</span> <span class="token function">handleRender</span><span class="token punctuation">(</span><span class="token parameter">req<span class="token punctuation">,</span> res</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">const</span> cache <span class="token operator">=</span> <span class="token function">createEmotionCache</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">const</span> <span class="token punctuation">{</span> extractCriticalToChunks<span class="token punctuation">,</span> constructStyleTagsFromChunks <span class="token punctuation">}</span> <span class="token operator">=</span>
    <span class="token function">createEmotionServer</span><span class="token punctuation">(</span>cache<span class="token punctuation">)</span><span class="token punctuation">;</span>

  <span class="token comment">// Render the component to a string.</span>
  <span class="token keyword">const</span> html <span class="token operator">=</span> ReactDOMServer<span class="token punctuation">.</span><span class="token function">renderToString</span><span class="token punctuation">(</span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token class-name">CacheProvider</span></span> <span class="token attr-name">value</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span>cache<span class="token punctuation">}</span></span><span class="token punctuation">></span></span>
      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token class-name">ThemeProvider</span></span> <span class="token attr-name">theme</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span>theme<span class="token punctuation">}</span></span><span class="token punctuation">></span></span>
        <span class="token punctuation">{</span><span class="token comment">/* CssBaseline kickstart an elegant, consistent, and simple baseline to build upon. */</span><span class="token punctuation">}</span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token class-name">CssBaseline</span></span> <span class="token punctuation">/></span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token class-name">App</span></span> <span class="token punctuation">/></span></span>
      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token class-name">ThemeProvider</span></span><span class="token punctuation">></span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token class-name">CacheProvider</span></span><span class="token punctuation">></span></span><span class="token punctuation">,</span>
  <span class="token punctuation">)</span><span class="token punctuation">;</span>

  <span class="token comment">// Grab the CSS from emotion</span>
  <span class="token keyword">const</span> emotionChunks <span class="token operator">=</span> <span class="token function">extractCriticalToChunks</span><span class="token punctuation">(</span>html<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">const</span> emotionCss <span class="token operator">=</span> <span class="token function">constructStyleTagsFromChunks</span><span class="token punctuation">(</span>emotionChunks<span class="token punctuation">)</span><span class="token punctuation">;</span>

  <span class="token comment">// Send the rendered page back to the client.</span>
  res<span class="token punctuation">.</span><span class="token function">send</span><span class="token punctuation">(</span><span class="token function">renderFullPage</span><span class="token punctuation">(</span>html<span class="token punctuation">,</span> emotionCss<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token keyword">const</span> app <span class="token operator">=</span> <span class="token function">express</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

app<span class="token punctuation">.</span><span class="token function">use</span><span class="token punctuation">(</span><span class="token string">'/build'</span><span class="token punctuation">,</span> express<span class="token punctuation">.</span><span class="token function">static</span><span class="token punctuation">(</span><span class="token string">'build'</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// This is fired every time the server-side receives a request.</span>
app<span class="token punctuation">.</span><span class="token function">use</span><span class="token punctuation">(</span>handleRender<span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">const</span> port <span class="token operator">=</span> <span class="token number">3000</span><span class="token punctuation">;</span>
app<span class="token punctuation">.</span><span class="token function">listen</span><span class="token punctuation">(</span>port<span class="token punctuation">)</span><span class="token punctuation">;</span>

Inject initial component HTML and CSS

The final step on the server-side is to inject the initial component HTML and CSS into a template to be rendered on the client-side.

<span class="token keyword">function</span> <span class="token function">renderFullPage</span><span class="token punctuation">(</span><span class="token parameter">html<span class="token punctuation">,</span> css</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">return</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">
    &lt;!DOCTYPE html>
    &lt;html>
      &lt;head>
        &lt;title>My page&lt;/title>
        </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>css<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">
        &lt;meta name="viewport" content="initial-scale=1, width=device-width" />
        &lt;link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&amp;display=swap" />
      &lt;/head>
      &lt;body>
        &lt;div id="root"></span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>html<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">&lt;/div>
      &lt;/body>
    &lt;/html>
  </span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

The client-side

The client-side is straightforward. All we need to do is use the same cache configuration as the server-side. Let's take a look at the client file:

client.js

<span class="token keyword">import</span> <span class="token operator">*</span> <span class="token keyword">as</span> React <span class="token keyword">from</span> <span class="token string">'react'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token operator">*</span> <span class="token keyword">as</span> ReactDOM <span class="token keyword">from</span> <span class="token string">'react-dom'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> CssBaseline <span class="token keyword">from</span> <span class="token string">'@mui/material/CssBaseline'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> ThemeProvider <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@mui/material/styles'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> CacheProvider <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@emotion/react'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> App <span class="token keyword">from</span> <span class="token string">'./App'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> theme <span class="token keyword">from</span> <span class="token string">'./theme'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> createEmotionCache <span class="token keyword">from</span> <span class="token string">'./createEmotionCache'</span><span class="token punctuation">;</span>

<span class="token keyword">const</span> cache <span class="token operator">=</span> <span class="token function">createEmotionCache</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">function</span> <span class="token function">Main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">return</span> <span class="token punctuation">(</span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token class-name">CacheProvider</span></span> <span class="token attr-name">value</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span>cache<span class="token punctuation">}</span></span><span class="token punctuation">></span></span>
      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token class-name">ThemeProvider</span></span> <span class="token attr-name">theme</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span>theme<span class="token punctuation">}</span></span><span class="token punctuation">></span></span>
        <span class="token punctuation">{</span><span class="token comment">/* CssBaseline kickstart an elegant, consistent, and simple baseline to build upon. */</span><span class="token punctuation">}</span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token class-name">CssBaseline</span></span> <span class="token punctuation">/></span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token class-name">App</span></span> <span class="token punctuation">/></span></span>
      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token class-name">ThemeProvider</span></span><span class="token punctuation">></span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token class-name">CacheProvider</span></span><span class="token punctuation">></span></span>
  <span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

ReactDOM<span class="token punctuation">.</span><span class="token function">hydrate</span><span class="token punctuation">(</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token class-name">Main</span></span> <span class="token punctuation">/></span></span><span class="token punctuation">,</span> document<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">'#root'</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

Reference implementations

We host different reference implementations which you can find in the GitHub repository under the /examples folder:

Troubleshooting

Check out the FAQ answer: My App doesn't render correctly on the server.