styled-components: 4.0 - [SSR] ServerStyleSheet's `collectStyles` fails to collect styles from symlinked modules

I’ve recently begun a styled-components 4.0 upgrade on our apps but ran into a hard blocker around SSR, local development and symlinked modules using yarn link, which differs from v3.

Here’s a repo reproducing the issue: https://github.com/damassi/styled-components-ssr-issue

Scenario: Our main website is a complex host environment (shell) that imports React components (Apps) from other NPM modules. When the host receives a route request it performs an SSR render

const html = renderToString(sheet.collectStyles(<App />)
... 
res.send(html) 

and sends a payload down to the client, which is then rehydrated on mount.

When working from a local dev environment each of these modules is symlinked via yarn link so dependency changes don’t need to be published to npm after each change.

Because of v4’s new static context API, its no longer possible to yarn link submodules and preserve SSR rendering locally as different styled-component modules are now managing their own list of styled-components to render. From the host app it appears as though there’s nothing to extract because no styled components have been created in that scope.

Due to application complexity, it’s fairly common to run into SSR reconciliation issues and so disabling SSR during local development is not an option. Is there a way to work around? This appears to be a regression from v3 since symlinked modules + SSR behaved well.

(Note that the --preserve-symlinks suggestion from this thread doesn’t seem to work, and we’re not working from a monorepo like Lerna.)

Update:

Was able to resolve via require-control; see this message below.

About this issue

  • Original URL
  • State: closed
  • Created 5 years ago
  • Comments: 30 (7 by maintainers)

Most upvoted comments

Ok @knieber, @probablyup - I think I’ve found a potential solution via require-control. Hasn’t been extensively tested, but looking good so far:

const { setAliases } = require("require-control")
const path = require("path")

setAliases({
  "styled-components": path.resolve(
    path.join(__dirname, "../node_modules/styled-components")
  ),
})

Obviously this isn’t totally ideal as it’s doing some require hacking but seems sufficient for our use-cases.

Let me know if this works for you.

What worked for me is aliasing styled components to the the instance I need. Without it styles probably got compiled with worng instance of SC. In my next.config.js I added

module.exports = nextTranslate({
   //...
    webpack: (config, options) => {
        //...
        config.resolve.alias = {
            ...config.resolve.alias,
            'styled-components': path.resolve(
                path.join(__dirname, 'node_modules/styled-components')
            ),
        };
        return config;
    },
});

Hope this will help somebody (me in future for example)

I’ve opened a pr in order to add this info to npm link issues on FAQ. Special kudos to @damassi for his amazing investigation in this issue. You saved my day dude!

https://github.com/styled-components/styled-components-website/pull/468

Yeah! Our team was psyched to discover it. I should probably open a PR to update SC’s docs.

Finally figured out by babel dupe-ing issue (component lib had a babel-core 6 dependency from storybook) and just got back to the styled-components instancing issue. The require-control solution worked perfectly @damassi. Thanks so much!

Well it’s not an issue with the library, more an environmental restriction. For that reason I would not reopen this issue. If you feel there’s a deficiency in our docs though, I’d welcome a PR there.

Yup, that works since the only instance of styled-components is in the main app folder.

I guess a somewhat flimsy solution would be to write a script that deletes the folder from target locations, but yeah 😄

We would love a PR @damassi!!!