UNPKG

@toruslabs/isomorphic-style-loader

Version:

CSS style loader for Webpack optimized for critical path CSS rendering and isomoprhic web apps

220 lines (182 loc) 6.72 kB
<img width="150" height="150" align="right" src="https://raw.githubusercontent.com/kriasoft/isomorphic-style-loader/8fe56ef8fba794e00bfbc9b6d731edf0f572d4e7/logo.png" /> # Isomorphic CSS style loader for [Webpack](http://webpack.github.io) [![NPM version](https://img.shields.io/npm/v/isomorphic-style-loader.svg)](https://www.npmjs.com/package/isomorphic-style-loader) [![NPM downloads](https://img.shields.io/npm/dw/isomorphic-style-loader.svg)](https://www.npmjs.com/package/isomorphic-style-loader) [![Library Size](https://img.shields.io/github/size/kriasoft/isomorphic-style-loader/src/withStyles.js.svg)](https://bundlephobia.com/result?p=isomorphic-style-loader) [![Online Chat](https://img.shields.io/discord/643523529131950086?label=Chat)](https://discord.gg/UFkeXwsARY) CSS style loader for Webpack that works similarly to [style-loader](https://github.com/webpack/style-loader), but is optimized for [critical path CSS](https://developers.google.com/web/fundamentals/performance/critical-rendering-path/) rendering and also works great in the context of [isomorphic apps](http://nerds.airbnb.com/isomorphic-javascript-future-web-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](#getting-started) &nbsp;|&nbsp; [changelog](CHANGELOG.md) &nbsp;|&nbsp; Join [#isomorphic-style-loader](https://discord.gg/UFkeXwsARY) chat room on Discord to stay up to date ## How to Install ```bash $ npm install isomorphic-style-loader --save-dev ``` ## Getting Started **Webpack configuration:** ```js 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:** ```css /* App.css */ .root { padding: 10px } .title { color: red } ``` ```js /* 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](https://github.com/css-modules/css-modules)! Just decorate your React component with the [withStyles](https://github.com/kriasoft/isomorphic-style-loader/blob/master/src/withStyles.js) higher-order component, and pass a function to your React app via `insertCss` context variable (see [React's context API](https://reactjs.org/docs/context.html)) that either calls `styles._insertCss()` on a client or `styles._getCss()` on the server. See server-side rendering example below: ```js 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 <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](https://en.wikipedia.org/wiki/Flash_of_unstyled_content) issue and improving client-side performance. CSS of the unmounted components will be removed from the DOM. Then on client-side use [hydrate](https://reactjs.org/docs/react-dom.html#hydrate) to make your markup interactive: ```js 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. ```js 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 * [React Starter Kit](https://github.com/kriasoft/react-starter-kit) — Isomorphic web app boilerplate (Express.js, React, Relay) * [Node.js API Starter](https://github.com/kriasoft/nodejs-api-starter) — Project tempalte for building GraphQL API backends ## License The MIT License © 2015-present Kriasoft ([@kriasoft](https://twitter.com/kriasoft)). All rights reserved. --- Made with ♥ by Konstantin Tarkus ([@koistya](https://twitter.com/koistya), [blog](https://medium.com/@tarkus)), Vladimir Kutepov ([frenzzy](https://github.com/frenzzy)) and [contributors](https://github.com/kriasoft/isomorphic-style-loader/graphs/contributors)