Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

No "working" styled-components example #167

Closed
1 task done
iDVB opened this issue Feb 15, 2023 · 13 comments · Fixed by #311
Closed
1 task done

No "working" styled-components example #167

iDVB opened this issue Feb 15, 2023 · 13 comments · Fixed by #311

Comments

@iDVB
Copy link

iDVB commented Feb 15, 2023

What version of Remix are you using?

1.12.0

Are all your remix dependencies & dev-dependencies using the same version?

  • Yes

Steps to Reproduce

official styled-components example

Expected Behavior

That it will render both on first load as well as consecutive loads without err.

Actual Behavior

Exhibits the hydration issues mentioned here.
However, in our experience, the styles match fine on FIRST load. Only on page refresh do them become broken. Even with JS disabled this issue persists, which leads me to believe the issue is strictly an SSR one for the moment.

It does not appear that it will work with any SSR without the aid of babel-plugin-styled-components which itself is not supported in Remix since it doesn't seem we have access to plugins or modifying esbuild config.

As a comparison, Next.js allows you to control webpack option values from a configuration file (next.config.js) so they can implement the needed plugins. A member of the development team says in a PR comment that this is because exposing the configuration values would lock in the compiler's choices and also risk breaking the application.

This limitation seems to be leading some remix users to implement workarounds using a custom module to override esbuild.

Then there are a number of other mentions out there of similar issues that link around and I'm not even quite sure if its relates to the same hydration issue or not, but certainly sounds like it would affect the existing example.

There are even posts about how to do this with NextJS that say there are no issues.... but that seems odd when the example doesnt seem to need babel-plugin-styled-components.

@kentcdodds mentions a possible fix here using patch-package, however then @jmurzy mentions that the needed code is stripped out, rendering that possible fix incomplete.

For added injury, we also need to get Material-UI v4 working, which seems to have exactly the same issue. First load, all the style names match up, on reload, none match. This is also with JS disabled, so it appears again to be an SSR only issue.

Conclusion

None of the methods above seem to work for us, and we're currently not clear if its because we use pnpm or that it's a monorepo, or that we also implement our own custom react ui lib that also uses babel-plugin-styled-components but in any case, none seem to work.

We're VERY much wanting to migrate all our sites to Remix but need to slowly migrate away from styled-components, and not cold-turkey cut it off. So we need a migration path for styled-components with Remix.

@iDVB iDVB changed the title No working styled-components example No "working" styled-components example Feb 15, 2023
@iDVB
Copy link
Author

iDVB commented Feb 17, 2023

I'm still getting these hydration errors:
image

Any guidance is VERY much appreciated.

@Xiphe
Copy link

Xiphe commented Feb 20, 2023

Until the root-cause of this is hopefully resolved with react 18.3 you can try out remix-island or a similar approach to hydrate only parts of the document. It works reliably on my end with styled-components (can't stream them though)

@MichaelDeBoey MichaelDeBoey transferred this issue from remix-run/remix Mar 2, 2023
@roj1512
Copy link

roj1512 commented Mar 19, 2023

Unrelated to this issue, but might still be interesting.

I’ve recently created a CSS-in-JS library that builds CSS like Tailwind, and doesn’t require a Babel/esbuild plugin. Specifically for having no issues with Remix.

Read more here: https://github.com/roj1512/classno
Example app: https://github.com/roj1512/classno_remix

@mehranhydary
Copy link

Hi @iDVB @Xiphe - are there any viable paths forward? Running into a hydration issue on Remix as well with a very minimal setup

@Xiphe
Copy link

Xiphe commented Jun 29, 2023

Hi @mehranhydary

Both the remix and the react team are aware of this issue. React is actively working towards a solution as this is affecting any solution that hydrates react into the complete document.

On my end, https://github.com/kiliman/remix-hydration-fix and https://github.com/Xiphe/remix-island present good enough workarounds until the problem will eventually be fixed at it's root cause. You can also try to downgrade react to v17.

@markdalgleish
Copy link
Member

Thanks for raising this issue! I've just merged a PR that updates our Styled Components example to address the issues mentioned here.

If you're still experiencing issues, let us know, or even help us improve the example if there's something we missed.

@Xiphe
Copy link

Xiphe commented Aug 14, 2023

Hi @markdalgleish thanks for taking the time to update the example on this!

Indeed, the change you added to the example should make it so styled components itself don't cause hydration issues.
Unfortunately, from my experience this still isn't really safe to use with react ~18.2.

The problem is that when anything else produces a hydration error, react will wipe away the <style> outlet of styled-components in the head effectively causing any styled component to be unstyled. I haven't validated this with v6 but to my knowledge styled-components is not able to recover from this.

To me that's not a state in which I would ship an app to production.
To my knowledge the only fix is remix-island or waiting and hoping for react@18.3.

I understand that there's little to nothing we can do about this from within remix itself (Other then maybe officially supporting rendering into a smaller part of the page) and the update you did on the example is certainly helpful. Still I wouldn't consider this issue fixed.

@seanmcquaid
Copy link

Hey ya'll! Is there any thought on getting an example together with Vite + Styled Components since this seems to be the direction Remix is going?

@mryechkin
Copy link

Would love to know that as well - just starting to explore Remix + Vite, and styled-components support is paramount for us.

@SilencerWeb
Copy link

@seanmcquaid @mryechkin hey guys, have you managed to successfully integrate styled-components with SSR support for Remix + Vite?

@prxg22
Copy link

prxg22 commented Mar 12, 2024

I did using remix-island. It helps managing the <header> rehydration, but as tradeoff, you loose the ability to render the whole document inside your application.

  1. first create the Header component on your app/root.tsx file
// app/root.tsx

export const Head = createHead(() => {
  return (
    <>
      <meta charSet="utf-8" />
      <meta name="viewport" content="width=device-width,initial-scale=1" />
      <Meta />
      <Links />
    </>
  )
})

export default function App() {
  // this will be rendered inside a node
  return (
    <ThemeProvider>
       <Outlet />   
    </ThemeProvider>
  )
}
  1. In your app/entry.server.tsx collect the StyleSheet and inject it with the head in your document before stream it
// app/entry.server.tsx
import { PassThrough } from 'node:stream'

import {
  createReadableStreamFromReadable,
  type EntryContext,
} from '@remix-run/node'
import { RemixServer } from '@remix-run/react'
import { isbot } from 'isbot'
import { renderToPipeableStream } from 'react-dom/server'
import { renderHeadToString } from 'remix-island'
import { ServerStyleSheet } from 'styled-components'

import { Head } from './root'

const ABORT_DELAY = 5_000

export function handleRequest(
  request: Request,
  responseStatusCode: number,
  responseHeaders: Headers,
  remixContext: EntryContext,
) {
  const styleSheet = new ServerStyleSheet()
  return new Promise((resolve, reject) => {
    let shellRendered = false

    const { pipe, abort } = renderToPipeableStream(
      styleSheet.collectStyles(
        <RemixServer
          context={remixContext}
          url={request.url}
          abortDelay={ABORT_DELAY}
        />,
      ),
      {
        onShellReady() {
          shellRendered = true
          responseHeaders.set('Content-Type', 'text/html')
          const head = renderHeadToString({
            request,
            remixContext,
            Head,
          })
          const body = injectStyles(head, styleSheet, pipe)
          const stream = createReadableStreamFromReadable(body)

          resolve(
            new Response(stream, {
              status: responseStatusCode,
              headers: responseHeaders,
            }),
          )
        },
        onShellError(err: unknown) {
          reject(err)
        },
        onError(err: unknown) {
          if (shellRendered) {
            console.error(err)
          }
        },
      },
    )

    setTimeout(abort, ABORT_DELAY)
  })
}

function injectStyles(
  head: string,
  styleSheet: ServerStyleSheet,
  pipe: <Writable extends NodeJS.WritableStream>(
    destination: Writable,
  ) => Writable,
) {
  const body = new PassThrough()
  body.write(
    `<!DOCTYPE html><html><head>${head} ${styleSheet.getStyleTags()}</head><body><div id="root">`,
  )
  pipe(body)
  body.write('</div></body></html>')
  return body
}
  1. Finally hydrate the #root element in your app/entry.client.tsx:
import { RemixBrowser } from '@remix-run/react'
import { StrictMode, startTransition } from 'react'
import { hydrateRoot } from 'react-dom/client'

startTransition(() => {
  hydrateRoot(
    document.getElementById('root')!,
    <StrictMode>
      <RemixBrowser />
    </StrictMode>,
  )
})

@prxg22
Copy link

prxg22 commented Mar 13, 2024

I know @Xiphe had already recommended the remix-island, but I manage to stream the responses, so I found valuable to share.

@Xiphe
Copy link

Xiphe commented Apr 22, 2024

Awesome! Thanks for sharing @prxg22

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

9 participants