preact-render-to-string icon indicating copy to clipboard operation
preact-render-to-string copied to clipboard

Styled-Components Rendering Incorrectly

Open JoaquimEsteves opened this issue 3 years ago • 2 comments

Hullo,

First of all I'd like to thank you for your work. I am quite enjoying using your library.

On to my issue:

I have a relatively bare-bones app and I was playing around with your library to generate a static page. However, render to string is refusing to cooperate with styled-components.

The page is quite simple, and can be summarized as:


const Bad = styled.p`
    color: red;
`;

export const App = () => (
  <>
    <Bad>Will Produce bad results</Bad>
    <div>this one is ok</div>
  </>
)

I use viteJS to first convert the typescript + preact shenanigans into plain old JS; followed by your renderToString to replace the html in the necessary place through a simple script;

  await vite.build({
    build: {
      ssr: true,
      rollupOptions: { input: toAbsolute("./src/renderToHtml.tsx") },
      outDir: toAbsolute("./dist/ssr_crap"),
      // Make things nice and readable while I have a bug...
      minify: false,
    },
  });

  const { render } = require("./dist/ssr_crap/renderToHtml.js");
  const appHtml = render();

  const template = fs.readFileSync(toAbsolute("dist/index.html"), "utf-8");

  const html = template.replace(`<!--app-html-->`, appHtml);

  const filePath = "dist/index.html";
  fs.writeFileSync(toAbsolute(filePath), html);

This results in an index.html that features the styled component rendered not as an html element with a class, but instead as: <.sc-bdvvtL>Hello Vite + Preact!</.sc-bdvvtL>

Furthermore: Attempting to use styled-component's CreateGlobalStyle simply crashes my poor script.

Why I believe the issue is in this specific library

Running ViteJs's normal build results in a working page. To my eye, this means that all of the react aliasing is correctly sorted out.

Stuff I tried but didn't work

Previous issues mentioned how I needed to go back to version 3.X of styled-components; but I found a changelog that mentioned that this was no longer the case.

I also tried to follow styled components SSR guide and apply a similar replace trick as I did with the HTML; but it just resulted in more inscrutible errors.

Reproducible example:

Go here and then run:

$ bash docker_functions.sh # See that the dev environment works
$ bash buggy_styled.sh     # See that ssr doesn't :(

Thanks for your time, any help would be appreciated ☺️

JoaquimEsteves avatar Feb 23 '22 18:02 JoaquimEsteves

Solved by forcefully patching React into my SSR script like so:

// Monkey Patch React
// This is necessary due to a bug in styled-components :(
// See: https://preactjs.com/guide/v8/switching-to-preact/#aliasing-manually
const React = require('react')
const ReactDOM = require('react-dom')
const Preact = require('preact/compat')
const Module = module.constructor
Module._cache[require.resolve('react')].exports = Preact
Module._cache[require.resolve('react-dom')].exports = Preact

Inspired by this guide on the preact docs.

This looks quite fragile to me; and I'm wondering if there's not a better alternative. Although I can close this issue if the maintainers believe it's not their concern

JoaquimEsteves avatar Feb 24 '22 11:02 JoaquimEsteves

You might want to try the following approach:

yarn add react@npm:@preact/compat react-dom@npm:@preact/compat

JoviDeCroock avatar Feb 24 '22 11:02 JoviDeCroock

As mentioned, this is in all likelihood due to Vite not applying aliases for SSR builds, instead, keeping the modules external.

Adding the alias package (@preact/compat) as suggested would fix this.

Closing, as it's not really an issue with preact-render-to-string and our docs cover this situation

rschristian avatar May 07 '24 18:05 rschristian