React_on_rails: Styled Components Server Side Rendering

Created on 29 Jun 2017  路  12Comments  路  Source: shakacode/react_on_rails

I need enable the server side rendering for styled components.
But I don't know how to enable this with the React on Rails API for server side rendering.

In the documentation of styled component this it's a example

import { renderToString } from 'react-dom/server'
import { ServerStyleSheet, StyleSheetManager } from 'styled-components'

const sheet = new ServerStyleSheet()
const html = renderToString(
  <StyleSheetManager sheet={sheet.instance}>
    <YourApp />
  </StyleSheetManager>
)

const css = sheet.getStyleTags() // or sheet.getStyleElement()

But I don't know how to enable with React on Rails.

discussion waiting for submitter

Most helpful comment

For those who come later the above implementation by @esshka should be afaict:

<% hello_world_app = react_component_hash("HelloWorld", prerender: true) %>
<%= hello_world_app["componentCss"] %>
<%= hello_world_app["componentHtml"] %>

All 12 comments

@MiguelSavignano I'm not clear on why you'd use this over CSS-Modules which work fine with React on Rails on Server Rendering. That being said, please document your experiences and consider adding doc PR file to our /docs/additional-reading. If you need a code change to make this work, please submit a code PR with a good example.

Hi thanks for your answer, my experience with styled components was very satisfactory and I currently have all my ui written with styled components.
So I would like that ReactOnRails be able to give us the possibility of using libraries that compiles CSS in the server side. Like the libraries: aphrodite, styled-components, glamorous.

I propose add a similar method as getHtmlAndCss that return the html and css renderized in the server; and add a new helper react_component_with_css for print the html and the style tag.

  import { ServerStyleSheet } from 'styled-components'

  ReactOnRails.getHtmlAndCss = (componentName, props = {}) => {
    const sheet = new ServerStyleSheet()
    let App = ReactOnRails.getComponent(componentName).component
    const html = ReactDOMServer.renderToString(
      <StyleSheetManager sheet={sheet.instance}>
        <App {...props} />
      </StyleSheetManager>
    )
    const css = sheet.getStyleTags()
    return { html, css }
  }
  def react_component_with_css(component_name, props: {})
    random_id = "react-#{component_name}--#{SecureRandom.uuid}"
    ReactOnRails::ServerRenderingPool.reset_pool_if_server_bundle_was_modified
    js_code = "ReactOnRails.getHtmlAndCss('#{component_name}', #{props.to_json})"
    result = ReactOnRails::ServerRenderingPool.eval_js(js_code) || {}
    "
    #{ result['css'] if result['css']  }
    <div id='#{random_id}'>#{result['html']}</div>
    <script>
      (function(){
        ReactOnRails.render('#{component_name}', #{props.to_json}, '#{random_id}');
      }())
    </script>
    ".html_safe
  end

I could work in these days in a pull request to add something similar like this two methods, what do you think about this idea?.
Thank you in advance.
Kind regards

@MiguelSavignano This sounds intriguing.

However, we don't need an additional API. We can simply have the server rendering return an additional key besides the HTML, and if so, then the right thing happens server side.

See https://github.com/shakacode/react_on_rails#generator-functions

And what we did for react-helmut. You can do something similar. Please create a new PR with the docs and examples updated first so I can be sure we are on the right track.

I'm excited to support this!

The current docs of passing an object back from server rendering:

Another reason to use a generator function is that sometimes in server rendering, specifically with React Router, you need to return the result of calling ReactDOMServer.renderToString(element). You can do this by returning an object with the following shape: { renderedHtml, redirectLocation, error }.

For server rendering, if you wish to return multiple HTML strings from a generator function, you may return an Object from your generator function with a single top level property of renderedHtml. Inside this Object, place a key called componentHtml, along with any other needed keys. An example scenario of this is when you are using side effects libraries like React Helmet. Your Ruby code will get this Object as a Hash containing keys componentHtml and any other custom keys that you added: { renderedHtml: { componentHtml, customKey1, customKey2} }

Hello, @justin808 I'm working on, but I'm not very clear why this error comes out and how should I fix it.
I try

const HelloWorldRenderer = (props, railsContext) => {
  const markup = renderToString(<HelloWorld {...props} />);
  return { renderedHtml: markup };
}
export default HelloWorldRenderer

the error it's this:
Uncaught Error: ReactOnRails encountered an error while rendering component: HelloWorld.
Original message: You returned a server side type of react-router error: {"renderedHtml":"

Hello, styled components!

"}
You should return a React.Component always for the client side entry point

This means that I have to create a special bundle for server side and other one for client side?

It works for me

const sheet = new ServerStyleSheet()

export const HelloWorldServer = (props, railsContext) => {
  const markup = renderToString(<StyleSheetManager sheet={sheet.instance}><HelloWorld {...props} /></StyleSheetManager>);
  const css = sheet.getStyleTags()
  return { renderedHtml: { componentHtml: markup, componentCss:  css} };
}

export const HelloWorldClient = (props, railsContext) => {
  <HelloWorld {...props} />
}

in Rails view

<% hello_world_app = react_component("HelloWorld", prerender: true) %>
<%= hello_world_app["componentCss"] %>
<%= hello_world_app["componentHtml"] %>

@MiguelSavignano Closing for now. Feel free to ask to reopen.

If you want personalized help on this, please consider our Coaching Plan.

What's the status on this feature? I also would like to you use it on a project I'm working on. Like @MiguelSavignano, I would like to use react rendered server side with styled components for the same reason. It's closer to the page component pattern that Google has been advocating and in my experience the pattern well suited for scaling and maintaining applications. Any plan to add this?

@zquintana Did this work?

@justin808, I'm using a slight bastardization this library with Symfony PHP. https://github.com/Limenius/ReactBundle. I'm going to try something similar to what's here, but I'll have to render it out in template differently.

@zquintana Did you successfully make styled-components work with ReactBundle ?

For those who come later the above implementation by @esshka should be afaict:

<% hello_world_app = react_component_hash("HelloWorld", prerender: true) %>
<%= hello_world_app["componentCss"] %>
<%= hello_world_app["componentHtml"] %>

Running into this issue while server rendering Material-UI, I had to change @esshka's proposal by using railsContext to differentiate the server and client cases. I documented my solution in another issue.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

amf9t2 picture amf9t2  路  8Comments

thomassnielsen picture thomassnielsen  路  4Comments

robwise picture robwise  路  3Comments

cubadomingo picture cubadomingo  路  6Comments

justin808 picture justin808  路  7Comments