Addng css files in React project with Webpack 4 - reactjs

I am building a simple React app from scratch using Webpack.
I was finally able to run the dev server and when I tried to apply some styles via CSS, the files wouldn't load and I assume my Webpack 4 configuration is not right.
Here is the code:
webpack.config.js
// const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const path = require('path');
module.exports = {
mode: 'development',
entry: ['./src/index.js'],
resolve: {
extensions: [".js", ".jsx"]
},
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'dist'),
publicPath: '/dist',
},
devServer: {
contentBase: path.join(__dirname, 'dist'),
compress: true,
port: 8080
},
module: {
rules: [
{
test: /\.js|.jsx$/,
exclude: /node_modules/,
loader: "babel-loader",
options: {
presets: ["react"]
}
},
{
test: /\.css$/,
use: ["style-loader", "css-loader"]
},
{
test: /\.(png|jpg)$/,
loader: 'url-loader'
}
]
},
};
My project structure is like this, I will include only src because it lives in the root:
**src**
|_assets
|_componentns
|_styles
|_App.css
|_index.css
App.jsx
index.js
index.html
I would like to be able to add multiple css files for each component I have and apply them, and to be able to style the index the index.html.
Thank you very much for your help.

Your webpack configuration looks fine.
make sure you import the required css files in your components.
import "./App.css";
class App extends Component {
render() {
return (
<div>
<Child />
</div>
);
}
}

All you have to do is import the CSS files when needed as you would a JavaScript module. So if you want to have a style sheet for your whole application, you can import a global stylesheet in your index.js.
import './styles/index.css';
and you can do the same for each component with specific styles
import './styles/App.css'
in which case you might want to setup CSS modules to avoid overlapping class names.

Ok, rookie mistake here, the way I ahve set up webpack is I have to build it first and then run the dev server, no the other way around.
All answers above are valid and helpful, I just forgot to run build after changes.

Related

mini-css-extract-plugin: Import CSS files ONLY when importing JS files

I'm making a tree shaken component library in React with webpack#5.24.2 and mini-css-extract-plugin#1.3.9 that has to work for client side rendering and server side rendering. I want to be able to do the following, create "N" components in the library, each with their specific "SCSS" files, something like this:
// KhalComponent.tsx
import React from 'react';
import './KhalComponent.scss';
const KhalComponent = () => {
return <div className="khal-component">Khal Component</div>;
};
export default KhalComponent;
// KhalComponent.scss
.khal-component {
padding: 2rem;
background: red;
}
and when an external project does the following:
import KhalComponent from 'some-library/KhalComponent' it gets the component along with its styles. I have this working with style-loader but I've read that this isn't a correct configuration for production AND it DOESN'T work with SSR (using NextJS) since with style-loader it tries to inject the styles adding them with document.createElement (document doesn't exist in the server side). So I tried migrating to using mini-css-extract-plugin to achieve this client side AND server side. I see that mini-css-extract-plugin does create the separate CSS files but (as far as I can tell) it doesn't let the corresponding JS file (KhalComponent.js) know that it NEEDS to load the CSS whenever KhalComponent is imported in another project.
I'm not generating an HTML file in this external component library since its just to import components JS and CSS content.
The webpack config is the following:
const path = require('path');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const entries = require('./entries');
const LIB_NAME = 'lib';
module.exports = {
entry: entries,
output: {
filename: '[name].js',
path: path.resolve(__dirname, LIB_NAME),
},
plugins: [new CleanWebpackPlugin(), new MiniCssExtractPlugin()],
module: {
rules: [
{
test: /\.s?css$/,
use: [MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader'],
},
{
test: /\.jsx?$/,
exclude: /node_modules/,
use: ['babel-loader'],
},
],
},
externals: [
{
react: 'react',
'react-dom': 'react-dom',
},
],
resolve: {
extensions: ['.js', '.jsx', '.ts', '.tsx'],
},
devtool: 'source-map',
devServer: {
contentBase: './lib',
hot: true,
},
};
and entries.js is:
const path = require('path');
const SRC_DIR = path.resolve('src');
module.exports = {
// Components
KhalComponent: path.join(SRC_DIR, 'KhalComponent'),
LinkComponent: path.join(SRC_DIR, 'LinkComponent'),
};
What am I missing or what tools should I use to do this? Thank you very much in advance!
I did a lot of research for this and the best solution I came up with was to just use mini-css-extract-plugin to separate out the css bundle (as I want to give user the functionality to add custom css in the form of styles) and port all imports through a parent index.js file.
Directory would look something like this:
|-build/
|--main.min.js
|--main.min.css
|-index.js
And your index.js content would look something like this:
export {default} from './build/main.min.js';
export * from './build/main.min.js';
import './index.css';
Hope it helps...

React component via custom npm package. module cannot be found

I am trying to create a custom npm package that will allow me to import some of my components over multiple projects. I wrote a simple package yesterday which can be found here: demo npm package. It's a simple starter project that has a webpack config and a uses npx babel to transpile and copy the files to the dist and lib folder.
If I include this package into my own project it works but not as I would expect. when I use the following code:
import {NavBar, HelloLib} from "testprivatenprodney;
It gives an error "Module not found".
when I use
import { NavBar, HelloLib } from "testprivatenprodney/lib/HelloLib";
it works as long as the navBar component does not have any child components. If it has I get "Module not found" error again.
I think I am missing something in my webpack configuration. yet all I can find is to have the resolve array, which is included.
const webpack = require("webpack");
module.exports = {
devtool: "source-map",
entry: "./src/index.js",
output: {
path: __dirname + "/dist",
publicPath: "/",
filename: "bundle.js"
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: ["babel-loader"]
},
{
test: /\.css$/,
loaders: ["style-loader", "css-loader"]
}
]
},
resolve: {
extensions: [".js", ".jsx"]
},
plugins: [
new webpack.DefinePlugin({
"process.env": {
NODE_ENV: JSON.stringify("production")
}
})
]
};
any help would be much appreciated.

Webpack: Can I get a source map for after babel, before minification?

I have a fairly basic webpack setup that runs babel and out comes my minified js with a source map.
Now when I run my source map in chrome I get the js before babel and before minification. However I would often like to have my source map after babel but before minification. Is this possible?
TL;DR I want source map to post-babel pre-minifcation. Possible?
For completeness
I run babel-loader 8 with webpack 4
Here is a screenshot from chrome showing the problem. As you can see the Dropzone tag indicates this is jsx (and so before babel)
Secondly here is my webpack config (not that it actually matters for my question).
const path = require('path');
module.exports = {
context: path.join(__dirname, 'Scripts', 'react'),
entry: {
client: './client'
},
output: {
path: path.join(__dirname, 'Scripts', 'app'),
filename: '[name].bundle.min.js'
},
module: {
rules: [
{
test: /\.jsx?$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
plugins: [require('#babel/plugin-proposal-object-rest-spread')],
presets: ["#babel/es2015", "#babel/react", "#babel/stage-0"]
}
}
}
]
},
resolve: {
extensions: ['.js', '.jsx']
},
externals: {
// Use external version of React (from CDN for client-side, or
// bundled with ReactJS.NET for server-side)
react: 'React'
},
devtool: 'source-map'
};
Running webpack with -d gives a second set of source maps in chrome that does the trick.

Webpack bundle JS without React or React Dom

I'd like to use webpack to bundle my JS together for a React component but without including React or React Dom, just my own React code.
The idea is so that I can load React or React Dom separately on the webpage with RequireJS as I'm adding the component to Magento2 which uses this approach for adding JS dependencies. My thought being that if I add another component this way, I don't want to be adding the React Libraries twice.
I think I need to use a different babel loader or pass in some other options?
Here is my webpack.config
const path = require('path');
const autoprefixer = require('autoprefixer');
const webpack = require('webpack');
module.exports = {
devtool: 'cheap-module-source-map',
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'web'),
filename: 'js/react-component.js',
publicPath: ''
},
resolve: {
extensions: ['.js', '.jsx']
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader'
}
]
},
plugins: [
new webpack.optimize.UglifyJsPlugin()
]
}
It works but as I said it includes the full react libraries in the generated js/react-component.js file. Any advice how I can do this?
As #Aaqib pointed out I needed to add this config options to webpack.
externals : {
react: 'react',
reactDom: 'react-dom'
}

ts-loader / css-loader not being able to import/resolve file

Trying to add css modules using style-loader and css-loader. Having a hard time figuring this out. I'm also not sure whether it's ts-loader to blame or css-loader.
webpack.config.js
const path = require('path');
module.exports = env => {
return {
devtool: "inline-source-map",
entry: "./src/index.tsx",
output: {
path: path.resolve(__dirname, "/public"),
filename: "build/app.js"
},
resolve: {
extensions: [".ts", ".tsx", ".js", ".json"],
},
module: {
rules: [
{
test: /\.tsx?$/,
loader: "ts-loader",
},
{
test: /\.css$/,
loader: 'style!css-loader?modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]'
}
]
}
}
}
component
import styles from "./Main.css"; // TS2307: Cannot find module './Main.css'.
P.S. I tried using the extract-text-webpack-plugin, but that only messed up everything even more making the errors overwhelming
So since this doesn't seem like a popular problem I managed to find the solution. Hope this will help anyone who struggles with ts-loader + css-loader.
1) Add .d.ts file that handles .css extensions
// I put it in root, but could be anywhere
// <root>/defs.d.ts
declare module "*.css" {
var styles: { [key: string]: string };
export = styles
}
2) Since I use Webpack 3.x, change style to style-loader in webpack.config.js
module: {
rules: [
//...
{
test: /\.css$/,
loader: 'style-loader!css-loader?modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]'
}
]
}
3) Import styles as * in component file
// In Main.tsx
import * as styles from "./Main.css";
// Usage
<div className={styles.nameOfClass} />
4) In tsconfig.json add .d.ts file to the include part. In my case its...
"include": [
"src",
"./defs.d.ts"
],
Restart webpack-dev-server or whatever and it should be good to go (hopefully).
Happy coding!

Resources