remix-run / remix

Build Better Websites. Create modern, resilient user experiences with web fundamentals.
https://remix.run
MIT License
29.82k stars 2.51k forks source link

styled-components fails in Facebook and TikTok browser #5144

Closed marianzburlea closed 1 year ago

marianzburlea commented 1 year ago

What version of Remix are you using?

1.11.0

Steps to Reproduce

import styled from 'styled-components'

const H1 = styled.h1`
  border: 1px solid red;
`

export default function Index() {
  return (
    <div>
      <H1>test</H1>
    </div>
  )
}

update entry.server.tsx with:

import { renderToString } from "react-dom/server";
import { RemixServer } from "@remix-run/react";
import type { EntryContext } from "@remix-run/node"; // or cloudflare/deno
import { ServerStyleSheet } from "styled-components";

export default function handleRequest(
  request: Request,
  responseStatusCode: number,
  responseHeaders: Headers,
  remixContext: EntryContext
) {
  const sheet = new ServerStyleSheet();

  let markup = renderToString(
    sheet.collectStyles(
      <RemixServer
        context={remixContext}
        url={request.url}
      />
    )
  );
  const styles = sheet.getStyleTags();
  markup = markup.replace("__STYLES__", styles);

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

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

inside root.tsx file update the head tag to include styles:

    <head>
        <Meta />
        <Links />
        {typeof document === "undefined"
          ? "__STYLES__"
          : null}
    </head>

and deploy to netlify.

Here is an example project that replicates the issue: https://github.com/marianzburlea/test-sc

and here is an example of the styled component receiving a red border. https://steady-pixie-99fc00.netlify.app/

Expected Behavior

actual styles to be displayed

image

Actual Behavior

hydration fails and no styles are applied, when I click on a link from Facebook and Tiktok.

The BIG problem is not facebook messenger, it's how people that click paid ads see the website.

This is the same way users that click on an ad, end up on the website, with no styles.

image

image

Xiphe commented 1 year ago

Im experiencing similar issues outside of the context you describe.

Basically any hydration error where react is forced to rebuild the <head> will cause the styles of styled-components to be removed. While react itself recovers from this state, styled-components fails to do so.

As mentioned in #5159 I'd say this is not per se a remix bug but an architectural incompatibility as the issue can be reproduced with any react app failing to hydrate the whole document and might also happen to other libraries that inject things into the DOM.

ref: https://github.com/styled-components/styled-components/issues/3924

Xiphe commented 1 year ago

related issue in react: https://github.com/facebook/react/issues/24430

Xiphe commented 1 year ago

I was able to work around the problem by:

  1. downgrading to react@17
  2. ensuring that {typeof document === "undefined" ? "__STYLES__" : null} is rendered at the very end of the <head>

Would still love to use latest react features within my app.

clgeoio commented 1 year ago

+1 to this issue - Kent has responded to this issue here, stating that it is a React issue, which I think is fair enough, but I'm not sure if or when the React team would be doing anything about it.

There's a potential workaround, but it's not great - you can render Remix not at the document level, but within a <div> in the body. By wrapping in an HTML container you can solve this issue, however it comes at the cost of not being able to use <Meta> or <Links> (mostly). You can somewhat work around this, only sending Links and Meta on the server render and forgoing meta and link changes on the client routing.

entry.client.tsx:

import { RemixBrowser } from '@remix-run/react';
import { hydrateRoot } from 'react-dom/client';

hydrateRoot(document.getElementById('root')!, <RemixBrowser />);

entry.server.tsx (an example using React 18 streams):

import { ServerRoot } from './root.server';

export default async function handleRequest(
  request: Request,
  responseStatusCode: number,
  responseHeaders: Headers,
  remixContext: EntryContext,
) {
  const stream = await renderToReadableStream(
    <ServerRoot
      location={remixContext.staticHandlerContext.location}
      loaderData={remixContext.staticHandlerContext.loaderData}
      routeModules={remixContext.routeModules}
      matches={remixContext.staticHandlerContext.matches}
    >
      <RemixServer context={remixContext} url={request.url} />
    </ServerRoot>,
  );

  return new Response(stream, {
    status: responseStatusCode,
    headers: responseHeaders,
  });
}

ServerRoot.tsx:

const ServerRoot = ({
  children,
  ...rest
}) => {
  return (
    <html lang="en">
      <head>
        <V1Meta {...rest} />
      </head>
      <body>
        <div id="root">
          {children}
        </div>
      </body>
    </html>
  );
};

Where V1Meta is the Meta component from Remix, but takes the params from props rather than hooks (as the context is not created yet)

The root.tsx does not contain any <html> tags and doesn't play allow any information in the head to be changed client-side.

Xiphe commented 1 year ago

Thanks for sharing. I was also tinkering around with rendering the remix app into a sub-element of the page but gave up because it caused too many things I love about remix to stop working.

As with the "who's problem is this": Yeah the issue lays between React and Styled Components. But Remix on the one hand made the architectural decision to work on the whole document which surfaces the problem and on the other hand advertises styled-components to work in remix apps (which it does but at the cost of react@18).

clgeoio commented 1 year ago

I have created a proposal / discussion for this issue https://github.com/remix-run/remix/discussions/5244 - give it an upvote!

Xiphe commented 1 year ago

@kiliman figured out how to do this with the current tools and for me it solves the styled-components issues: https://github.com/remix-run/remix/discussions/5244#discussioncomment-4832036

kiliman commented 1 year ago

@Xiphe awesome... glad it resolved the styled-components issue for you.

github-actions[bot] commented 1 year ago

This issue has been automatically marked stale because we haven't received a response from the original author in a while 🙈. This automation helps keep the issue tracker clean from issues that are not actionable. Please reach out if you have more information for us or you think this issue shouldn't be closed! 🙂 If you don't do so within 7 days, this issue will be automatically closed.

github-actions[bot] commented 1 year ago

This issue has been automatically closed because we didn't hear anything from the original author after the previous notice.