isomorphic-style-loader
isomorphic-style-loader copied to clipboard
Why is it just for react?
The README.md no where said that it's just for react. I wanted it to use for infernojs. I set it up and boom.. it said, react undefined.
Using babel-plugin-transform-runtime
I'm trying to setup for my inferno SSR repository
Any help?
My code:
import Inferno from 'inferno';
import withStyles from 'isomorphic-style-loader/lib/withStyles';
import styles from '../../styles/components/card.sass';
class Card extends Component {
render() {
// const styles = require('../../styles/components/card.sass');
return <div className="card">{ this.props.children }</div>
}
}
export default withStyles(styles)(Card);
My webpack config:
{
test: /\.(sass|scss)$/,
loader: 'isomorphic-style-loader!css-loader!postcss-loader!sass-loader'
}
Want to back this issue? Post a bounty on it! We accept bounties via Bountysource.
The withStyles is a higher order component which supports React.js only. For any other libraries you must call ._insertCss() (client-side) and ._getCss() (server-side) methods manually.
For example:
import style from './style.css';
// use this line when your component get mounted
const removeCss = style._insertCss()
// and this to cleanup after unmounting
removeCss()
this works similar to .use()/.unuse() of style-loader but also provide ability to return css string on server-side during server side rendering via ._getCss()
Thanks @frenzzy What if I don't use this loader for client side? Then I would have to add a flag right? , like
if (__SERVER__) {....}
Also, tried with inferno RouterContext. I'm not able to make it work.
Also, can you please explain why using but "it is optimized for critical path CSS rendering", I couldn't find the reason ?
Of course you can use css on server side only, how you will do it it is up to you.
"it is optimized for critical path CSS rendering" means you can easily extract critical path css during server-side rendering via withStyles helper in react apps.
You can find a working example in react-starter-kit:
- src/client.js - uses
._insertCss()to insert only necessary css at runtime - src/server.js - uses
._getCss()to extract critical path css during server-side rendering - src/components/Header/Header.js - "isomorphic" react component example
Of course you can use css on server side only, how you will do it it is up to you.
Thanks for examples.. Looks like insertCss will be automatically called by React Router (my problem was, this doesn't appear to be the case with inferno RouterContext(which is used at server side)
Thanks for help :) I'll check at inferno community, regarding context in RouterContext and handling insertCss
@frenzzy
In the article for critical rendering css, it suggested to inline only the critical css and not the entire css. How to ensure this using isomorphic-style-loader? It appears that, style imported inside all the components involved in yet to be rendered(SSRed) will be inlined with isomorphic-style-loader. Can you please confirm?
@aseem2625 i'm using isomorphic-style-loader for a project with preact. The only thing I had to do was to implement withStyles by my self and make sure that both server and client build had a insertCss method in the context. You can checkout my solution at https://github.com/malbernaz/preact-hn.
Hi @malbernaz
Thanks for the pointers...
I tried from your repo(only for server part though with your WithStyles.js).
My component looks like this...
import Inferno from 'inferno';
import { Link } from 'inferno-router';
import WithStyles from '../WithStyles';
import s from '../../styles/components/card-link.sass';
const CardLink = function (props) {
return (
<Link to={ props.to } className={s.card}>{ props.children }</Link>
)
};
export default WithStyles(s)(CardLink);
But in WithStyles.js, it's throwing error that TypeError: Cannot read property 'apply' of undefined at this line
this.context.insertCss.apply(undefined, styles);
I am not able to pass context properly to components.
My server code is similar as yours
const css = new Set(); // CSS for all rendered React components
const context = { insertCss: (...styles) => {
console.log('STYLES..', styles);
return styles.forEach(style => css.add(style._getCss()));
}};
<Provider store={store} context={context}> // Passing context like you did
<RouterContext {...renderProps} />
</Provider>
Looks like I need to handle router in RouterContext somehow. Will update soon if it works. Thanks
You missed that I've implemented a Provider component myself. You are trying to use Inferno redux provider for something it is not meant to. Try something like this:
import Component from "inferno-component";
export default class StyleProvider extends Component {
getChildContext() {
return this.props.context;
}
render() {
return this.props.children;
}
}
@frenzzy @malbernaz
So, I'm now able to get my CSS inserted in <head> in index.html at server but issue is, I don't want to do use it for client side.. but at client side it is throwing error because the component I pasted above, when tries to compute in frontend, throws error at this.context.insertCss.apply in WithStyles. And in your code, I noticed that you're handling things at client side which is resolving this issue (but i don't want client side)
1) For client side, to avoid this above error to be thrown, I'm using __SERVER__ flag which works fine but is there any better solution @frenzzy
2) Another main doubt is that.. I'm not able to make it work but how will it work along with Extract Text Css plugin? Because it inserts the extracted CSS inside
tag and until this CSS is loaded, 1st render won't happen faster even when all styles are actually there!!-> I noticed that Lighthouse(chrome dev tools) is giving first paint time of 2.3sec, however, performance(chrome dev tools) is giving it 800ms(which I think isn't considering .css inside
Thanks guys for your help 🙂
Are you providing your app at the client side with the same context you are passing to the server? You have to implement the insertCss function your self. It should look something like this:
function insertCss(...styles) {
const removeCss = styles.map(x => x._insertCss());
return () => removeCss.forEach(f => f());
}
And assuming you've implemented a context provider your self, given the example I've gave you:
const context = { insertCSS };
const App = () =>
<StyleProvider context={context}>
<ReduxProvider store={store}>
<Whatever />
</ReduxProvider>
</styleProvider>
@malbernaz
I'm not providing any context at client side. And only context, I'm providing at server side is InserCss only bcoz I am using this loader, otherwise no context..
I'm using Inferno, so, it's a bit different though at server
<Provider store={store}>
<MyRouterContext {...renderProps} context={context}/>
</Provider>
class MyRouterContext extends RouterContext {
getChildContext() {
let baseContext = super.getChildContext();
baseContext = {...baseContext, ...this.props.context};
return baseContext;
}
}
This above works perfectly fine for me with a condition that i've put a condition using flag __SERVER__ to do
if (__SERVER__) {
this.removeCss = this.context.insertCss.apply(undefined, styles);
}
and
if (__SERVER__) {
setTimeout(this.removeCss, 0);
}
So, things worked fine except for one thing below which I didn't understand how to resolve along with plugin..!
-> Also, do you have any idea regarding, bundle.[chunkash].css being added in <head> tag bcoz of extract plugin. And this making Critical rendering blocking even when it's not required as CSS has already been included in inlined css using this loader!
Thanks 🙂
Hi @frenzzy @malbernaz
I noticed one thing that.. I'm using this iso style loader on server and style loader in client side.
Couple of serious issues, I encountered:
- If a element is there, example card(decorated with
WithStyles) and if this card is (reusable component) used more than 1 time for a particular route then all those styles for card will be inserted in HTML as many times. - Once client takes over (when all scripts are loaded), then
style-loaderis also inserting those styles again causing same styles to present more than 1 time.
Any idea how can I resolve this?
@aseem2625 Could you set up isomorphic-style-loader for infernojs finally?
Hey @TotallWAR I was able to make it work. Only issue was the duplicate styles. One which was getting inserted on the server side and the other which was happening on client side. I didn't invest too much time that while, so didn't resolve it.
Some months ago, I did for react, and you can easily resolve this by extracting styles from client side code, so it won't produce duplicate styles in the DOC. (I didn't use isomorphic styles tho. I used plain setup and different webpack scripts for both server and client which made things pretty straightforward.)
@aseem2625 i have error style._getCss is not a function on server side however i added loader to server webpack config. Did you meet this problem?
@TotallWAR
Been long, I don't remember exactly. But you can have a look at my repo
https://github.com/aseem2625/inferno-starter [Switch to ssr branch]