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.
@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":"
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.
Most helpful comment
For those who come later the above implementation by @esshka should be afaict: