How to embed React App into another website - reactjs

I have an old website running on server x. Now a React-App has been developed, which is on server y.
The website should display the React-App.
I have searched and read several posts on the topic but with no success so far.
The only solution currently working is an iframe but we don't want this.
If I do
npm run
and inspect the source on the server hosting the React-App, there is the following:
...
<div id="react-reporting"></div>
<script src="/static/js/bundle.js"></script><script src="/static/js/0.chunk.js"></script><script src="/static/js/main.chunk.js"></script>
Basically I would like to take this HTML-part and put it into the old site. Is this possible and if yes how?

My approach actually works, but I missed these two scripts:
<script src="https://unpkg.com/react#16/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom#16/umd/react-dom.development.js" crossorigin></script>
So for testing locally it is:
<div id="react-reporting"></div>
<script src="https://unpkg.com/react#16/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom#16/umd/react-dom.development.js" crossorigin></script>
<script src="http://localhost:3000/static/js/bundle.js"></script><script src="http://localhost:3000/static/js/0.chunk.js"></script>
<script src="http://localhost:3000/static/js/main.chunk.js"></script>
I also approached an error regarding sockjs which was caused by the test-file, which imports the React App, was being opened as file:// not via http://
Edit
After some more tests, I found using webpack is the best solution - it makes the app accessible as a single file.
package.json
...
"scripts": {
"start": "webpack-dev-server --inline --hot",
"build": "webpack --config webpack.config.js"
},
...
webpack.config.js
const path = require("path")
const UglifyJsPlugin = require("uglifyjs-webpack-plugin")
const glob = require("glob")
module.exports = {
entry: ['#babel/polyfill','./src/index.js'],
output: {
filename: "bundle.js"
},
module: {
rules: [
{
test: /\.css$/,
loader: 'style-loader!css-loader'
},
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader'
}
],
},
optimization: {
minimizer: [new UglifyJsPlugin()]
}
}
polyfill is needed for compatibility with IE11
every file processed (starting with the entries) counts as module. If it matches the "test"-part of a rule, the rule is applied (except for the excludes).
.babelrc
{
"presets": ['#babel/react',
['#babel/preset-env', {
"targets": {
"browsers": ["last 2 versions", "ie >= 11"]
}
}]],
"plugins": ['#babel/plugin-proposal-class-properties']
}
plugin-proposal-class-properties is only needed because I have some static members in classes.
When running
npm start
the Webpack-Dev-Server will start and make the app accessible under http://localhost:8080/dist/bundle.js.
Code to embed the app in another site:
<div id="react-reporting"></div>
<script src="https://unpkg.com/react#16/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom#16/umd/react-dom.development.js" crossorigin></script>
<script src="http://localhost:8080/dist/bundle.js"></script>

If you are using create-react-app, go to the folder of your React App, and insert this command:
npm run build
This will generate a ./build folder with all that you need for your app.
Then it's simply a matter of dragging the ./build folder to server x.
You can also change the HTML that calls the react app so it will fit the old webpage.

Related

Specify URL path as a variable for a REACT SSR applications built manually (i.e. NOT using NextJS )

Note: I am not using NextJS for my SSR application. I also did not use create-react-app to create my application.
I have a React Server Side Rendered (SSR) application which was hand built (as apposed to using a tool like create-react-app). I use WebPack to bundle up the server side code and the client side code. I followed the excellent Udemy course https://www.udemy.com/course/server-side-rendering-with-react-and-redux/ to understand how to create a React SSR application
My Application
Application structure
webpack.base.config.js
module.exports = {
module: {
rules: [
{
test: /\.(png|jpe?g|gif)$/i,
use: 'file-loader',
},
{
test: /\.(js|jsx)$/,
loader: 'babel-loader',
exclude: /node_modules/,
query: {
cwd: __dirname,
},
},
],
},
resolve: {
extensions: ['.js', '.jsx'],
},
};
webpack.client.config.js
const path = require('path');
const { merge } = require('webpack-merge');
const CopyPlugin = require('copy-webpack-plugin');
const baseConfig = require('./webpack.base.config.js');
const config = {
entry: './src/client/client.jsx',
output: {
filename: 'client-bundle.js',
path: path.resolve(__dirname, 'public'),
},
plugins: [
new CopyPlugin({
patterns: [
{
from: path.resolve(__dirname, 'src', 'styles'),
to: path.resolve(__dirname, 'public'),
},
],
}),
],
};
module.exports = merge(baseConfig, config);
webpack.server.config.js
const path = require('path');
const { merge } = require('webpack-merge');
const webpackNodeExternals = require('webpack-node-externals');
const baseConfig = require('./webpack.base.config.js');
const config = {
target: 'node',
entry: './src/server/server.js',
output: {
filename: 'server-bundle.js',
path: path.resolve(__dirname, 'build'),
},
externals: [webpackNodeExternals()],
};
module.exports = merge(baseConfig, config);
Routes
{
...Home,
path: '/',
exact: true,
},
{
...Page1,
path: '/page1',
exact: true,
},
Client Side Routing
<BrowserRouter>
...
</BrowserRouter>
Server Side Routing
<StaticRouter context={context} location={req.path}>
...
</StaticRouter>
Server Side generated HTML template
<html>
<head>
<link rel="stylesheet" type="text/css" href="styles.css">
</head>
<body>
<div id="root">${content}</div>
<script src="client-bundle.js"></script>
</body>
</html>
package.json scripts
"scripts": {
"start": "node build/server-bundle.js",
"build": "npm-run-all --parallel prod:build*",
"prod:build-server-bundle": "webpack --config webpack.server.config.js",
"prod:build-client-bundle": "webpack --config webpack.client.config.js",
"dev": "npm-run-all --parallel dev:*",
"dev:run-server": "nodemon --watch build --exec node build/server-bundle.js",
"dev:build-server-bundle": "webpack --config webpack.server.config.js --watch",
"dev:build-client-bundle": "webpack --config webpack.client.config.js --watch",
"lint": "eslint ./src --ext .js,.jsx"
},
Running my application
I run the application locally using
npm run dev
My application URLs are therefore
http://localhost:/
http://localhost:/page1
My Requirements
I would like my application to have a customizable URL path, for example "/a/b" so that my URLs would be
http://localhost:/a/b
http://localhost:/a/b/page1
or if my path is "xyz" my URLs would be
http://localhost:/xyz
http://localhost:/xyz/page1
How to i enable a custom base path in my React SSR Application.
What i tried
I hardcoded a path in my application in the HTML, and routers, i..e
<html>
<head>
<link rel="stylesheet" type="text/css" href="a/b/styles.css">
</head>
<body>
<div id="root">${content}</div>
<script src="a/b/client-bundle.js"></script>
</body>
</html>
<BrowserRouter basename="a/b/">
...
</BrowserRouter>
<StaticRouter context={context} location={req.path} basename="a/b/">
...
</StaticRouter>
But this does not work, going to either of the following
http://localhost
http://localhost/a/b
renders my home page with no stylesheet applied and no client side bundle. This is because neither of the following can be found and return a 404
http://localhost/a/b/styles.css
http://localhost/a/b/client-bundle.js
Furthermore, if i use a link to invoke the router, the URL for the styles and client-bundle has the path twice, i.e.
client side navigation to
http://localhost:8080/a/b/contact
means styles and client-bundle request urls are
http://localhost/a/b/a/b/styles.css
http://localhost/a/b/a/b/client-bundle.js
You can simply add an env variable basePath and then use that to set your routes.
Routes
{
...Home,
path: `${process.env.basePath}/`,
exact: true,
},
{
...Page1,
path: `${process.env.basePath}/page1`,
exact: true,
},
Now, if your basePath is '/a/b', your index component will be available on yourdomain/a/b/ and page1 will be available on yourdomain/a/b/page1
Hassaan Tauqir's post above which I have marked as the answer helped me refine the solution. Thank you to Hassaan.
package.json
Change the scripts for the PRODUCTION environment to have the BASE_URL specified.
"prod:build-server-bundle": "cross-env BASE_URL=/a/b webpack --config webpack.server.config.js",
"prod:build-client-bundle": "cross-env BASE_URL=/a/b webpack --config webpack.client.config.js",
Note: You have to use "cross-env" otherwise this will not work on every operating system, therefore I had to install "cross-env" first
npm install cross-env
I left the DEVELOPMENT scripts unchanged as I do not need a path when testing locally
"dev:build-server-bundle": "webpack --config webpack.server.config.js --watch",
"dev:build-client-bundle": "webpack --config webpack.client.config.js --watch",
webpack.base.config.js
Reading in BASE_URL
The "BASE_URL" is accessible in "webpack.base.config.js". I added code so that I can handle the "BASE_URL" being specified with a trailing slash or not.
// Set up the BASE_URL parameter, ensuring it does not have a trailing slash
let BASE_URL = '';
if (process.env.BASE_URL) {
BASE_URL = process.env.BASE_URL.toString().trim();
if (BASE_URL.substr(-1) === '/') {
BASE_URL = BASE_URL.substr(0, BASE_URL.length - 1);
}
}
publicPath
In the "module.exports" add the "output" section and add the "publicPath" setting. "publicPath" allows you to specify the base path for all the assets within your app, for example I have images which I reference in my application using the following code.
import myImage from '../images/myImage.png';
....
<img src={myImage } alt="myImage " />
....
"publicPath" must end in a trailing slash, therefore if we have a BASE_URL I append a / otherwise I leave it empty.
output: {
publicPath: (BASE_URL) ? `${BASE_URL}/` : '',
},
For more information on "publicPath" see https://webpack.js.org/guides/public-path/
webpack.DefinePlugin
In the "module.exports" add the "webpack.DefinePlugin" setting the environment variable to be passed through to the reset of the application
plugins: [
new webpack.DefinePlugin({
'process.env.BASE_URL': JSON.stringify(BASE_URL),
}),
],
For more information on "DefaultPlugin" see https://webpack.js.org/plugins/define-plugin/
Server Side Routing
Add the "basename" to the server side router, whose value is the variable specified in the "DefinePlugin" in the webpack config file
<StaticRouter context={context} location={req.path} basename={process.env.BASE_URL}>
...
</StaticRouter>
Client Side Routing
Add the "basename" to the client side router, whose value is the variable specified in the "DefinePlugin" in the webpack config file
<BrowserRouter basename={process.env.BASE_URL}>
...
</BrowserRouter>

best way to install react app manually

i am learning reactjs. And i install my react app by
create-react-app first
but i want to know is there any manual way to install react app
I will write the shortest tutorial :)
Step1: Create folder and file structure like below:
Step2: Install dependencies below:
npm install -S react react-dom prop-types
Step3: Install dev dependencies:
npm install -D babel-core babel-loader babel-plugin-transform-class-properties babel-preset-es2015 babel-preset-react html-webpack-plugin webpack
Step4: Add index.html file to root folder:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.3/css/bootstrap.min.css">
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<!--Mounting point for React VDOM-->
<div id="root"></div>
</body>
</html>
Step5: Create webpack.config.js file in root folder with content:
let path = require('path'),
webpack = require('webpack'),
HtmlWebPackPlugin = require('html-webpack-plugin')
const PATHS = {
src: path.join(__dirname, 'src'),
dist: path.join(__dirname, 'dist'),
main: path.join(__dirname, 'src/main.js')
}
let wpConfig = {
entry: PATHS.main,
output: {
path: PATHS.dist,
filename: 'build.js',
},
module: {
loaders: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
query: {
"presets": [
"es2015",
"react"
],
"plugins": [
"transform-class-properties"
]
}
}
]
},
plugins: [
new HtmlWebPackPlugin({
title: 'My First React App',
template: './index.html'
})
]
}
module.exports = wpConfig
Step6: Add nmp command. In package.json file go to "scripts" sections and add build command like below:
"scripts": {
"build": "node_modules/.bin/webpack"
}
Step7: Create this simple React app file (main.js) in src folder:
import React from 'react'
import ReactDOM from 'react-dom'
const App = () => {
return (
<div>
<h1>Hello,</h1>
<p>This is my first react app!</p>
</div>
)
}
ReactDOM.render(
<App />,
document.getElementById('root')
)
Step8: Run command:
npm run build
Webpack will build and save files(build.js and index.html) to dist folder. Open your /dist/index.html file in browser and your first react app from zero is ready! Start with this basic app, then add some additional features like stylesheets (css, sass), router, webpack dev-server, hot reloading and etc.Happy coding!

how to get webpack to bundle node_modules for angular 1

I am about to move angular 1 + typescript project build setup from gulp to webpack, the only part I am stuck with is, how to bundle node_modules js file in proper sequence.
I was till now using bower for client side dependencies, and gulp has wiredep plugin which will look into bower dependencies section + main section to build the dependency order and get it to bundle it properly.
Now I understand that webpack philosophy is different, we should depend upload whatever is imported rather than any dependencies section as such.
so to get it to work I will need to do: move all dependencies from bower.json to package.json
currently as I am using typings, tsc considers the typings and gives me the output, I really don't need to write imports for bower packages as such.
So if i understand correctly, to get it work with webpack,
a) I should get away with typings and then directly import the js
files inside all my ts files??
As I understand, all the js modules from npm do work with modules, so does webpack really needs typings files?
OR
b) I should write a separate vendor.ts, where I should maintain the
sequence of imports (for js and css) myself,
but then that would be little painful to do (given I am used to wiredep handling it for me).
But then this will allow me bundle the vendor files separately using chunks-plugin
OR
c) is there any other standard way to handle this.
This is kinda pain point to move angular 1 from gulp to webpack.
When you import a module from a TypeScript file that's going to be loaded by webpack, it'll get it's content from node_modules and bundle it into the output file.
index.ts
import * as angular from "angular";
const myApp = angular.module("myApp", []);
myApp.controller("MyController", function PhoneListController($scope) {
$scope.phones = [
{
name: "Nexus S",
snippet: "Fast just got faster with Nexus S."
}, {
name: "Motorola XOOMâ„¢ with Wi-Fi",
snippet: "The Next, Next Generation tablet."
}, {
name: "MOTOROLA XOOMâ„¢",
snippet: "The Next, Next Generation tablet."
}
];
});
index.html
<!DOCTYPE html>
<html ng-app="myApp">
<head>
...
<script src="bundle.js"></script>
</head>
<body ng-controller="MyController">
<ul>
<li ng-repeat="phone in phones">
<span>{{phone.name}}</span>
<p>{{phone.snippet}}</p>
</li>
</ul>
</body>
</html>
webpack.config.js
module.exports = {
entry: "./index.ts",
output: {
filename: "./bundle.js"
},
devtool: "source-map",
resolve: {
extensions: ["", ".webpack.js", ".web.js", ".ts", ".tsx", ".js"]
},
module: {
loaders: [
{ test: /\.tsx?$/, loader: "ts-loader" }
],
preLoaders: [
{ test: /\.js$/, loader: "source-map-loader" }
]
}
};
Add angular, #types/angular, ts-loader, source-map-loader, typescript to your packages.json and run webpack.
It'll bundle everything inside a single file named bundle.js which will be used by your index.html.

Can't set up React/Babel/Webpack

So I was trying to set up the React/Babel/Webpack environment but I had some trouble doing so. I started creating a new folder, did the npm init and then I followed everything in this tutorial: https://facebook.github.io/react/docs/package-management.html
First, I've installed webpack globally
I've created a index.js with the same content on the tutorial
I've created a .babelrc file with { "presets": ["react"] }
Ran the npm install --save react react-dom babel-preset-react babel-loader babel-core
Then, when I run the command webpack main.js bundle.js --module-bind 'js=babel-loader' it gives me an error: "Module parse failed ~ You may need an appropriate loader to handle this file type.
Any idea guys? I've literally copy and pasted every step from the tutorial and I am sure all the files are correct. Thanks in advice!
Create file webpack.config.js
var path = require('path');
var webpack = require('webpack');
module.exports = {
entry: './main.js',
output: { path: __dirname, filename: 'bundle.js' },
module: {
loaders: [{
test: /.js?$/,
loader: 'babel-loader',
exclude: /node_modules/
}]
},
};
Run
webpack
and it will generate bundle.js for you.
Now make sure you have added index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Hello React!</title>
</head>
<body>
<div id="example"></div>
<script src="bundle.js"></script>
</body>
</html>
Looks like you accessing webpack from global. You might have installed webpack by doing
npm install -g webpack
Now,
Install webpack locally,
npm install webpack
and run.
./node_modules/webpack/bin/webpack.js main.js bundle.js --module-bind 'js=babel-loader'

grunt bowerInstall not working

I'm setting up a new AngularJS project (first time for me) and I'm finding it very touchy... Latest issue is getting bower to properly configure things in my index.html file.
If I just hardcode things to the googleapis, it all works just fine (example in the index.html file).
If I setup bower and do a grunt bowerInstall, it adds what look to be the correct lines in my index.html but they don't work at all. I get errors like:
Resource interpreted as Script but transferred with MIME type text/html: "http://localhost:8081/bower_components/angular/angular.js".
and
Uncaught SyntaxError: Unexpected token < for the angular files.
So far bower has been a royal pain... any ideas what's going wrong here? Thanks!
BTW, the simple app is working as expected and I've gotten some basic karma tests working.
bower.json:
{
"name": "meanjs_book",
"version": "0.1.0",
"homepage": "https://github.com/JESii/xxx",
"authors": [
"Jon Seidel <jseidel#edpci.com>"
],
"description": "Rudimentary app from MeanJS Book",
"main": "server.js",
"moduleType": [
"node"
],
"keywords": [
"MongoDB",
"Express",
"AngularJS",
"Node",
"HighPlans"
],
"license": "MIT",
"private": true,
"ignore": [
"**/.*",
"node_modules",
"bower_components",
"test",
"tests",
"spec"
],
"dependencies": {
"angular-route": "~1.3.15",
"angular": "~1.3.15",
"angular-animate": "~1.3.15",
"angular-mocks": "~1.3.15"
}
}
Gruntfile
module.exports = function(grunt) {
// Project configuration.
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
uglify: {
options: {
banner: '/*! <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> */\n'
},
build: {
src: 'src/<%= pkg.name %>.js',
dest: 'build/<%= pkg.name %>.min.js'
}
},
bowerInstall: {
target: {
// Point to the files that should be updated when
// you run `grunt bowerInstall`
src: ['public/app/views/index.html'], // index.html support
// Optional:
// ---------
cwd: '',
dependencies: true,
devDependencies: false,
exclude: [],
fileTypes: {},
ignorePath: '',
overrides: {}
}
}
});
// Load the plugin that provides the "uglify" task.
grunt.loadNpmTasks('grunt-contrib-uglify');
// Default task(s).
grunt.registerTask('default', ['uglify']);
grunt.loadNpmTasks('grunt-bower-install');
};
index.html:
<!-- AngularJS -->
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.min.js" type="text/javascript" charset="utf-8"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular-route.js" type="text/javascript" charset="utf-8"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular-animate.js" type="text/javascript" charset="utf-8"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular-mocks.js" type="text/javascript" charset="utf-8"></script>
<!-- bower:js -->
<!-- <script src="../../../bower_components/angular/angular.js" type="text/javascript"></script> -->
<!-- <script src="../../../bower_components/angular-route/angular-route.js" type="text/javascript"></script> -->
<!-- <script src="../../../bower_components/angular-animate/angular-animate.js" type="text/javascript"></script> -->
</script> -->
I'm assuming you're getting that error when running grunt serve. The console message you pasted indicates that grunt isn't allowing JS files through. If a particular file type isn't allowed, it's replaced with index.html which is why it was transferred with MIME type HTML. Where did you get that Gruntfile from?
Additionally, I recommend using Yeoman to scaffold a basic Angular app. Check it out here: http://yeoman.io/codelab.html. It's very easy to follow and will make you more comfortable with using tools like Grunt & Bower.

Resources