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
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
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/,
},
// ...
],
},
// ...
}
Usage:
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
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.
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
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.
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}
`;
Related
I am trying to import my jss file to React component (.jsx) and I have this error :
I looked through related questions and I tried all solutions, mostly about updating versions of material-ui. Here is my package.json file:
the way I write jss file :
import { makeStyles } from '#material-ui/styles';
const useStyles = makeStyles((theme) => ({
root:
{
margin: '10px'
}
}));
export default useStyles;
And jsx file :
import React from 'react';
import DateBar from './DateBar';
import Grid from '#material-ui/core/Grid';
import useStyles from './Board.jss';
export default function Board()
{
const classes = useStyles();
return(
...
Am I missing something or Do I need additional packages for using jsx and jss in React?
I have been trying all the solutions but didn't work..
Note: When I put the code inside of Jss into Jsx file, code works fine. Importing/Exporting might be an issue..
Edit: Still couldn't fix even though I created a new app and installed dependancies from the beginning..
I'm writing a React 16 application, I'm using material-ui components.
I import Button like this:
import Button from '#material-ui/core/Button';
just like it says in the official documentation: https://material-ui.com/components/buttons/
WebStorm show an error saying:
ESLint: '#material-ui/core/Button' import is restricted from being used. This loads CommonJS version of the package. To fix replace with: import { Button } from "#material-ui/core";(no-restricted-imports)
When I change the import to:
import {Button} from '#material-ui/core';
the message error goes away. The code works in both instances.
I want to know why I need to change the import for the error to disappear. why can't I just use the same code suggested by material-ui.com itself.
The template I'm using uses this .eslintrc.js
"use strict";
const fs = require("fs");
const path = require("path");
const restrictedPaths = [
{ name: "react-bootstrap" },
{ name: "#material-ui/core" }
].map(pkg =>
fs
.readdirSync(path.dirname(require.resolve(`${pkg.name}/package.json`)))
.map(component => ({
name: `${pkg.name}/${component}`,
message: `This loads CommonJS version of the package. To fix replace with: import { ${component} } from "${pkg.name}";`
}))
);
// TODO: Wait for https://github.com/facebook/create-react-app/pull/7036 to enable rules in react-scripts.
module.exports = {
extends: "eslint-config-react-app",
rules: {
// "no-script-url": "warn",
"jsx-a11y/anchor-is-valid": "warn",
"no-restricted-imports": ["error", { paths: [].concat(...restrictedPaths) }]
}
};
When you specify the full path to the file you want to import, then you import this file. When you use the import {Button} from '#material-ui/core', you let node choose the file it wants to use for this import. The package.json of a module may specify different entry points for a single #material-ui/core, depending on if working with CommonJS or ES modules.
I'm building a react component library with styled components. I have a <GlobalStyles /> component for projects consuming the library. In the SASS that I'm migrating from, I had a root index.scss file, within which I was doing:
#import '~bootstrap/scss/bootstrap';
I gather I cannot just put this in my global styles:
import { createGlobalStyle } from "styled-components"
const GlobalStyles = createGlobalStyle`
#import '~bootstrap/scss/bootstrap';
`
How can I make sure that any project including <GlobalStyles /> will also receive the bootstrap stylesheets?
You cannot import the bootstrap scss stylesheet without processing it for obvious reasons.
But you can import the bootstrap stylesheet. Unfortunately createGlobalStyle does not handle these imports as the underlying CSSDOM imports do not handle this well. The easiest implementation would be to import the bootstrap CSS files into your library like:
import 'bootstrap/dist/css/bootstrap.min.css';
But note that this will generate an extra CSS file that users of your library will have to include with something similiar to this too.
If you would want to create this dynamically, you could try adding this from a CDN.
import { useEffect } from "react";
function App() {
useEffect(() => {
const head = document.head;
const link = document.createElement('link');
link.href = `https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css`;
link.rel = `stylesheet`;
head.append(link);
}, []);
}
But consider the fact that these will have to be include on all components, but should not be included more than once. One way you can do this is by adding an ID to the link tag and querying the DOM before adding a new one.
I want to share my logic between react js and react native
here is my logic file. it is a HOC that wrap the design component.
I use recompose to create HOCs.
import {compose, withState} from 'recompose';
import FilterOptionsPostsPaginateRelay from './FilterOptionsPostsPaginateRelay';
import handlers from './handlers';
import {withRouter} from "next/router";
export default compose(
withRouter
FilterOptionsPostsPaginateRelay,
withState('loading', 'setLoading', false),
handlers,
)
All of the HOCs are sharable between the react js and react native but the withRouter that comes from next.js router
I want to conditional import {withRouter} from "next/router"; or import {withNavigation} from 'react-navigation'; in my logic file.
I know recompose has branch HOC that I can use but I want the condition checks at build time and prevent extra codes to my bundle and increase performance.
You can create next.config.js to custom webpack config to add alias for router.
module.exports = {
webpack: (config, { buildId, dev, isServer, defaultLoaders, webpack }) => {
// Note: we provide webpack above so you should not `require` it
// Perform customizations to webpack config
// Important: return the modified config
// Example using webpack option
config.plugins.push(new webpack.IgnorePlugin(/\/__tests__\//));
config.resolve.alias.router = process.env.IS_REACT_NATIVE ? 'react-navigation' : 'next/dist/client/router.js';
return config
},
};
then in your app, you need to use require to import router
const router = require('router');
const withRouter = router.withRouter || router.withNavigation;
I find a solution using babel-plugin-module-resolver
In web project I added following code to .babelrc
{
"plugins": [
[
"module-resolver",
{
"alias": {
"hybridRouter": "next/router"
}
}
]
]
}
In mobile project I added following code to .babelrc
{
"plugins": [
[
"module-resolver",
{
"alias": {
"hybridRouter": "react-navigation"
}
}
]
]
}
And in my logic files I used this like
import HybridRouter from 'hybridRouter';
Now HybridRouter point to next/router in web project and HybridRouter points to react-navigation in mobile project
You can have conditional imports using the require syntax. There is no need to mess with webpack, if you don't want to. Just do something like this in your case:
let withRouter;
try {
//This is expected to fail if next/router is not available
const router = require("next/router");
withRouter = router.withRouter;
} catch(ex){
const reactNav = require("react-navigation");
withRouter = reactNav.withNavigation;
}
// Use withRouter like you would normally use withRouter from next/router or withNavigation from react-navigation
I am learning how develop react native development and I am curious if you could put all the styles into one style.js file and then export it which allow all the components to be able to use the styles.
Yes you can. u can write styles in seperate file.
import React, { Component, PropTypes } from 'react';
import { StyleSheet } from 'react-native';
var styles = StyleSheet.create({
...
});
module.exports = styles;
Then import it in componnet class like
import styles from './styles'