Isomorphic CSS style loader for Webpack
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
How to Install
$ npm install isomorphic-style-loader --save-dev
Getting Started
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;
Related Projects
License
The MIT License © 2015-present Kriasoft (@kriasoft).
All rights reserved.
Made with ♥ by
Konstantin Tarkus (@koistya, blog),
Vladimir Kutepov (frenzzy)
and contributors