Emotion Theme with Remix doesn't work properly

Open
#584 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
3/5
Estimated time
1-2 days
Newbie friendliness
35/100
Issue type
Bug
Clarity
Needs clarification
Activity status
Stale
Tech stack
react, typescript
Domain
frontend, web-dev

Research direction

Start with the Remix + MUI example linked in the issue, especially app/entry.server.tsx, and compare it with the shown SSR provider setup. Reproduce the behavior using the provided StyledDiv and server render path. Done means the configured theme is available to styled components and useTheme() during SSR, with the client behavior still working.

Written by the indexing model from the issue text.

Description

Hello,

I'm using Remix with Emotion and have encountered a server-side rendering issue: when I wrap the app in <ThemeProvider> from @emotion/react and try to access the theme inside a styled component (or using useTheme()), the theme object is {} during SSR. On the client side, everything works fine.

I'm basing my setup on this Remix + MUI example, with slight modifications:
https://github.com/mui/material-ui/blob/master/examples/material-ui-remix-ts/app/entry.server.tsx

Do you have a relevant example with a theme, or maybe you know where I made a mistake in the configuration?

Here is the relevant SSR configuration:

const handleBrowserRequest = (
  request: Request,
  responseStatusCode: number,
  responseHeaders: Headers,
  remixContext: EntryContext,
) => {
  const cache = createEmotionCache();
  const { extractCriticalToChunks } = createEmotionServer(cache);

  function MuiRemixServer() {
    return (
      <ServerStyleContext.Provider value={null}>
        <CacheProvider value={cache}>
          <ThemeProvider theme={theme}>
            <RemixServer context={remixContext} url={request.url} />
          </ThemeProvider>
        </CacheProvider>
      </ServerStyleContext.Provider>
    );
  }

  // Render the component to a string.
  const html = ReactDOMServer.renderToString(<MuiRemixServer />);
  // Grab the CSS from emotion
  const chunks = extractCriticalToChunks(html);

  const markup = ReactDOMServer.renderToString(
    <ServerStyleContext.Provider value={chunks.styles}>
      <CacheProvider value={cache}>
        <ThemeProvider theme={theme}>
          <CssBaseline />
          <RemixServer context={remixContext} url={request.url} />
        </ThemeProvider>
      </CacheProvider>
    </ServerStyleContext.Provider>,
  );

  responseHeaders.set("Content-Type", "text/html");

  // Enhanced cache control headers to prevent aggressive browser caching
  responseHeaders.set("Cache-Control", "no-store, no-cache, must-revalidate, proxy-revalidate");
  responseHeaders.set("Surrogate-Control", "no-store");
  responseHeaders.set("Pragma", "no-cache");
  responseHeaders.set("Expires", "0");
  // Add a unique timestamp to force browsers to refetch resources
  responseHeaders.set("X-Content-Timestamp", new Date().getTime().toString());

  return new Response(`<!DOCTYPE html>${markup}`, {
    status: responseStatusCode,
    headers: responseHeaders,
  });
};

A simplified styled component that reproduces the issue:

import styled from "@emotion/styled";

export const StyledDiv = styled.div`
  ${({ theme }) => {
    console.log("Theme on server:", theme); // Outputs {}
    return "";
  }}
`;

Environment information:

  • react version: 18.2.0
  • @emotion/react version: 11.13.3
  • @emotion/server version: 11.11.0
  • @mui/material version: 6.1.6
  • @remix-run/dev version: 2.16.4
Dominant language
TypeScript
Stars
1.2k
Forks
235
Avg merge
2h 5m
Merged PRs (30d)
2

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from remix-run/examples

All issues in remix-run/examples

Similar issues

More TypeScript issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.