CSS style loader for Webpack that is optimized for isomorphic (universal) web apps.

Overview

Isomorphic CSS style loader for Webpack

NPM version NPM downloads Library Size Online Chat

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:

${body}
` res.status(200).send(html) }) server.listen(port, () => { console.log(`Node.js app is running at http://localhost:${port}/`) }) ">
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 = `
    
      
        
        
      
      
        
   
${body}
` 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:

Hello, World!

">
<html>
  <head>
    <title>My Applicationtitle>
    <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 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

Comments
  • [request] Example without usage of a boilerplate kit, css-modules or withStyles

    [request] Example without usage of a boilerplate kit, css-modules or withStyles

    I've been going through the code and tried to source help on the Discord Reactiflux website to see if I could take the example usage provided and make it work with my own server setup : https://github.com/JaxCavalera/Tagged_Isomorphic/blob/master/src/server.jsx

    The most challenging lines to follow are specifically related to :

    const context = { insertCss: (styles) => css.push(styles._getCss()) };
    

    I don't really understand the purpose of using that variable and I'm unsure of where the styles variable is being defined or how it ends up being an object with functions such as _getCss().

    Will isomorphic-style-loader work without the use of the withStyles Higher order function or is that a dependency module that needs to manually be imported and used in each of my components.. which would seem to then signify that I need to also use css-modules so that makes 2 dependencies.

    I'm not very clear on css-modules and what benefit it brings to the table.

    opened by JaxCavalera 12
  • Move react to peerDependencies

    Move react to peerDependencies

    Fixes #155

    Motivation

    See #155. When react is a dependency of a React-related package, it's possible for multiple instances of React to end up floating around in the bundled/executed code. This isn't always a problem, but recent changes to hooks and context in React have made it that much more likely for it to become a problem.

    Moving react to a peerDependency means that it's up to the consumer to install react, but it also makes it that much less likely that this library will cause problems in other people's applications.

    Changes

    • Move React to a peerDependency
    opened by tstirrat15 11
  • Upgrade isomorphic-style-loader to React 16.6.0 and use new Context API

    Upgrade isomorphic-style-loader to React 16.6.0 and use new Context API

    I want to take advantage of the new React.Component.contextType property in my React application but it requires React 16.6.0. I'm not able to upgrade because of a dependency on isomorphic-style-loader which still uses the legacy context API. Here is a proposed change on how the isomorphic-style-loader can leverage the new React context API.

    Usage:

    import StyleContext from 'isomorphic-style-loader/lib/styleContext';
    
    class App extends React.PureComponent {
      static propTypes = {
        children: PropTypes.element.isRequired,
      };
    
      render() {
        const css = new Set();
        const insertCss = (...styles) => styles.forEach(style => css.add(style._getCss()));
    
        return (
          <StyleContext.Provider value={{ insertCss }}>
            {React.Children.only(this.props.children)}
          </StyleContext.Provider>
        );
      }
    }
    
    opened by mglace 8
  • Error

    Error "Cannot resolve module" and "Unexpected token ."

    I'm using React-Starter-Kit and trying to integrate with AdminLTE. When I imported a css from AdminLTE I got this error.

    If I change the background url from url(blue.png) to url("./blue.png"), the error "Cannot resolve module" will disappear but the error "Unexpected token ." still exist.

    webpack built 706e73eda3d2e1bd0af7 in 6548ms
    Child
        Time: 6548ms
    
        ERROR in ./~/css-loader?{"sourceMap":true,"modules":true,"localIdentName":"[name]_[local]_[hash:base64:3]","minimize":false}!./~/postcss-loader?pack=default!./~/admin-lte/plugins/iCheck/square/blue.css
        Module not found: Error: Cannot resolve module 'blue.png' in /home/nginx/domains/vps.manhhailua.com/public/node_modules/admin-lte/plugins/iCheck/square
         @ ./~/css-loader?{"sourceMap":true,"modules":true,"localIdentName":"[name]_[local]_[hash:base64:3]","minimize":false}!./~/postcss-loader?pack=default!./~/admin-lte/plugins/iCheck/square/blue.css 6:342-361
    
        ERROR in ./~/css-loader?{"sourceMap":true,"modules":true,"localIdentName":"[name]_[local]_[hash:base64:3]","minimize":false}!./~/postcss-loader?pack=default!./~/admin-lte/plugins/iCheck/square/blue.css
        Module not found: Error: Cannot resolve module '[email protected]' in /home/nginx/domains/vps.manhhailua.com/public/node_modules/admin-lte/plugins/iCheck/square
         @ ./~/css-loader?{"sourceMap":true,"modules":true,"localIdentName":"[name]_[local]_[hash:base64:3]","minimize":false}!./~/postcss-loader?pack=default!./~/admin-lte/plugins/iCheck/square/blue.css 6:1768-1790
    Child
        Time: 5675ms
                Asset    Size  Chunks             Chunk Names
            server.js  190 kB       0  [emitted]  main
        server.js.map  184 kB       0  [emitted]  main
    webpack: bundle is now VALID.
    /home/nginx/domains/vps.manhhailua.com/public/node_modules/admin-lte/plugins/iCheck/square/blue.css:3
    .icheckbox_square-blue,
    ^
    SyntaxError: Unexpected token .
        at Object.exports.runInThisContext (vm.js:76:16)
        at Module._compile (module.js:513:28)
        at Object.Module._extensions..js (module.js:550:10)
        at Module.load (module.js:458:32)
        at tryModuleLoad (module.js:417:12)
        at Function.Module._load (module.js:409:3)
        at Module.require (module.js:468:17)
        at require (internal/module.js:20:19)
        at Object.module.exports.Object.defineProperty.value (/home/nginx/domains/vps.manhhailua.com/public/build/webpack:/external "admin-lte/plugins/iCheck/square/blue.css":1:1)
        at __webpack_require__ (/home/nginx/domains/vps.manhhailua.com/public/build/webpack:/webpack/bootstrap f9baa6a0523f95e79945:19:1)
        at Object.<anonymous> (/home/nginx/domains/vps.manhhailua.com/public/build/webpack:/routes/login/Login.js:16:1)
        at __webpack_require__ (/home/nginx/domains/vps.manhhailua.com/public/build/webpack:/webpack/bootstrap f9baa6a0523f95e79945:19:1)
        at Object.module.exports.Object.defineProperty.value (/home/nginx/domains/vps.manhhailua.com/public/build/webpack:/routes/login/index.js:11:1)
        at __webpack_require__ (/home/nginx/domains/vps.manhhailua.com/public/build/webpack:/webpack/bootstrap f9baa6a0523f95e79945:19:1)
        at Object.module.exports.Object.defineProperty.value (/home/nginx/domains/vps.manhhailua.com/public/build/webpack:/routes/index.js:16:1)
        at __webpack_require__ (/home/nginx/domains/vps.manhhailua.com/public/build/webpack:/webpack/bootstrap f9baa6a0523f95e79945:19:1)
    /home/nginx/domains/vps.manhhailua.com/public/tools/runServer.js:71
            throw new Error(`Server terminated unexpectedly with code: ${ code } signal: ${ signal }`);
            ^
    
    Error: Server terminated unexpectedly with code: 1 signal: null
        at ChildProcess.server.once (/home/nginx/domains/vps.manhhailua.com/public/tools/runServer.js:53:15)
        at ChildProcess.g (events.js:286:16)
        at emitTwo (events.js:106:13)
        at ChildProcess.emit (events.js:191:7)
        at Process.ChildProcess._handle.onexit (internal/child_process.js:204:12)
    
    opened by manhhailua 7
  • babel-node syntax error

    babel-node syntax error

    When running in development babel-cli (babel-node) is throwing syntax error on CSS imports when I run my server.js. Any suggestion on how to solve this?

    `products.scss: Unexpected token (1:0)

    1 | .products { | ^ 2 | padding-top: 24px; 3 | padding-bottom: 24px;`

    I've seen the suggestion to wrap css in conditional requires based on ENV, but that doesn't seem to fit in with server side rendering concept that this plugin is solving.

    opened by BinahLinden 7
  • Use just client side?

    Use just client side?

    I just came across this project today and I'm not quite sure if this is something that can be used JUST client side (static-only site) to grab the critical path css?

    opened by rossthedevigner 6
  • What's the recommended approach for updating styles when client-side route updates

    What's the recommended approach for updating styles when client-side route updates

    Ok so let's say that our isomorphic app changes route and the client side router changed the view based on JavaScript. Is there an official approach to retrieving the CSS that was not already inserted in <head>?

    In other words:

    1. User navigates to the web site.
    2. Server side renderer returns HTML + CSS with help from isomorphic-style-loader
    3. Server also returns the JS bundle used to render views on the client side (a-la SPA style)
    4. User wants to go to the Account page. Account link is clicked.
    5. Client side router changes view to Account.
    6. The Account page is rendered on the client side and shown

    ok, here's my question 7. How should I retrieve the CSS needed for the Account page to show?

    One approach could be to use the ExtractTextPlugin, but that doesn't seem to work (because ExtractTextPlugin loader doesn't know how to call _getCss().

    Any ideas?

    Thanks a bunch

    opened by grgur 6
  • Correct error if 'insertCss' is not provided

    Correct error if 'insertCss' is not provided

    Fixes problems we are having running tests and storybook without having to add insertCss to the react context.

    Updated version of #63

    Fixes #60

    @frenzzy @koistya

    opened by VilliGunn 5
  • Hoist non-react statics

    Hoist non-react statics

    Hoist statics from component to higher-order-component.

    react-redux's connect does exactly this: https://github.com/reactjs/react-redux/blob/master/src/components/connect.js#L365

    opened by alexanderchr 5
  • Multiple withStyles only rendering the first

    Multiple withStyles only rendering the first

    I asked this on Gitter originally, but I'm struggling to get the following to work (example):

    import appStyles from './App.scss`;
    import otherStyles from '../OtherComponent/otherStyles.scss';
    
    ...
    
    export default withStyles(App, appStyles, otherStyles);
    

    This doesn't throw any errors, and both the styles are within the styles array, only the appStyles are loaded in.

    Is this the correct way of going about this?

    opened by Ehesp 5
  • import css does not return the classes

    import css does not return the classes

    Hi,

    When I do: import s1 from "./button.css";

    And then try: s1.button

    This returns undefined.

    When I console.log(s1) I get:

    {_getContent: ƒ, _getCss: ƒ, _insertCss: ƒ}
    _getContent: ƒ ()
    _getCss: ƒ ()
    _insertCss: ƒ (options)
    __proto__: Object
    

    button.css contains:

    .button {
      ...
    }
    

    In my webpack config I have:

      {
          test: /\.(css|scss)$/,
          use: [
              'isomorphic-style-loader',
              {
                  loader: "css-loader",
                  options: {
                      importLoaders: 1
                  }
              },
              'postcss-loader'
          ]
      }
    

    I'm really out of ideas of what I'm doing wrong. Thanks for your help! Karel

    opened by kbruneel 4
  • On refresh the classname hashing in css files is not loading for container divs

    On refresh the classname hashing in css files is not loading for container divs

    The classname hashing come wrong in js file. Eg.in scss file, the classname container compiles to abcW_ but in js file the imported classname for containers div element compiles to hhSab_.

    opened by sufyan0021 0
  • Is there any way to load styles conditionally?

    Is there any way to load styles conditionally?

    We have isomorphic react rendering for our application. We are trying to implement dark/light themes using user preferences. Is there any way we can load light.scss or dark.scss based on a flag/variable?

    Any help is appreciated. Thanks.

    "css-loader": "^2.1.1" "isomorphic-style-loader": "^5.1.0"

    opened by matheenpasha 1
  • Output hot module logics on demand

    Output hot module logics on demand

    According to the webpack docs https://webpack.js.org/api/loaders/#thishot

    we can know if HMR is enabled during COMPILE PHASE.

    The current code increase unnecessary codes in production. ( Hot Module Reload is never enabled in production)

    Hope this little change may help generating smaller output codes.

    I also found that refs variable is never used, so it could be removed safely.

    opened by xboy2012 0
Releases(v5.3.2)
Owner
Kriasoft
Here on GitHub we share our experience with web infrastructure, scalable web application design, and DevOps.
Kriasoft
Visual primitives for the component age. Use the best bits of ES6 and CSS to style your apps without stress 💅

Visual primitives for the component age. Use the best bits of ES6 and CSS to style your apps without stress ?? Looking for v5? The master branch is un

styled-components 38k Dec 27, 2022
A Simple Spinner and Loader Library for React

React-Spinner A Simple Spinner and Loader Library Have a look How to use? The NPM package is not released yet. You can clone the project and try out t

Venusha Dilshan Dushmantha 23 Nov 15, 2022
👩‍🎤 CSS-in-JS library designed for high performance style composition

emotion The Next Generation of CSS-in-JS Emotion 11 has been released ?? See the blog post Emotion is a performant and flexible CSS-in-JS library. Bui

emotion 15.9k Jan 5, 2023
🎨 Aesthetic is an end-to-end multi-platform styling framework that offers a strict design system, robust atomic CSS-in-JS engine, a structural style sheet specification (SSS), a low-runtime solution, and much more!

Aesthetic Start using Aesthetic now! aestheticsuite.dev Aesthetic is an end-to-end, multi-platform styling and design suite, that offers the following

Aesthetic 198 Nov 11, 2022
CSS-in-JS solution with style-to-state bindings, SSR, and a next-level developer experience.

A set of modules is for CSS-in-JS solution that includes state-to-style bindings, SRR and next-level developer experience. It includes a framework-agnostic implementation

Numl.Design 20 Nov 24, 2022
Style your React components with simplicity using css inside your comments.

Style your React components with simplicity using css inside your comments.

Charly Albin 1 Oct 12, 2021
Court.css - A naive CSS Utility framework based on JSX and css custom properties

court.css ?? A (work in progress) JSX-based, CSS utility framework with a predic

Tone Row 7 Aug 29, 2022
Autoprefixer for JavaScript style objects

inline-style-prefixer A small, simple and fast vendor prefixer from JavaScript style object. Support Us Support Robin Frischmann's work on Fela and it

Robin Weser 385 Nov 18, 2022
A tool for creating ratio-based style systems built in React.

Rational Design is a tool for generating type-scales for greater consistency in design systems or web design projects. It’s built with React Hooks, SCSS, Webpack, and npm scripts, and is fully responsive.

Timothy Merritt 4 Nov 16, 2022
A terminal style/styled portfolio website made with <3 using react.

A Terminal Styled Portfolio Website. ??‍??, a terminal style/styled portfolio website made with <3 using react.

ashish 67 Nov 22, 2022
Dynamic, theme-driven, style props for vanilla-extract.

Rainbow Sprinkles ?? Dynamic, theme-driven, style props for vanilla-extract Rainbow sprinkles works similarly to @vanilla-extract/sprinkles. Like spri

Wayfair Tech 90 Dec 23, 2022
Framework-agnostic CSS-in-JS with support for server-side rendering, browser prefixing, and minimum CSS generation

Aphrodite Framework-agnostic CSS-in-JS with support for server-side rendering, browser prefixing, and minimum CSS generation. Support for colocating y

Khan Academy 5.3k Jan 1, 2023
Tailwind CSS variants for email client CSS targeting.

tailwindcss-email-variants A plugin that provides variants for email client targeting hacks used in HTML emails. All variants are based on targeting h

Maizzle 23 Nov 3, 2022
Flexibly styled chat-widget for your react apps.

Flexibly styled chat-widget for your react apps. It was mainly created for integration with chat-bots in messengers.

Eddie Nubes 4 Nov 5, 2022
Repositório utilizado na criação de um setup para facilitar o inicio do desenvolvimento de apps com React, Next e TypeScript. Só "clonar" e "codar"! hahaha

Template para desenvolvimento de projetos utilizando REACT e NEXT com TypeScript Como executar Clone o projeto e acesse a pasta do mesmo. $ git clone

Adair Juneo 1 Jan 7, 2022
JSS is an authoring tool for CSS which uses JavaScript as a host language.

JSS A lib for generating Style Sheets with JavaScript. For documentation see our docs. Backers Support us with a monthly donation and help us continue

JSS 6.9k Jan 1, 2023
Seamless mapping of class names to CSS modules inside of React components.

React CSS Modules React CSS Modules implement automatic mapping of CSS modules. Every CSS class is assigned a local-scoped identifier with a global un

Gajus Kuizinas 5.3k Dec 28, 2022
CSS media queries in react - for responsive design, and more.

react-responsive Information Package react-responsive Description Media queries in react for responsive design Browser Version >= IE6* Demo The best s

contra 6.5k Jan 6, 2023
🏠 Airbnb website using React, Next.js and Tailwind CSS

This example shows how to use Tailwind CSS (v2.2) with Next.js. It follows the steps outlined in the official Tailwind docs.

Michele Picozzi 49 Dec 31, 2022