react-universally icon indicating copy to clipboard operation
react-universally copied to clipboard

styled-components improvement

Open datoml opened this issue 8 years ago • 31 comments

Hello,

I made some tests with server side rendering and the styled components feature branch. Seems like the server side rendering doesn't work for the styles. Regarding to this discussions https://github.com/styled-components/styled-components/issues/124 I implemented this:

file: src/server/middleware/reactApplication/index.js

import styleSheet from 'styled-components/lib/models/StyleSheet'
...
// Create our React application and render it into a string.
  const reactAppString = renderToString(
    <CodeSplitProvider context={codeSplitContext}>
      <ServerRouter location={request.url} context={reactRouterContext}>
        <DemoApp />
      </ServerRouter>
    </CodeSplitProvider>,
  );

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

  // Generate the html response.
  const html = generateHTML({
    // Provide the full app react element.
    reactAppString,
    // Nonce which allows us to safely declare inline scripts.
    nonce,
    // Running this gets all the helmet properties (e.g. headers/scripts/title etc)
    // that need to be included within our html.  It's based on the rendered app.
    // @see https://github.com/nfl/react-helmet
    helmet: Helmet.rewind(),
    // We provide our code split state so that it can be included within the
    // html, and then the client bundle can use this data to know which chunks/
    // modules need to be rehydrated prior to the application being rendered.
    codeSplitState: codeSplitContext.getState(),
    styledComponents: styles,
  });

Then I added them into

file: src/server/middleware/reactApplication/generateHTML.js
export default function generateHTML(args: Args) {
  const { reactAppString, initialState, nonce, helmet, codeSplitState, styledComponents } = args;
  ...
return `<!DOCTYPE html>
    <html ${helmet ? helmet.htmlAttributes.toString() : ''}>
      <head>
        ${helmet ? helmet.title.toString() : ''}
        ${helmet ? helmet.meta.toString() : ''}
        ${helmet ? helmet.link.toString() : ''}
        ${styleTags(assetsForRender.css)}
        <style>${styledComponents || ''}</style>
        ${helmet ? helmet.style.toString() : ''}
      </head>
      <body>

Looks now a lot nicer to me. Can someone confirm that this is a good solution? :) Thanks

datoml avatar Dec 31 '16 14:12 datoml

It's a start. More information on styled-components SSR @ https://github.com/styled-components/styled-components/pull/214

codepunkt avatar Dec 31 '16 16:12 codepunkt

Thanks for looking into this @datoml :)

I'll have a review of this soon. :)

ctrlplusb avatar Jan 01 '17 08:01 ctrlplusb

The glamor styleSheet that styled-components exposes is a singleton.With the above approach every request to render a page will result in it's styles being appended to it.So in subsequent requests you end up sending previously rendered CSS unrelated to the current page.

Calling styleSheet.flush() in between requests doesn't seem to work at the moment.

teonik avatar Jan 09 '17 12:01 teonik

Has someone got a full working implementation that they are willing to create a PR for?

ctrlplusb avatar Jan 09 '17 12:01 ctrlplusb

I am currently using styled-components for my project with the hope that there will be a fix in die near future for this. @ctrlplusb I am not that familiar with creating a PRs :).

datoml avatar Jan 09 '17 13:01 datoml

@datoml I encourage you to try!

Check out this cool egghead course: How to Contribute to an Open Source Project on GitHub

ctrlplusb avatar Jan 09 '17 15:01 ctrlplusb

Thanks for the link. I'll have a look :):

datoml avatar Jan 09 '17 15:01 datoml

@ctrlplusb I implemented the SSR changed that @datoml recommended above, you can check it out here. It seems to work, but it double injects the styles, once on the server and once on the client.

rlindskog avatar Jan 09 '17 23:01 rlindskog

I'm wondering if you read what i linked. That's all known and discussed and there's no solution for it yet, even though it's very simple in principle https://github.com/styled-components/styled-components/pull/214

codepunkt avatar Jan 10 '17 07:01 codepunkt

I did. We currently have to wait that this gets implemented. Everything here is currently a workaround until its done and gets released.

datoml avatar Jan 10 '17 07:01 datoml

FYI I have been using styletron, which has no problems with SSR and has a similar API to styled-components. In case this is blocking someones adoption, there are alternatives.

ctrlplusb avatar Jan 10 '17 07:01 ctrlplusb

I tried styletron but I like the api from styled-components more. Doing something like this feels awesome to me :).

import styled from 'styled-components';

const button = styled.button`
  color: green;
  border: 1px solid blue;
`;

datoml avatar Jan 10 '17 07:01 datoml

I'm hard to convince as well. On the one hand i'm not sure about the performance implications of styletrons many CSS selectors in really large DOMs (think Githubs Diff view), on the other hand i love to be able to write CSS instead of objects, including inline syntax highlighting etc, so i'm with @datoml on that one :)

codepunkt avatar Jan 10 '17 07:01 codepunkt

Totally see your guys points, I guess I prefer the object style as it opens up the Javascript as the API to modifying/merging styles:

const centeredButtonStyle = Object.assign(
  {}, 
  { color: 'red' },
  theme.layout.centered
); 

I have been really enjoying creating and composing objects for more generic styles.

ctrlplusb avatar Jan 10 '17 09:01 ctrlplusb

Thats true. The reason styled-components won my heart was that I can copy the whole css and go back to good old css styles. Every approach has it pros and cons tho :).

datoml avatar Jan 10 '17 09:01 datoml

Check this out: https://github.com/RafalFilipek/styled-props

:)

ctrlplusb avatar Jan 11 '17 09:01 ctrlplusb

I will have a look on it :). But it seems similar to the build in ThemeProvider. https://github.com/styled-components/styled-components#theming

Or am I missing something? :D

datoml avatar Jan 11 '17 09:01 datoml

@codepunkt Thanks for suggesting styletron, i was totally unaware of it's existence....:P

teonik avatar Jan 11 '17 10:01 teonik

@ctrlplusb @codepunkt Server Rendering API for styled-components has just been merged into master

lucianlature avatar Jan 11 '17 11:01 lucianlature

@lucianlature it was merged into v2 branch, not master.

codepunkt avatar Jan 11 '17 11:01 codepunkt

Got too excited for a moment, sorry.

lucianlature avatar Jan 11 '17 11:01 lucianlature

Out of curiosity, did you guys look at https://github.com/rofrischmann/fela According to https://github.com/hellofresh/css-in-js-perf-tests it is extremely fast and creates very small sized output compared to something like glamor/styletron. The docs are great (http://fela.js.org/docs/guides/UsageWithReact.html), it works with react-native, and SSR is implemented and works too.

And with https://github.com/jakecoxon/babel-plugin-css-to-js you can have a very similar API to styled-components!

bkniffler avatar Jan 17 '17 11:01 bkniffler

@bkniffler The alternatives are endless. CSS in JSS fatigue! 🤣

codepunkt avatar Jan 18 '17 10:01 codepunkt

In order to get styled-components working server-side with current v2-beta, which api seems still a bit under discussion (https://github.com/styled-components/styled-components/pull/214), you can use this approach.

https://gist.github.com/mschipperheyn/c17280278218074a53147f54259af66a

The reference keyword is: styledComponentCSS

I based this on react-universally@next

mschipperheyn avatar Mar 03 '17 22:03 mschipperheyn

Does anybody have styled components working with react universally nowadays? I am using version 2.1.1 of styled components, but have not had any joy. I looked at the gist provided by 'mschipperheyn', but it looks like things have changed in styled-components land!

I am trying to use ServerStyleSheet, creating an instance and then using collectStyles on my app component. But I am missing something, getStyleElement returns an empty array. I know that app is async so perhaps at the point of collection it is empty?

designspin avatar Jul 13 '17 16:07 designspin

Okay, I was on the right track. I managed to implement Styled Components by passing my ServerStyleSheet instance as a prop on ServerHTML, then using .getStyleElement() to add the css to headerElements within ServerHTML.js. I am really pleased I got this working and also pleased I found React Universally.

designspin avatar Jul 13 '17 18:07 designspin

@designspin is there a gist you could share if it all works? Btw, here is the main issue on styled-components where you could also maybe post that update for others following the issue - https://github.com/styled-components/styled-components/issues/762

oyeanuj avatar Jul 13 '17 19:07 oyeanuj

@oyeanuj

Here is a GIST of what appears to be working for me right now: https://gist.github.com/designspin/c11095334ae1f105d1f93123232d37fd

designspin avatar Jul 13 '17 21:07 designspin

@designspin If you will link me a repo where you're having issues, I'll take a look.

strues avatar Jul 13 '17 22:07 strues

@strues

No issue, the above GIST is in fact a solution. It is working for me, thanks.

designspin avatar Jul 13 '17 22:07 designspin