Server Side Rendering #124

Closed
geelen opened this Issue Oct 20, 2016 · 37 comments

Comments

Projects
None yet
@geelen
Member

geelen commented Oct 20, 2016

Thought I'd open an issue to kick off the discussion. It's already possible because of the way we build on top of Glamor, but we haven't exposed it as an API. But basically, if you did this:

import styleSheet from 'styled-components/lib/models/StyleSheet'

/* before each render */
styleSheet.flush()

/* after each render */
styleSheet.rules().map(rule => rule.cssText).join('\n')

Then you should get the chunk of CSS you need for each request. Can someone with a server-rendered setup take a look and confirm this works, and maybe show how they'd be invoking it? I'd be happy enough to export something like:

import { serverStylesheet } from 'styled-components'

/* before each render */
serverStylesheet.reset()

/* after each render */
serverStylesheet.getCSS()

What do people think?

@pheuter

This comment has been minimized.

Show comment
Hide comment
@pheuter

pheuter Oct 22, 2016

I'll gladly help test this out! We currently avoid using styled components for most styles because of the unclear path to support SSR, would be great to move the inline styles into styled components if we can generate the CSS on the server.

Here is what I have setup so far:

import styleSheet from 'styled-components/lib/models/StyleSheet';

styleSheet.flush();

let markup = renderToString(
  <Root store={store} Router={ServerRouter} routerProps={{ location, context }} />
);

console.log(styleSheet.rules());

Unfortunately, I'm getting back an empty array []. I can confirm that one of the components nested inside Root is wrapped by styled and generates a className when the client loads.

pheuter commented Oct 22, 2016

I'll gladly help test this out! We currently avoid using styled components for most styles because of the unclear path to support SSR, would be great to move the inline styles into styled components if we can generate the CSS on the server.

Here is what I have setup so far:

import styleSheet from 'styled-components/lib/models/StyleSheet';

styleSheet.flush();

let markup = renderToString(
  <Root store={store} Router={ServerRouter} routerProps={{ location, context }} />
);

console.log(styleSheet.rules());

Unfortunately, I'm getting back an empty array []. I can confirm that one of the components nested inside Root is wrapped by styled and generates a className when the client loads.

@diegohaz

This comment has been minimized.

Show comment
Hide comment
@diegohaz

diegohaz Oct 24, 2016

Member

I'm currently implementing SSR on a project and I managed to make this work without styleSheet.flush() (if I call this, the result is the same as @pheuter), only the styles from injectGlobal weren't included.

Once I finish it I will post the complete code and a demo here. (I'm trying to make the app work completely without javascript enabled on client).

Member

diegohaz commented Oct 24, 2016

I'm currently implementing SSR on a project and I managed to make this work without styleSheet.flush() (if I call this, the result is the same as @pheuter), only the styles from injectGlobal weren't included.

Once I finish it I will post the complete code and a demo here. (I'm trying to make the app work completely without javascript enabled on client).

@mxstbr

This comment has been minimized.

Show comment
Hide comment
@mxstbr

mxstbr Oct 24, 2016

Member

only the styles from injectGlobal weren't included.

Huh, that's interesting. I think I might accidentally use another styleSheet for that? /cc @geelen

Member

mxstbr commented Oct 24, 2016

only the styles from injectGlobal weren't included.

Huh, that's interesting. I think I might accidentally use another styleSheet for that? /cc @geelen

@diegohaz

This comment has been minimized.

Show comment
Hide comment
@diegohaz

diegohaz Oct 24, 2016

Member

Demo: https://arc.diegohaz.com (disable javascript, even the form works).
Code: https://github.com/diegohaz/arc/blob/universal-redux/src/server.js#L74

It doesn't work with injectGlobal (I removed).

The serverStylesheet.getCSS() API would be nice. 👍

Member

diegohaz commented Oct 24, 2016

Demo: https://arc.diegohaz.com (disable javascript, even the form works).
Code: https://github.com/diegohaz/arc/blob/universal-redux/src/server.js#L74

It doesn't work with injectGlobal (I removed).

The serverStylesheet.getCSS() API would be nice. 👍

@degroote22 degroote22 referenced this issue in este/este Oct 29, 2016

Closed

styled-components #1197

@degroote22

This comment has been minimized.

Show comment
Hide comment
@degroote22

degroote22 Oct 29, 2016

I noticed something.
I'm using Grid from 'grid-styled' and using styleSheet.rules().map(rule => rule.cssText).join('\n') seems to go just one level deep. It never rendered code of Grid, but rendered my code that used media queries.

code is like this:

const Wrapper = styled(Grid)`
  position: fixed;
  display: flex;
  justify-content: center;
  align-items: center;
  height: 40px;
  z-index: 0;
  top: 90px;
  z-index: 1;
  width: 100%;
  left: 0px;
  @media screen and (min-width: 52em){
    left: 16.66%;
  }
  @media screen and (min-width: 64em){
    left: 25%;
  }
`

Invoked as <Wrapper xs={1} sm={1} md={2/3} lg={1/2}>{children}</Wrapper>

css gets rendered as

.iMsaJZ { 
  position: fixed;
  display: -webkit-box;display:flex;display:-webkit-flex;display:-ms-flexbox;
  -webkit-box-pack: center;
  -ms-flex-pack: center;
  -moz-justify-content: center;
  -webkit-justify-content: center;
  justify-content: center;
  -webkit-box-align: center;
  -ms-flex-align: center;
  -webkit-align-items: center;
  align-items: center;
  height: 40px;
  z-index: 0;
  top: 90px;
  z-index: 1;
  width: 100%;
  left: 0px;
 }
@media screen and (min-width: 52em){
  .iMsaJZ { 
    left: 16.66%;
  }
}
@media screen and (min-width: 64em){
  .iMsaJZ { 
    left: 25%;
  }
}

The element receives the correct classname on SSR (class="iMsaJZ hqknNf"), wich are two classes, one for my code(iMsaJZ) and one for Grid's(hqknNf).
But there is no mention to the classname designed to Grid in the initial server response.
When I turn JS on then Grid's code appears to the browser.

.hqknNf { 
  -webkit-box-sizing: border-box; 
  box-sizing: border-box;
  display: inline-block;
  vertical-align: top;
  padding-left: 0px;
  padding-right: 0px;
  width: 100%;
  width: 100%;
 }
@media screen and (min-width: 40em) {
  .hqknNf { 
    width: 100%;
  }
}
@media screen and (min-width: 52em) {
  .hqknNf { 
    width: 66.66666666666666%;
  }
}
@media screen and (min-width: 64em) {
  .hqknNf { 
    width: 50%;
  }
}

Inlining code solves the problem and ssr can be used but it breaks composition :(
I hope I made myself clear :)

Also to notice that while profiling on my dev pc (windows 10) I got 0 ~ 1ms latency calling styleSheet.rules().map(rule => rule.cssText).join('\n')
That's nice!

degroote22 commented Oct 29, 2016

I noticed something.
I'm using Grid from 'grid-styled' and using styleSheet.rules().map(rule => rule.cssText).join('\n') seems to go just one level deep. It never rendered code of Grid, but rendered my code that used media queries.

code is like this:

const Wrapper = styled(Grid)`
  position: fixed;
  display: flex;
  justify-content: center;
  align-items: center;
  height: 40px;
  z-index: 0;
  top: 90px;
  z-index: 1;
  width: 100%;
  left: 0px;
  @media screen and (min-width: 52em){
    left: 16.66%;
  }
  @media screen and (min-width: 64em){
    left: 25%;
  }
`

Invoked as <Wrapper xs={1} sm={1} md={2/3} lg={1/2}>{children}</Wrapper>

css gets rendered as

.iMsaJZ { 
  position: fixed;
  display: -webkit-box;display:flex;display:-webkit-flex;display:-ms-flexbox;
  -webkit-box-pack: center;
  -ms-flex-pack: center;
  -moz-justify-content: center;
  -webkit-justify-content: center;
  justify-content: center;
  -webkit-box-align: center;
  -ms-flex-align: center;
  -webkit-align-items: center;
  align-items: center;
  height: 40px;
  z-index: 0;
  top: 90px;
  z-index: 1;
  width: 100%;
  left: 0px;
 }
@media screen and (min-width: 52em){
  .iMsaJZ { 
    left: 16.66%;
  }
}
@media screen and (min-width: 64em){
  .iMsaJZ { 
    left: 25%;
  }
}

The element receives the correct classname on SSR (class="iMsaJZ hqknNf"), wich are two classes, one for my code(iMsaJZ) and one for Grid's(hqknNf).
But there is no mention to the classname designed to Grid in the initial server response.
When I turn JS on then Grid's code appears to the browser.

.hqknNf { 
  -webkit-box-sizing: border-box; 
  box-sizing: border-box;
  display: inline-block;
  vertical-align: top;
  padding-left: 0px;
  padding-right: 0px;
  width: 100%;
  width: 100%;
 }
@media screen and (min-width: 40em) {
  .hqknNf { 
    width: 100%;
  }
}
@media screen and (min-width: 52em) {
  .hqknNf { 
    width: 66.66666666666666%;
  }
}
@media screen and (min-width: 64em) {
  .hqknNf { 
    width: 50%;
  }
}

Inlining code solves the problem and ssr can be used but it breaks composition :(
I hope I made myself clear :)

Also to notice that while profiling on my dev pc (windows 10) I got 0 ~ 1ms latency calling styleSheet.rules().map(rule => rule.cssText).join('\n')
That's nice!

@gbozee

This comment has been minimized.

Show comment
Hide comment
@gbozee

gbozee Nov 2, 2016

Based on @pheuter test, I get the same result,(the class is injected in the string markup) but not calling stylesheet.flush() before calling styleSheet.rules() produces TypeError: Cannot read property 'cssRules' of undefined

gbozee commented Nov 2, 2016

Based on @pheuter test, I get the same result,(the class is injected in the string markup) but not calling stylesheet.flush() before calling styleSheet.rules() produces TypeError: Cannot read property 'cssRules' of undefined

@aesopwolf

This comment has been minimized.

Show comment
Hide comment
@aesopwolf

aesopwolf Nov 3, 2016

Ah, this is so great. I was able to get it working with gatsby in no time

  render () {
    const styles = styleSheet.rules().map(rule => rule.cssText).join('\n');

    return (
      <html lang="en">
        <head>
          <style>
            {styles}
          </style>
        </head>
        <body>
          <div id="react-mount" dangerouslySetInnerHTML={{ __html: this.props.body }} />
          <script src={prefixLink(`/bundle.js?t=${BUILD_TIME}`)} />
        </body>
      </html>
    )
  }

I'm all for exposing a simpler api via serverStylesheet.getCSS()

aesopwolf commented Nov 3, 2016

Ah, this is so great. I was able to get it working with gatsby in no time

  render () {
    const styles = styleSheet.rules().map(rule => rule.cssText).join('\n');

    return (
      <html lang="en">
        <head>
          <style>
            {styles}
          </style>
        </head>
        <body>
          <div id="react-mount" dangerouslySetInnerHTML={{ __html: this.props.body }} />
          <script src={prefixLink(`/bundle.js?t=${BUILD_TIME}`)} />
        </body>
      </html>
    )
  }

I'm all for exposing a simpler api via serverStylesheet.getCSS()

@kitten

This comment has been minimized.

Show comment
Hide comment
@kitten

kitten Nov 9, 2016

Member

@gbozee I ran into that problem. Turns out when (f.e.) webpack 2 is used, it loads the es bundle, due to the package.json js:next/module fields.

Until there is an API for the stylesheet that we can use, you should work around it using webpack's resolve.alias to load the lib folder instead.

Member

kitten commented Nov 9, 2016

@gbozee I ran into that problem. Turns out when (f.e.) webpack 2 is used, it loads the es bundle, due to the package.json js:next/module fields.

Until there is an API for the stylesheet that we can use, you should work around it using webpack's resolve.alias to load the lib folder instead.

@MoOx MoOx referenced this issue in phenomic/phenomic Nov 14, 2016

Closed

Add Support for Glamor / Aphrodite (CSS-in-JS) #864

MoOx added a commit to phenomic/phenomic that referenced this issue Nov 16, 2016

Added: support for ``Glamor`` and ``styled-components`` out of the…
… box.

Now if you use [Glamor](https://github.com/threepointone/glamor/) to
write your style, static rendering will take that into account and will
prerender styles for you. Nothing to setup. It’s even injecting glamor
ids if you want to rehydrate on startup. See [glamor server
documentation](https://github.com/threepointone/glamor/blob/master/docs/
server.md) to setup hydratation (you will need to handle this yourself
in your ``scripts/phenomic.browser.js``.
Since [styled-components](https://styled-components.com/) [use Glamor
under the
hood](styled-components/styled-components#124)
, this will work for this library as well.

Ref #864

MoOx added a commit to phenomic/phenomic that referenced this issue Nov 16, 2016

Added: support for ``Glamor`` and ``styled-components`` out of the…
… box.

Now if you use [Glamor](https://github.com/threepointone/glamor/) to
write your style, static rendering will take that into account and will
prerender styles for you. Nothing to setup. It’s even injecting glamor
ids if you want to rehydrate on startup. See [glamor server
documentation](https://github.com/threepointone/glamor/blob/master/docs/
server.md) to setup hydratation (you will need to handle this yourself
in your ``scripts/phenomic.browser.js``.
Since [styled-components](https://styled-components.com/) [use Glamor
under the
hood](styled-components/styled-components#124)
, this will work for this library as well.

Ref #864
@diegohaz

This comment has been minimized.

Show comment
Hide comment
@diegohaz

diegohaz Nov 16, 2016

Member

I think I might accidentally use another styleSheet for that?

@mxstbr I'm interested in solving this (injectGlobal). Can you give me a way?

Member

diegohaz commented Nov 16, 2016

I think I might accidentally use another styleSheet for that?

@mxstbr I'm interested in solving this (injectGlobal). Can you give me a way?

@MoOx

This comment has been minimized.

Show comment
Hide comment
@MoOx

MoOx Nov 17, 2016

Please get in touch when somebody start to handle this. I would like to get this to work out of the box in Phenomic (I just integrated Glamor and Aphrodite rendering and would like to cover all populars CSS in JS solutions).
I was thinking glamor integration will be enough but @philpl just told me it won't work :(

MoOx commented Nov 17, 2016

Please get in touch when somebody start to handle this. I would like to get this to work out of the box in Phenomic (I just integrated Glamor and Aphrodite rendering and would like to cover all populars CSS in JS solutions).
I was thinking glamor integration will be enough but @philpl just told me it won't work :(

@thisguychris

This comment has been minimized.

Show comment
Hide comment
@thisguychris

thisguychris Nov 17, 2016

Member

@aesopwolf did you get the same problem as @diegohaz of not having styles included from injectGlobal calls?

I think its a general consensus that this is working out of the box. The only minor caveat is styles from injectGlobal isn't included. But fixing that is trivial I guess?

Member

thisguychris commented Nov 17, 2016

@aesopwolf did you get the same problem as @diegohaz of not having styles included from injectGlobal calls?

I think its a general consensus that this is working out of the box. The only minor caveat is styles from injectGlobal isn't included. But fixing that is trivial I guess?

@mxstbr mxstbr referenced this issue in react-boilerplate/react-boilerplate Nov 17, 2016

Closed

Server side rendering #1236

8 of 8 tasks complete

MoOx added a commit to phenomic/phenomic that referenced this issue Nov 17, 2016

@aesopwolf

This comment has been minimized.

Show comment
Hide comment
@aesopwolf

aesopwolf Nov 17, 2016

@thisguychris I just ran a quick test with injectGlobal and I didn't get the same problem. My document head included a single style tag with everything expected.

Input:

const Title = styled.h1`
  font-size: 1.5em;
  text-align: center;
  color: palevioletred;
`;

injectGlobal`
  body {
    background: red;
  }
`;

Output:

<style>
    .kZQsGZ {
        font-size: 1.5em;
        text-align: center;
        color: palevioletred;
    }
    body {
        background: red;
    }
</style>

@thisguychris I just ran a quick test with injectGlobal and I didn't get the same problem. My document head included a single style tag with everything expected.

Input:

const Title = styled.h1`
  font-size: 1.5em;
  text-align: center;
  color: palevioletred;
`;

injectGlobal`
  body {
    background: red;
  }
`;

Output:

<style>
    .kZQsGZ {
        font-size: 1.5em;
        text-align: center;
        color: palevioletred;
    }
    body {
        background: red;
    }
</style>
@geelen

This comment has been minimized.

Show comment
Hide comment
@geelen

geelen Nov 18, 2016

Member

Yeah the implementation for injectGlobal uses the same instance of StyleSheet under the hood so what @aesopwolf is getting looks right..

Member

geelen commented Nov 18, 2016

Yeah the implementation for injectGlobal uses the same instance of StyleSheet under the hood so what @aesopwolf is getting looks right..

@thisguychris

This comment has been minimized.

Show comment
Hide comment
@thisguychris

thisguychris Nov 18, 2016

Member

@geelen so do we get a green light for the exposed api? aka serverStylesheet.getCSS()?

Also, @aesopwolf would you mind giving me a test repo of your SSR to test my PR with?

Member

thisguychris commented Nov 18, 2016

@geelen so do we get a green light for the exposed api? aka serverStylesheet.getCSS()?

Also, @aesopwolf would you mind giving me a test repo of your SSR to test my PR with?

@mxstbr

This comment has been minimized.

Show comment
Hide comment
@mxstbr

mxstbr Nov 18, 2016

Member

I would love an exposed API, would love for you to look into that @thisguychris!

Member

mxstbr commented Nov 18, 2016

I would love an exposed API, would love for you to look into that @thisguychris!

@diegohaz

This comment has been minimized.

Show comment
Hide comment
@diegohaz

diegohaz Nov 18, 2016

Member

You guys are right, injectGlobal is working. It was a problem in my code.
I was using it inside componentDidMount (which doesn't run in ssr).

Sorry for the mess.

Member

diegohaz commented Nov 18, 2016

You guys are right, injectGlobal is working. It was a problem in my code.
I was using it inside componentDidMount (which doesn't run in ssr).

Sorry for the mess.

@diegohaz

This comment has been minimized.

Show comment
Hide comment
@diegohaz

diegohaz Nov 18, 2016

Member

Now this repo is working with styled-components, injectGlobal, SSR and everything: https://github.com/diegohaz/arc/tree/fullstack

@thisguychris

Member

diegohaz commented Nov 18, 2016

Now this repo is working with styled-components, injectGlobal, SSR and everything: https://github.com/diegohaz/arc/tree/fullstack

@thisguychris

@tomazy

This comment has been minimized.

Show comment
Hide comment
@tomazy

tomazy Nov 18, 2016

It would be great if .flush() worked. The problem may be somewhere here:
https://github.com/styled-components/styled-components/blob/master/src/vendor/glamor/sheet.js#L88

     this.sheet  = {
        cssRules: [],
        insertRule: rule => {
          const serverRule = { cssText: rule }
          this.sheet.cssRules.push(serverRule)              
          return {serverRule, appendRule: (newCss => serverRule.cssText += newCss)}  
        }
      }

flush() on server does this:

this.sheet.cssRules = []

So after flushing appendRule will be appending text to a dangling serverRule that is not in this.sheet.cssRules anymore.

EDIT:
ComponentStyle holds the ref to the dangling serverRule here:
https://github.com/styled-components/styled-components/blob/master/src/models/ComponentStyle.js#L44

tomazy commented Nov 18, 2016

It would be great if .flush() worked. The problem may be somewhere here:
https://github.com/styled-components/styled-components/blob/master/src/vendor/glamor/sheet.js#L88

     this.sheet  = {
        cssRules: [],
        insertRule: rule => {
          const serverRule = { cssText: rule }
          this.sheet.cssRules.push(serverRule)              
          return {serverRule, appendRule: (newCss => serverRule.cssText += newCss)}  
        }
      }

flush() on server does this:

this.sheet.cssRules = []

So after flushing appendRule will be appending text to a dangling serverRule that is not in this.sheet.cssRules anymore.

EDIT:
ComponentStyle holds the ref to the dangling serverRule here:
https://github.com/styled-components/styled-components/blob/master/src/models/ComponentStyle.js#L44

thisguychris added a commit to thisguychris/styled-components that referenced this issue Nov 18, 2016

Expose Server Rendering API
- fixes #124
- remap flush to reset()
- convenience function getCSS() for SSR

thisguychris added a commit to thisguychris/styled-components that referenced this issue Nov 18, 2016

update CHANGELOG.md
- reference issue #124 and the PR itself

thisguychris added a commit to thisguychris/styled-components that referenced this issue Nov 18, 2016

Expose Server Rendering API
- fixes #124
- remap flush to reset()
- convenience function getCSS() for SSR

update CHANGELOG.md

- reference issue #124 and the PR itself

removed prototype
@thisguychris

This comment has been minimized.

Show comment
Hide comment
@thisguychris

thisguychris Nov 18, 2016

Member

@diegohaz tried fullstack branch getting this error
[0] MongoDB connection error: MongoError: failed to connect to server [0.0.0.0:27017] on first connect

Member

thisguychris commented Nov 18, 2016

@diegohaz tried fullstack branch getting this error
[0] MongoDB connection error: MongoError: failed to connect to server [0.0.0.0:27017] on first connect

@diegohaz

This comment has been minimized.

Show comment
Hide comment
@diegohaz

diegohaz Nov 18, 2016

Member

@thisguychris It's because that branch requires the MongoDB service to run. You can use the universal-redux branch as well (it doesn't require mongod).

Member

diegohaz commented Nov 18, 2016

@thisguychris It's because that branch requires the MongoDB service to run. You can use the universal-redux branch as well (it doesn't require mongod).

@thisguychris

This comment has been minimized.

Show comment
Hide comment
@thisguychris

thisguychris Nov 18, 2016

Member

@diegohaz looks like its working 👍 / js disabled:
http://take.ms/r9QiC

So we don't need flush() / reset() for SSR?

Member

thisguychris commented Nov 18, 2016

@diegohaz looks like its working 👍 / js disabled:
http://take.ms/r9QiC

So we don't need flush() / reset() for SSR?

@tomazy

This comment has been minimized.

Show comment
Hide comment
@tomazy

tomazy Nov 19, 2016

Currently styled components will inject the styles even though they are rendered on the server and there is no need for that. Are you guys planning to do something about it? Ideal solution would be to reuse the prerendered styles and inject only these that are necessary.

Maybe it would be possible to render the generated style tag(s) within with React? Then React would update their contents only when the change.

tomazy commented Nov 19, 2016

Currently styled components will inject the styles even though they are rendered on the server and there is no need for that. Are you guys planning to do something about it? Ideal solution would be to reuse the prerendered styles and inject only these that are necessary.

Maybe it would be possible to render the generated style tag(s) within with React? Then React would update their contents only when the change.

thisguychris added a commit to thisguychris/styled-components that referenced this issue Nov 20, 2016

Expose Server Rendering API
- fixes #124
- remap flush to reset()
- convenience function getCSS() for SSR

update CHANGELOG.md

- reference issue #124 and the PR itself

removed prototype

thisguychris added a commit to thisguychris/styled-components that referenced this issue Nov 20, 2016

Expose Server Rendering API
- fixes #124
- remap flush to reset()
- convenience function getCSS() for SSR

update CHANGELOG.md

- reference issue #124 and the PR itself

removed prototype

thisguychris added a commit to thisguychris/styled-components that referenced this issue Nov 20, 2016

Expose Server Rendering API
- fixes #124
- remap flush to reset()
- convenience function getCSS() for SSR

update CHANGELOG.md

- reference issue #124 and the PR itself

removed prototype

changed to ServerStyleSheet as Pascal Case

- as per [@diegohaz](https://github.com/diegohaz) recommendation

remove mutation and capitlization in export

renamed serverStyle to styleSheet and refactor

- now just extending StyleSheet as pointed out by geeleen
- renamed flush as reset
- added convenience function to call rules.map...

thisguychris added a commit to thisguychris/styled-components that referenced this issue Nov 20, 2016

Expose Server Rendering API
- fixes #124
- remap flush to reset()
- convenience function getCSS() for SSR

update CHANGELOG.md

- reference issue #124 and the PR itself

removed prototype

changed to ServerStyleSheet as Pascal Case

- as per [@diegohaz](https://github.com/diegohaz) recommendation

remove mutation and capitlization in export

renamed serverStyle to styleSheet and refactor

- now just extending StyleSheet as pointed out by geeleen
- renamed flush as reset
- added convenience function to call rules.map...

oopsie - remove dupe entry in changelog.md

add smart default for getCSS

- defaults to now new line
- can be overriden by passing `{ min: false }` in `getCSS()`

add test for styleSheet api
@jorilallo

This comment has been minimized.

Show comment
Hide comment
@jorilallo

jorilallo Nov 21, 2016

Being relatively new to styled-components I started adopting SSR for a hack project. For some reason I'm getting TypeError: Cannot set property 'cssRules' of undefined with or without .flush(). Execution will actually fail to this error already when calling flush() prior to renderToString. I did add some logging to sheet.js and it does visit the block where server side sheet is injected but it's still unavailable.

Here's what logging out styleSheet yields:

e {
  isSpeedy: false,
  sheet: undefined,
  tags: [],
  maxLength: 40,
  ctr: 0 }

and here's my sample code for sanity check:

import express from 'express';
import React from 'react';
import { renderToString } from 'react-dom/server';
import fs from 'fs';
import App from './App.js';
import template from './template.js';
import styleSheet from 'styled-components/lib/models/StyleSheet';

const config = JSON.parse(fs.readFileSync('./build/asset-manifest.json', { encoding: 'utf8' }));

const jsPath = config ? config[Object.keys(config).find(fileName => fileName.endsWith('.js'))] : '/bundle.js';
const cssPath = config ? config[Object.keys(config).find(fileName => fileName.endsWith('.css'))] : null;

styleSheet.flush();
const markup = renderToString(<App />);
const styles = styleSheet.rules().map(rule => rule.cssText).join('\n');

const server = express();
server.use('/static', express.static('build/static'));
server.get('*', (req, res) => res
  .send(template(markup, jsPath, cssPath, styles)));

const port = process.env.PORT || 3000;
server.listen(port);

SSR is working fine aside from styled components.

jorilallo commented Nov 21, 2016

Being relatively new to styled-components I started adopting SSR for a hack project. For some reason I'm getting TypeError: Cannot set property 'cssRules' of undefined with or without .flush(). Execution will actually fail to this error already when calling flush() prior to renderToString. I did add some logging to sheet.js and it does visit the block where server side sheet is injected but it's still unavailable.

Here's what logging out styleSheet yields:

e {
  isSpeedy: false,
  sheet: undefined,
  tags: [],
  maxLength: 40,
  ctr: 0 }

and here's my sample code for sanity check:

import express from 'express';
import React from 'react';
import { renderToString } from 'react-dom/server';
import fs from 'fs';
import App from './App.js';
import template from './template.js';
import styleSheet from 'styled-components/lib/models/StyleSheet';

const config = JSON.parse(fs.readFileSync('./build/asset-manifest.json', { encoding: 'utf8' }));

const jsPath = config ? config[Object.keys(config).find(fileName => fileName.endsWith('.js'))] : '/bundle.js';
const cssPath = config ? config[Object.keys(config).find(fileName => fileName.endsWith('.css'))] : null;

styleSheet.flush();
const markup = renderToString(<App />);
const styles = styleSheet.rules().map(rule => rule.cssText).join('\n');

const server = express();
server.use('/static', express.static('build/static'));
server.get('*', (req, res) => res
  .send(template(markup, jsPath, cssPath, styles)));

const port = process.env.PORT || 3000;
server.listen(port);

SSR is working fine aside from styled components.

thisguychris added a commit to thisguychris/styled-components that referenced this issue Nov 21, 2016

Expose Server Rendering API
- fixes #124
- remap flush to reset()
- convenience function getCSS() for SSR

update CHANGELOG.md

- reference issue #124 and the PR itself

removed prototype

changed to ServerStyleSheet as Pascal Case

- as per [@diegohaz](https://github.com/diegohaz) recommendation

remove mutation and capitlization in export

renamed serverStyle to styleSheet and refactor

- now just extending StyleSheet as pointed out by geeleen
- renamed flush as reset
- added convenience function to call rules.map...

thisguychris added a commit to thisguychris/styled-components that referenced this issue Nov 21, 2016

Expose Server Rendering API
- fixes #124
- remap flush to reset()
- convenience function getCSS() for SSR

update CHANGELOG.md

- reference issue #124 and the PR itself

removed prototype

changed to ServerStyleSheet as Pascal Case

- as per [@diegohaz](https://github.com/diegohaz) recommendation

remove mutation and capitlization in export

renamed serverStyle to styleSheet and refactor

- now just extending StyleSheet as pointed out by geeleen
- renamed flush as reset
- added convenience function to call rules.map...

@JakeDawkins JakeDawkins referenced this issue in NewSpring/Holtzman Nov 22, 2016

Closed

Styling Components Research #1497

@stereobooster stereobooster referenced this issue in timarney/react-faq Nov 22, 2016

Closed

Is CSS in JS the only way to go? #23

@dignifiedquire

This comment has been minimized.

Show comment
Hide comment
@dignifiedquire

dignifiedquire Nov 23, 2016

I am trying to use hedron, but it seems the styles from it are not present. But my custom styles are rendered fine. Any ideas why this is happening/how to work around?

I am trying to use hedron, but it seems the styles from it are not present. But my custom styles are rendered fine. Any ideas why this is happening/how to work around?

thisguychris added a commit to thisguychris/styled-components that referenced this issue Nov 23, 2016

Expose Server Rendering API
- fixes #124
- remap flush to reset()
- convenience function getCSS() for SSR

update CHANGELOG.md

- reference issue #124 and the PR itself

removed prototype

changed to ServerStyleSheet as Pascal Case

- as per [@diegohaz](https://github.com/diegohaz) recommendation

remove mutation and capitlization in export

renamed serverStyle to styleSheet and refactor

- now just extending StyleSheet as pointed out by geeleen
- renamed flush as reset
- added convenience function to call rules.map...
@clempat

This comment has been minimized.

Show comment
Hide comment
@clempat

clempat Nov 29, 2016

@jorilallo I had quite similar issue. In my case I solved it by adding 'styled-components/lib/models/StyleSheet' in externals in webpack 2.

externals: {
  'styled-components/lib/models/StyleSheet': 'commonjs styled-components/lib/models/StyleSheet',
}

it was not there because I was adding only the node_modules root folder to externals.

clempat commented Nov 29, 2016

@jorilallo I had quite similar issue. In my case I solved it by adding 'styled-components/lib/models/StyleSheet' in externals in webpack 2.

externals: {
  'styled-components/lib/models/StyleSheet': 'commonjs styled-components/lib/models/StyleSheet',
}

it was not there because I was adding only the node_modules root folder to externals.

@codepunkt

This comment has been minimized.

Show comment
Hide comment
@codepunkt

codepunkt Dec 4, 2016

Member

I'm not sure why this is so hard using styled-components. It's already possible with glamor, which it seems this is based on, right? Can't we just simply use the underlying glamor API or parts of it and re-expose?

@geelen Concatenating the resulting cssText is fine, but it should also be rehydrated on server-side to prevent the client from injecting the styles again.

Member

codepunkt commented Dec 4, 2016

I'm not sure why this is so hard using styled-components. It's already possible with glamor, which it seems this is based on, right? Can't we just simply use the underlying glamor API or parts of it and re-expose?

@geelen Concatenating the resulting cssText is fine, but it should also be rehydrated on server-side to prevent the client from injecting the styles again.

@thisguychris

This comment has been minimized.

Show comment
Hide comment
@thisguychris

thisguychris Dec 4, 2016

Member

@code-punkt see #214 :)

Member

thisguychris commented Dec 4, 2016

@code-punkt see #214 :)

@SachaG

This comment has been minimized.

Show comment
Hide comment
@SachaG

SachaG Dec 30, 2016

Contributor

@aesopwolf thanks for the code snippet, worked with Gatsby for me too. But I think it should be <style dangerouslySetInnerHTML={{ __html: styles }} />, otherwise special characters will get escaped out.

Contributor

SachaG commented Dec 30, 2016

@aesopwolf thanks for the code snippet, worked with Gatsby for me too. But I think it should be <style dangerouslySetInnerHTML={{ __html: styles }} />, otherwise special characters will get escaped out.

@eXon

This comment has been minimized.

Show comment
Hide comment
@eXon

eXon Dec 30, 2016

@tomazy You can always remove your SSR style block once the client-side rendering is done. That way, your page will look great while the app is loading and you will not get duplicate styles.

ReactDOM.render(<App />, document.getElementById('app'));
document.getElementById('ssr-style').remove();

eXon commented Dec 30, 2016

@tomazy You can always remove your SSR style block once the client-side rendering is done. That way, your page will look great while the app is loading and you will not get duplicate styles.

ReactDOM.render(<App />, document.getElementById('app'));
document.getElementById('ssr-style').remove();
@codepunkt

This comment has been minimized.

Show comment
Hide comment
@codepunkt

codepunkt Dec 30, 2016

Member

@eXon yes, but styled-components could also just stop injecting it on the client-side again so you don't have to hack stuff like that together ;-)

See #214 for more discussion about this.

Member

codepunkt commented Dec 30, 2016

@eXon yes, but styled-components could also just stop injecting it on the client-side again so you don't have to hack stuff like that together ;-)

See #214 for more discussion about this.

@teonik

This comment has been minimized.

Show comment
Hide comment
@teonik

teonik Dec 31, 2016

@tomazy I used something similar to remove the server rendered critical-css block when react and styled-components kicked in on the client side.
The problem with this approach is that it will also remove any styles you injected with injectGlobals,Those styles, if rendered on the server, will end up in the ssr block.
It is also feels like a dirty hack compared to the rest of the experience offered by styled-components 😛

Of course there are simpler ways to stick some global css in the pre-rendered page's head section but the point is that it would be nice to have proper support for it.

teonik commented Dec 31, 2016

@tomazy I used something similar to remove the server rendered critical-css block when react and styled-components kicked in on the client side.
The problem with this approach is that it will also remove any styles you injected with injectGlobals,Those styles, if rendered on the server, will end up in the ssr block.
It is also feels like a dirty hack compared to the rest of the experience offered by styled-components 😛

Of course there are simpler ways to stick some global css in the pre-rendered page's head section but the point is that it would be nice to have proper support for it.

@datoml datoml referenced this issue in ctrlplusb/react-universally Dec 31, 2016

Open

styled-components improvement #297

thisguychris added a commit to thisguychris/styled-components that referenced this issue Jan 10, 2017

Expose Server Rendering API
- fixes #124
- remap flush to reset()
- convenience function getCSS() for SSR

update CHANGELOG.md

- reference issue #124 and the PR itself

removed prototype

changed to ServerStyleSheet as Pascal Case

- as per [@diegohaz](https://github.com/diegohaz) recommendation

remove mutation and capitlization in export

renamed serverStyle to styleSheet and refactor

- now just extending StyleSheet as pointed out by geeleen
- renamed flush as reset
- added convenience function to call rules.map...

@mxstbr mxstbr modified the milestone: v2.0 Jan 17, 2017

@kristojorg

This comment has been minimized.

Show comment
Hide comment
@kristojorg

kristojorg Jan 19, 2017

Contributor

PSA: I was seeing TypeError: Cannot read property 'cssRules' of undefined when using this line of code:

const styles = styleSheet.rules().map(rule => rule.cssText).join('\n');

The fix: If you are seeing this as well, make sure you are rendering at least one styled component in your app.

For me, I was just installing styled-components for the first time, and hadn't used one yet, and was therefore seeing this error.

Contributor

kristojorg commented Jan 19, 2017

PSA: I was seeing TypeError: Cannot read property 'cssRules' of undefined when using this line of code:

const styles = styleSheet.rules().map(rule => rule.cssText).join('\n');

The fix: If you are seeing this as well, make sure you are rendering at least one styled component in your app.

For me, I was just installing styled-components for the first time, and hadn't used one yet, and was therefore seeing this error.

@kristojorg

This comment has been minimized.

Show comment
Hide comment
@kristojorg

kristojorg Jan 20, 2017

Contributor

I have submitted a PR to fix this issue here. Hope this helps some!

Contributor

kristojorg commented Jan 20, 2017

I have submitted a PR to fix this issue here. Hope this helps some!

@karthikiyengar

This comment has been minimized.

Show comment
Hide comment
@karthikiyengar

karthikiyengar Mar 23, 2017

I am trying to use hedron, but it seems the styles from it are not present. But my custom styles are rendered fine. Any ideas why this is happening/how to work around?

@dignifiedquire - Could you please tell me how you worked around this? I'm having the exact same issue with hedron.
@diegohaz - Using the universal-redux branch for this, is there anything that I should look into particularly?

I am trying to use hedron, but it seems the styles from it are not present. But my custom styles are rendered fine. Any ideas why this is happening/how to work around?

@dignifiedquire - Could you please tell me how you worked around this? I'm having the exact same issue with hedron.
@diegohaz - Using the universal-redux branch for this, is there anything that I should look into particularly?

@kristojorg

This comment has been minimized.

Show comment
Hide comment
@kristojorg

kristojorg Mar 24, 2017

Contributor

@karthikiyengar Hedron is using styled-components which I believe means you need to import both your own server markup and theirs.

Contributor

kristojorg commented Mar 24, 2017

@karthikiyengar Hedron is using styled-components which I believe means you need to import both your own server markup and theirs.

@karthikiyengar

This comment has been minimized.

Show comment
Hide comment
@karthikiyengar

karthikiyengar Mar 24, 2017

@kristojorg - Thanks for your answer.

Please disregard, my vendor assets weren't being generated properly, leading to the issue.

@kristojorg - Thanks for your answer.

Please disregard, my vendor assets weren't being generated properly, leading to the issue.

@raygesualdo raygesualdo referenced this issue in phenomic/phenomic Mar 26, 2017

Closed

ServerSideStyles not working with webpack 2 #986

@mxstbr

This comment has been minimized.

Show comment
Hide comment
@mxstbr

mxstbr Apr 1, 2017

Member

This discussion has now been moved to #386.

Member

mxstbr commented Apr 1, 2017

This discussion has now been moved to #386.

@mxstbr mxstbr closed this Apr 1, 2017

kherrick added a commit to kherrick/postpress that referenced this issue Apr 29, 2017

@kherrick kherrick referenced this issue in kherrick/postpress Apr 30, 2017

Merged

ssr styled components #7

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment