Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to include styling in a component-like way #439

Closed
JarLowrey opened this issue Oct 6, 2017 · 8 comments
Closed

How to include styling in a component-like way #439

JarLowrey opened this issue Oct 6, 2017 · 8 comments

Comments

@JarLowrey
Copy link

JarLowrey commented Oct 6, 2017

I'm Submitting a ...

[ ] Bug report
[ ] Feature request
[ X ] Support request

Other than including the CSS globally in the head of the HTML document, how can we getting working in a react/component like way? I've tried using css-loader and styled-components without much luck.

@bjbrewster
Copy link

bjbrewster commented Oct 9, 2017

Hi

If you are using Webpack (I assume you are as you're using css-loader), you should be able to require/import react-datetime/css/react-datetime.css from a js file and Webpack will add it to your bundle or extracted CSS file like any other CSS that you import.

You can also import from within an SCSS file using @import "~react-datetime/css/react-datetime";. I have a global SCSS styles file in my project and am using this method (along with importing bootstrap-sass and misc global styles) as I import the react-datetime js file from several js components in my project.

I'm not happy about this in my project, so to reduce the dependencies on this date component, I am adding a helper DateTime component to my project that is just a wrapper for react-datetime and has the css import at the top of the js. So if I replace react-datetime or just want to change the default params for my project etc, I only have to update the one file. Something like:

import DateTime from 'react-datetime'
import 'react-datetime/css/react-datetime.css'

export default function DateTimeWrapper(props) {
  return <DateTime {...props} dateFormat="YYYY-MM-dd" timeFormat={false} />
}

If still having trouble, please post what build system you're using and a snippet of how you're importing the css and where.

Cheers,
Bren

@JarLowrey
Copy link
Author

So you're adding the wrapper in case you want to switch out this DateTime for another later on? Not sure if I understood that correctly.

So here's what my code looks like, styling is not applied.

import Datetime from 'react-datetime';
import 'react-datetime/css/react-datetime.css'

export default class EventSearchForm extends React.Component {
  render() {
    return (
      <div>
        <Datetime/>
      </div>
     );
}

If I instead use

import styles from 'react-datetime/css/react-datetime.css'
...
<Datetime className={styles.rdt} />

No improvements are made. Am I doing this wrong, or is there probably an issue with my Webpack/CSS loading dependencies not working?

@bjbrewster
Copy link

It seems like its a problem with your webpack setup.

Are you including other css/scss files ok or are you using some CSS in JS system?
Are you using the Webpack ExtractTextPlugin and not adding the output css file in your HTML?

Cheers,
Bren

@JarLowrey
Copy link
Author

I am using Webpacker 3.0 in a Rails 5.1 App. Just tried to integrate this Webpack configuration for my dev environment.js: rails/webpacker#756 . Unfortunately that doesn't seem to be working. I am using styled-components but also have css-loader installed locally. My package.json:

{
  "name": "asdf",
  "private": true,
  "dependencies": {
    "@rails/webpacker": "^3.0.1",
    "moment": "^2.18.1",
    "prop-types": "^15.6.0",
    "react": "^16.0.0",
    "react-datetime": "^2.10.3",
    "react-dom": "^16.0.0",
    "react-google-maps": "^8.3.4",
    "styled-components": "^2.2.1"
  },
  "devDependencies": {
    "babel-plugin-styled-components": "^1.2.1",
    "babel-preset-react": "^6.24.1",
    "css-loader": "^0.28.7",
    "webpack-dev-server": "^2.9.1",
    "webpack-merge": "^4.1.0"
  }
}

@JarLowrey
Copy link
Author

JarLowrey commented Oct 16, 2017

It was an issue with my Rails setup. Got the loader working here- rails/webpacker#775 (comment).

Now the issue is that import 'react-datetime/css/react-datetime.css' does not work, while

import styles from 'react-datetime/css/react-datetime.css'
...
<Datetime className={styles.rdt} />

does work but only on the top-level rdt class. Any ideas?

@bjbrewster
Copy link

bjbrewster commented Oct 17, 2017

From your list of dependencies, I think the problem is your webpack config's CSS module rule. You normally use css-loader with style-loader. Something like:

  module: {
    rules: [
      {
        test: /\.js$/,
        use: 'babel-loader',
        exclude: /(node_modules/
      },
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader']
      },
      ...
    ]
  }

This Stack Overflow post explains it well:
https://stackoverflow.com/questions/34039826/webpack-style-loader-vs-css-loader

css-loader just loads the css file as a module into your source but doesn't really do anything with it. style-loader takes the output of css-loader and wraps it in a block of code that inserts it into the HTML DOM's <head> in a <style> tag when that file is run.

You can also add sass-loader to the end of the css loaders and update the test to /\.s?css$/ to support SASS/SCSS styles. There's other loaders for Stylus and other CSS alternatives. Another helpful loader is postcss-loader along with autoprefixer that will automatically add browser vendor specific prefixes to all your css rules if they are needed (eg css transitions etc).

Instead of using style-loader, you can also extract all of the static CSS into a separate CSS bundle file if you use the Webpack ExtractTextPlugin. You will need to manually add this CSS to your Rail's project's layout file's <head> section like <link rel="stylesheet" href="/assets/bundle.css">. I used to prefer using ExtractTextPlugin to keep CSS out of my JS (reduced code size etc), but now I think it really doesn't matter. It's more convenient to leave it in and have one less thing to manage the deployed version etc.

If you are using some other CSS in JS library, they may have some other way to import static styles.

Cheers,
Bren

@JarLowrey
Copy link
Author

JarLowrey commented Oct 18, 2017

I'm learning this stuff so I had no idea there was a style loader too, thanks for the info + links! Is there anything I need to do to make style-loader insert into the <head>?

After checking I think style loader is already included. I believe the way it works in Rails is that it has default NPM dependencies and then you can add additional ones in package.json. When I print out the default 'style' loaders this is what I receive.

{ test: /\.(scss|sass|css)$/i,
  use:
   [ { loader: '/home/james/Documents/comity.fit/node_modules/extract-text-webpack-plugin/dist/loader.js',
       options: [Object] },
     { loader: 'style-loader' },
     { loader: 'css-loader', options: [Object] },
     { loader: 'postcss-loader', options: [Object] },
     { loader: 'resolve-url-loader' },
     { loader: 'sass-loader', options: [Object] } ] }

@bjbrewster
Copy link

bjbrewster commented Oct 18, 2017

Ok, as you said style-loader must already be installed from some other dependency and is already in the list of loaders for css etc and sass is already supported. The loaders are applied from last/bottom to top. You can't use style-loader and extract-text-webpack-plugin at the same time. I'm assuming you added the extract text webpack plugin but you will need to update the config similar to mine below, and add the bundle.css to your apps layout/page template. style-loader should have been working with the extract-text-webpack-plugin line so I'm confused why its not working. The styles should be embedded in the JS output bundle - you can search for them in the text to make sure.

I suggest using Facebook's https://github.com/facebookincubator/create-react-app to create an example app already setup to play around with config settings and compare with your own app.

npm i -g create-react-app
create-react-app my-test
cd my-test
npm run eject    # copies all webpack and other configs locally so you can see and configure them

I still recommend you just use style-loader if you can get it to work. Also, if you do use ExtractTextWebpackPlugin, it should only be used for your production build as it interferes with Webpack hot reloading of changed css files - this will slow down your development. ExtractTextWebpackPlugin has a disable property that you can use to turn it on or off but it can be easier to have separate Webpack configs, one for debug (with it removed altogether) and one for production, and run webpack like webpack --config path/to/webpack.config.prod.js.

This is my config that I simplified a bit for your reference. I haven't used Webpack in Rails so the above might not apply to you. I suggest you add an issue to https://github.com/rails/webpacker/issues and ask them for help adding this CSS file. Good luck getting style-loader working!

const path = require('path')
const autoprefixer = require('autoprefixer')
const ExtractTextPlugin = require('extract-text-webpack-plugin')

// Extract text plugin should only be used for production build
// My production build script sets NODE_ENV var to 'production' before calling webpack.  
// You can just set debug to true or false to try extract text plugin or styles-loader.   
const debug = process.env.NODE_ENV !== 'production'
const outputName = 'bundle'

const postCssLoader = {
  loader: 'postcss-loader',
  options: {
    plugins: [
      autoprefixer({
        browsers: ['last 2 versions']
      })
    ],
  }
}

module.exports = {
  entry: [
    // I add any required polyfills my app needs here. https://github.com/zloirock/core-js
    'core-js/fn/array/includes',
    'core-js/fn/object/assign',
    'core-js/fn/object/values',
    'core-js/fn/promise',
    'core-js/fn/string/includes',
    './src',   // this is the path to your app's main JS file
  ],

  output: {
    path: path.join(__dirname, 'dist'),
    filename: `js/${outputName}.js`,
  },

  externals: {
    'react': 'React',
    'react-dom': 'ReactDOM'
  },

  module: {
    rules: [
      {
        test: /\.js$/,
        use: 'babel-loader',
        exclude: /node_modules/
      },
      {
        test: /\.(scss|css)$/,
        use: ExtractTextPlugin.extract({
          fallback: 'style-loader', // this loader will be used when ExtractTextPlugin is disabled. See below 
          use: ['css-loader', postCssLoader,  'sass-loader'],
        })
      },
      {
        test: /\.(svg|png|jpg|jpeg|gif|woff)$/,
        use: 'url-loader',
      },
    ],
  },

  plugins: [
    new ExtractTextPlugin({
      filename: `css/${outputName}.css`,
      allChunks: true,
      disable: debug, // when disabled, ExtractTextPlugin will use style-loader. See above.
    }),
  ]
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants