CSS style loader for Webpack that works similarly to style-loader, but is optimized for critical path CSS rendering and also works great in the context of isomorphic apps. It provides two helper methods on to the styles
object - ._insertCss()
(injects CSS into the DOM) and ._getCss()
(returns a CSS string).
See getting started | changelog | Join #isomorphic-style-loader chat room on Discord to stay up to date
$ npm install isomorphic-style-loader --save-dev
Webpack configuration:
module.exports = { /* ... */ module: { rules: [ { test: /\.css$/, use: [ 'isomorphic-style-loader', { loader: 'css-loader', options: { importLoaders: 1 } }, 'postcss-loader' ] } ] } /* ... */ }
Note: Configuration is the same for both client-side and server-side bundles. For more information visit https://webpack.js.org/configuration/module/.
React component example:
/* App.css */ .root { padding: 10px } .title { color: red }
/* App.js */ import React from 'react' import withStyles from 'isomorphic-style-loader/withStyles' import s from './App.scss' function App(props, context) { return ( <div className={s.root}> <h1 className={s.title}>Hello, world!</h1> </div> ) } export default withStyles(s)(App) // <--
P.S.: It works great with CSS Modules! Just decorate your React component with the withStyles higher-order component, and pass a function to your React app via insertCss
context variable (see React's context API) that either calls styles._insertCss()
on a client or styles._getCss()
on the server. See server-side rendering example below:
import express from 'express' import React from 'react' import ReactDOM from 'react-dom' import StyleContext from 'isomorphic-style-loader/StyleContext' import App from './App.js' const server = express() const port = process.env.PORT || 3000 // Server-side rendering of the React app server.get('*', (req, res, next) => { const css = new Set() // CSS for all rendered React components const insertCss = (...styles) => styles.forEach(style => css.add(style._getCss())) const body = ReactDOM.renderToString( <StyleContext.Provider value={{ insertCss }}> <App /> </StyleContext.Provider> ) const html = `<!doctype html> <html> <head> <script src="client.js" defer></script> <style>${[...css].join('')}</style> </head> <body> <div id="root">${body}</div> </body> </html>` res.status(200).send(html) }) server.listen(port, () => { console.log(`Node.js app is running at http://localhost:${port}/`) })
It should generate an HTML output similar to this one:
<html> <head> <title>My Application</title> <script async src="/client.js"></script> <style type="text/css"> .App_root_Hi8 { padding: 10px } .App_title_e9Q { color: red } </style> </head> <body> <div id="root"> <div class="App_root_Hi8"> <h1 class="App_title_e9Q">Hello, World!</h1> </div> </div> </body> </html>
Regardless of how many styles components there are in the app.js
bundle, only critical CSS is going to be rendered on the server inside the <head>
section of HTML document. Critical CSS is what actually used on the requested web page, effectively dealing with FOUC issue and improving client-side performance. CSS of the unmounted components will be removed from the DOM.
Then on client-side use hydrate to make your markup interactive:
import React from 'react' import ReactDOM from 'react-dom' import StyleContext from 'isomorphic-style-loader/StyleContext' import App from './App.js' const insertCss = (...styles) => { const removeCss = styles.map(style => style._insertCss()) return () => removeCss.forEach(dispose => dispose()) } ReactDOM.hydrate( <StyleContext.Provider value={{ insertCss }}> <App /> </StyleContext.Provider>, document.getElementById('root') )
React Hooks Support:
You can also use useStyles
inside your React Functional Components, instead of using withStyles
. Please note that you still need to pass insertCss
function to StyleContext.Provider
from top of the tree.
import React from 'react' import useStyles from 'isomorphic-style-loader/useStyles' import s from './App.scss' const App = (props) => { useStyles(s); return ( <div className={s.root}> <h1 className={s.title}>Hello, world!</h1> </div> ) }; export default App;
The MIT License © 2015-present Kriasoft (@kriasoft). All rights reserved.
Made with ♥ by Konstantin Tarkus (@koistya, blog), Vladimir Kutepov (frenzzy) and contributors
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4