Webpack bundle JS without React or React Dom - reactjs

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'
}

Related

How can I use webpack to build a package that I can use in another react app

My organization has a custom build datatable that is built with React, compiled and used in several other applications. One of those other applications is itself a React app. We recently updated our larger react app so that we're using the latest builds on several npm packages, and the datatable build stopped working within react. I decided to try building the datatable in webpack the same way to get them working again, but I can't seem to produce a file that builds the datatable react into something I can import as a local package.
Here's the webpack.config.js I'm using to build the datatable. Any advice would be very welcome.
const path = require('path');
const nodeExternals = require('webpack-node-externals');
module.exports = [
{
entry: './src/react/datatable/datatable-component.jsx',
externals: [nodeExternals()],
name: 'datatable-react',
output: {
path: path.resolve(__dirname, './dist/assets/js/datatable'),
filename: 'datatable.js',
chunkFilename: '[id].js',
publicPath: ''
},
mode: 'production',
resolve: {
extensions: ['.js', '.jsx']
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
loader: 'babel-loader',
exclude: /node_modules/
}
]
}
},
];

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...

Addng css files in React project with Webpack 4

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.

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.

React webpack create bundle with separated react

is possible to configure webpack to not compile react inside bundle file? I would like to have more react applications in one page and I would like to load react library only once to get smallest size. How to achieve this?
In same situation I created final bundle with all small "applications" + React bundled once.
// components/index.js (webpacks entry point)
require("expose?React!react");
require("expose?MarketProductListing!./MarketProductListing");
require("expose?ProductDetail!./ProductDetail");
require("expose?MarketSearch!./MarketSearch");
Then, I include bundled JS via <script/> tag.
<script src="js/components.bundle.js"></script>
Now, I can access React, MarketProductListing, ... components in JS and render them where needed.
As you see, I use expose-loader for Webpack.
Webpack config
var path = require('path');
var webpack = require('webpack');
const js_dir = path.join(__dirname, 'src');
module.exports = {
devtool: 'eval',
entry: {
components: path.join(js_dir, 'components'),
},
output: {
path: js_dir,
filename: '[name].bundle.js'
},
plugins: [
new webpack.ProvidePlugin({
'es6-promise': 'es6-promise',
'fetch': 'imports?this=>global!exports?global.fetch!whatwg-fetch'
})
],
module: {
loaders: [
{ test: /\.js$/, loaders: ['babel'] }
]
},
resolve: {
extensions: ['', '.js', '.json']
}
};

Resources