15
votes

Is there a way to import a CSS file into a styled component?

One of my dependencies, React Quill Editor, is themeable by importing their CSS as a base and applying changes on top of it. All of my components are styled components, and I'd like to keep the CSS localized to the JS component rather than import the CSS as a 'global' style.

Right now I've take to copying their CSS into my own file in the following form.

I've written a brief example below.

/** editorCSS.js **/
import { css } from 'styled-components';
export default css`
/* copied CSS here */

.class-to-extend {
   color: green;
}
`


/** EditorComponent.js **/ 
import styled from 'styled-components';
import ReactQuill from 'react-quill';
import editorCSS from './editorCSS';

const StyledReactQuill = styled(ReactQuill)`
    ${editorCSS}
    /** Additional customization if necessary (e.g. positioning) */
`
export default StyledReactQuill;
`

I'd much rather import reference their css file in the scope of the styled component vs copying it.

If I do import ReactQuillCSS from 'react-quill/dist/quill.snow.css'; it will still apply my css globally due to the css-loader plugin.

Best, Daniel

6

6 Answers

7
votes

You could use raw-loader to load the quill.snow.css stylesheet and then include it in your styled component.

/** EditorComponent.js **/ 
import styled from 'styled-components';
import ReactQuill from 'react-quill';
import quillCSS from '!!raw-loader!react-quill/dist/quill.snow.css';

const StyledReactQuill = styled(ReactQuill)`
    ${quillCSS}
    /** Additional customization if necessary (e.g. positioning) */
`
export default StyledReactQuill;

Per the raw-loader docs, you can use !! to prevent the styles being added globally via css-loader

Adding !! to a request will disable all loaders specified in the configuration

5
votes

You can add a module rule to import styles locally from a CSS file into a styled component.

E.g. import all third party .css files from node_modules as raw string, others as usual:

// webpack.config.js
const config = {
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ["style-loader", "css-loader"], // load project styles via style-loader
        exclude: /node_modules/, 
      },
      {
        test: /\.css$/,
        use: ["to-string-loader", "css-loader"], // use to-string-loader for 3rd party css
        include: /node_modules/,
      },
      // ...
    ],
  },
  // ...
}
import styled from 'styled-components';
import ReactQuill from 'react-quill';
import ReactQuillCSS from 'react-quill/dist/quill.snow.css' // no custom webpack syntax

const StyledReactQuill = styled(ReactQuill)`
    ${ReactQuillCSS}
    // ... other styles
`

Don't forget to install to-string-loader, if not used yet.


This has some advantages over @jonathanhculver's solution:

  • One central config file determines, how to process .css files
  • Follow Webpack recommendations:

    Use module.rules whenever possible, as this will reduce boilerplate in your source code and allow you to debug or locate a loader faster if something goes south. (docs)

  • Avoid ESLint errors - take a look at the Codesandbox demo

  • css-loader can still resolve @import and url() for external CSS files, raw-loader won't
1
votes

In order to achieve that you would need to use a different loader than css-loader. You could write an different loader which prepares it for styled-components rather than adding it to the global style sheet.

If you need css-loader, you would however need to define which css files are handled by it and which ones are loaded for styled-components, which makes it not really practical imho.

0
votes

This is how I did:

Code

import React, { Component } from "react";
import ReactDOM from "react-dom";
import Styled from "styled-components";

const fetchStyles = () =>
  fetch(
    "https://gist.githubusercontent.com/bionicvapourboy/61d3d7a8546cb42e0e3194eb9505f48a/raw/5432218dd83320d53d1cbc2f230b81c765183585/style.css"
  ).then(response => response.text());

class App extends Component {
  state = {
    style: ""
  };

  componentDidMount() {
    fetchStyles().then(data => this.setState({ style: data }));
  }

  Wrapper = () => Styled.div`
    ${this.state.style}
  `;
  render() {
    const Wrapper = this.Wrapper();
    return (
      <div className="App">
        <Wrapper>
          <h1>Styled</h1>
        </Wrapper>
        <h1>Not Styled</h1>
        <h2>Start editing to see some magic happen!</h2>
      </div>
    );
  }
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

Demo

https://codesandbox.io/s/yw7xmx6x3x

-1
votes

As far as I know there is no way to import regular CSS just in scope. The way I have combined styled-components with CSS from libraries so far is to give your own styled component a className in the jsx.

const MyStyledComponent = styled(ComponentFromLibrary)`
    color: red;
`;


// and in the render function

return (
    <MyStyledComponent className="libraryClassname" />
);

Another example can be found in the official documentation: https://www.styled-components.com/docs/advanced#existing-css

What you are proposing would work if editorCSS is just a string of the styles you want to apply to the component.

-1
votes

From my understanding styled-components is just essentially template literals anyways.

Your only issue should be that the imported css is defined as an object when you try to place it into your components css. Have you tried typecasting it to a string first?

/** EditorComponent.js **/ 
import styled from 'styled-components';
import ReactQuill from 'react-quill';
import editorCSS from './editorCSS';

const newCSS = editorCSS.toString();

export default Styled(ReactQuill)`
  ${newCSS}
`;