score:90
currently, i am doing something similar.
the application is not a full react app, i am using react for dynamic stuff, like commentbox, which is autark. and can be included at any point with special params..
however, all my sub apps are loaded and included into a single file all.js
, so it can be cached by the browser across pages.
when i need to include an app into the ssr templates, i just have to include a div with the class "__react-root" and a special id, ( the name of the react app to be rendered )
the logic is really simple:
import commentbox from './apps/commentbox';
import otherapp from './apps/otherapp';
const apps = {
commentbox,
otherapp
};
function renderappinelement(el) {
var app = apps[el.id];
if (!app) return;
// get props from elements data attribute, like the post_id
const props = object.assign({}, el.dataset);
reactdom.render(<app {...props} />, el);
}
document
.queryselectorall('.__react-root')
.foreach(renderappinelement)
<div>some article</div>
<div id="commentbox" data-post_id="10" class="__react-root"></div>
<script src="/all.js"></script>
edit
since webpack perfectly supports code-splitting & lazyloading, i thought it make sense to include an example where you don't need to load all your apps in one bundle, but split them up and load on demand.
import react from 'react';
import reactdom from 'react-dom';
const apps = {
'one': () => import('./one'),
'two': () => import('./two'),
}
const renderappinelement = (el) => {
if (apps[el.id]) {
apps[el.id]().then((app) => {
reactdom.render(<app {...el.dataset} />, el);
});
}
}
score:-2
i suggest you take a look at inertiajs: https://inertiajs.com/
with inertia you build apps just like you've always done with your server-side web framework of choice. you use your framework's existing functionality for routing, controllers, middleware, authentication, authorization, data fetching, and more.
the only thing that's different is your view layer. instead of using server-side rendering (eg. blade or erb templates), the views are javascript page components. this allows you to build your entire front-end using react, vue or svelte.
score:0
i revive this old question since i was in the same situation, without finding an answer that could satisfy my needs. so, based on @webdeb 's answer, i wrote a mini-framework that use cra (without eject) to inject as many components you want in any html page while preserving all the cra's benefits.
tl;dr
you can check my public repo here that contains all the needed files and a link to a medium article where i thoroughly explain all this stuff.
the general idea
the trick is to install cra as you normally would, and update the index.js
file as follows :
import react, { suspense } from 'react';
import reactdom from 'react-dom';
import './index.css';
import reportwebvitals from './reportwebvitals';
//list here all the components that could be inserted in a web page
const apps = {
'app': react.lazy(() => import('./app')),
'testcomponent1': react.lazy(() => import('./testcomponent1')),
'testcomponent2': react.lazy(() => import('./testcomponent2')),
}
//event manager to communicate between the components
const bridgeevent = new eventtarget();
//common fallback for all the components
function fallback() {
return <div>loading...</div>;
}
const renderappinelement = (el) => {
if(apps[el.dataset.reactcomponent] && !el.dataset.rendered){
//get the component's name stored in the data-react-component attribute
const app = apps[el.dataset.reactcomponent];
//render the component, inject all the html attributes and the event bridge
reactdom.render(
<suspense fallback={<fallback />}>
<app {...el.dataset} bridgeevent={bridgeevent}/>
</suspense>
, el);
el.dataset.rendered = true;
}
else if(el.dataset.rendered){
console.log('el', el, 'is already rendered')
}
}
//only for the dev phase
const rootel = document.getelementbyid('root');
//generate components without attributes
if(process.env.react_app_render_cmp){
const components = process.env.react_app_render_cmp.split(',');
components.foreach(item => {
const componentel = document.createelement('div');
componentel.setattribute("data-react-component", item);
componentel.classname = "__react-cmp";
rootel.append(componentel);
});
}
//generate components with attributes
if(process.env.react_app_render_cmp_with_attrs){
let componentswithattrs;
try{
componentswithattrs = json.parse(process.env.react_app_render_cmp_with_attrs);
}
catch(e){
console.log('fail to parse react_app_render_cmp_with_attrs', e);
}
if(componentswithattrs){
componentswithattrs.foreach(cmp => {
const componentel = document.createelement('div');
componentel.setattribute("data-react-component", cmp.class);
componentel.classname = "__react-cmp";
object.keys(cmp.data).foreach(attrkey => {
componentel.setattribute(attrkey, cmp.data[attrkey]);
});
rootel.append(componentel);
});
}
}
//the default name of the global object is reactcomponents, but it could be customized via the react_app_namespace environment variable
const appnamespace = process.env.react_app_namespace || "reactcomponents";
window[appnamespace] = {
ready: false,
parsecomponents(container){
//parse the container or the whole document and inject all the components in the containers that have a "__react-cmp" class
(container || document)
.queryselectorall('.__react-cmp')
.foreach(renderappinelement);
}
}
window[appnamespace].parsecomponents();
window[appnamespace].ready = true;
//if dynamic parsing must be done via the window.reactcomponents.parsecomponents() method
//check the availability of window.reactcomponents object via window.reactcomponents.ready property
//or define a window.reactcomponentsasyncinit() method to be notified of the availability
if(typeof window[`${appnamespace}asyncinit`] === 'function'){
window[`${appnamespace}asyncinit`]();
}
// if you want to start measuring performance in your app, pass a function
// to log results (for example: reportwebvitals(console.log))
reportwebvitals();
then you can add react_app_render_cmp
and/or react_app_render_cmp_with_attrs
environment variables to test your components while using the cra's development server. your .env.development.local
file could look like:
#this will render the testcomponent1 and testcomponent2 without any attributes
react_app_render_cmp="testcomponent1,testcomponent2"
#this will render testcomponent1 with the data-test-attribute attribute set to "test attribute value"
react_app_render_cmp_with_attrs="[{"class":"testcomponent1","data":{"data-test-attribute":"test attribute value"}}]"
after building your files, you should have your index.html
file with all the .js
and .css
files you need to include in each page of your multi-page app that should load your react components. don't forget to add inline_runtime_chunk=false
in your .env
file to avoid any inline javascript!
then, add the components' containers in the html pages where you want them to show. for instance:
<div class="__react-cmp" data-react-component="testcomponent1"></div>
the parsecomponents()
declared in the cra's index.js
file should be executed, grabbing your div
with the .__react-cmp
class, then use it as a container for your testcomponent1
react component.
in the dedicated repo and article i explain how you could change your build path with the cra's build_path
environment variable (so you can host your built files in your server or in a cdn) and i provide a loader that will parse the built index.html
file and dynamically insert all the needed .js
and .css
files in your page (so you just have to include the loader, instead of all the files). here is how the loader looks like, assuming its file name is cmp-loader.js
and hosted next to your built index.html
file:
(async () => {
const head = document.getelementsbytagname('head')[0];
const scriptsrcregexp = new regexp('<script.*?src="(.*?)"', 'gmi');
//get the exact script's src as defined in the src attribute
const scriptsrc = scriptsrcregexp.exec(document.currentscript.outerhtml);
//all the resources should be relative to the path of this script
const resourcespath = (scriptsrc && scriptsrc.length > 1) ? scriptsrc[1].replace('cmp-loader.js', '') : '';
//get the index content
const indexhtml = await (await fetch(resourcespath+'index.html', {cache:'reload'})).text();
//assume that all the .js and .css files to load are in the "static" folder
const reactcssregexp = new regexp(`<link href="${resourcespath}static\/css\/(.*?)\.css" rel="stylesheet">`, 'gm');
const reactjsregexp = new regexp(`<script (.*?) src="${resourcespath}static\/js\/(.*?)\.js"><\/script>`, 'gm');
//grab all the css tags
const reactcss = [].concat(indexhtml.match(reactcssregexp)).join('');
//grab all the js tags
const reactjs = [].concat(indexhtml.match(reactjsregexp)).join('');
//parse and execute the scripts
const scriptsdoc = new domparser().parsefromstring(reactjs, 'text/html');
array.from(scriptsdoc.getelementsbytagname('script')).foreach(item => {
const script = document.createelement('script');
[...item.attributes].foreach(attr => {
script.setattribute(attr.name, attr.value)
})
head.appendchild(script);
});
//inject the css
head.insertadjacenthtml('beforeend', reactcss);
})().catch(e => {
console.log('fail to load react-cmp', e)
});
score:1
i know it's been a while since this question was asked but hopefully this helps someone.
as @cocomico mentioned you could provide several entry points for the application in the webpack.config.js file. if you are looking for a simple webpack setup (based on the idea of multiple entry points) that allows you to add react components to static pages you may consider using this: https://github.com/przemek-nowicki/multi-page-app-with-react
score:12
are you using a cms? they tend to like changing urls which could break your application.
another way is using something like react habitat.
with it, you can register components and they automatically get exposed to the dom.
example
register component(s):
container.register('animalbox', animalbox);
container.register('animalsearchbox', animalsearchbox);
then they are availiable in your dom like this:
<div data-component="animalbox"></div>
<div data-component="animalsearchbox"></div>
the above will be automatically replaced with your react components.
you can then automatically pass properties (or props) to your components too:
<div data-component="animalbox" data-prop-size="small"></div>
this will expose size
as a prop to your component. there are additional options for passing other types such as json, array's, ints, floats etc.
score:32
i'm building an application from the ground up and am learning as i go, but i think what you are looking for is react-router. react-router maps your components to specific urls. for example:
render((
<router>
<route path="/" component={app}>
<route path="api/animals" component={animals}>
<route path="birds" component={birds}/>
<route path="cats" component={cats}/>
</route>
</route>
<route path="api/search:term" component={animalsearchbox}>
</router>
), document.body)
in the search case, 'term' is accessible as a property in the animalsearchbox:
componentdidmount() {
// from the path `/api/search/:term`
const term = this.props.params.term
}
try it out. this tutorial is the one that put me over the top in terms of my understanding of this and other related topics.
original answer follows:
i found my way here looking for the same answer. see if this post inspires you. if your application is anything like mine, it will have areas that change very little and varies only in the main body. you could create a widget whose responsibility it is to render a different widget based upon the state of the application. using a flux architecture, you could dispatch a navigation action that changes the state your body widget switches upon, effectively updating the body of the page only.
that's the approach i'm attempting now.
score:55
you can provide several entry points for the application in the webpack.config.js file:
var config = {
entry: {
home: path.resolve(__dirname, './src/main'),
page1: path.resolve(__dirname, './src/page1'),
page2: path.resolve(__dirname, './src/page2'),
vendors: ['react']
},
output: {
path: path.join(__dirname, 'js'),
filename: '[name].bundle.js',
chunkfilename: '[id].chunk.js'
},
}
then you can have in your src folder three different html files with their respective js files (example for page1):
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>page 1</title>
</head>
<body>
<div id="app"></div>
<script src="./vendors.js"></script>
<script src="./page1.bundle.js"></script>
</body>
</html>
javascript file:
import react from 'react'
import reactdom from 'react-dom'
import app from './components/app'
import componenta from './components/reactcomponenta'
reactdom.render(<div>
<app title='page1' />
<reactcomponenta/>
</div>, document.getelementbyid('app'))
different react components can be then loaded for each single page.
Source: stackoverflow.com
Related Query
- How to create multiple page app using react
- React app showing page with "404 the requested path could not be found" when using Apache
- How to manage React Js to multiple page app not using routes?
- Getting a blank page in react app while deploying using ngnix as reverse proxy
- I'm trying to scroll to top of each page using react app
- Refresh Page in React App if Location Change is same using React-Router
- How to render an iframe of local page within React app using React Router Dom
- How to create routing for multi page app in react
- Using IndexRoute to set up a Landing Page in my React App
- Using App component as a home page in react (path "/")
- React - Wrapping all components in redux provider with store injection, Multi page app
- How can I get my React App to correctly send page title in link previews? Using NGINX as proxy
- First Page of React App Blank when deployed using github pages
- routing within an app using react router to move to another page
- What is the best way to redirect a page using React Router?
- Using React in a multi-page app
- Why IE 11 display blank page rendering react app
- Using API keys in a react app
- Cheapest way to deploy a React app using NextJS SSR on AWS?
- React app error: Failed to construct 'WebSocket': An insecure WebSocket connection may not be initiated from a page loaded over HTTPS
- google is not defined in react app using create-react-app
- Architecture in a react native app using WebSockets
- How to show build datetime on my react web app using create-react-app?
- Info.plist file for react native ios app using expo SDK
- What do you lose by ejecting a React app that was created using create-react-app?
- Share codebase using common Sdk module in create react app Reactjs application
- How to set headers for a React app created using 'create-react-app'
- Hitting Back button in React app doesn't reload the page
- structure of a Backbone and React single page app
- Why Code Coverage in react app is empty? Tried using npm run test -- --coverage. But always showing empty code coverage
More Query from same tag
- Accessing state of another component in ReactJS?
- Create-React-App Proxy in Production Build
- What is the equivalent of 'SafeAreaView' on android - React Native
- Blob to base64 ReactJs
- How to add items in select dynamically in react?
- How to convert a "raw" DOM Event to a React SyntheticEvent?
- I do not get to pass props through mapDispatchToProps
- Passing data to the dynamic click handler when using the ReactJS Hooks
- useContext clear when logout function react js
- React Fetch API in Laravel application
- Submitting SUMMARY Data from Django to React using DRF
- How to render DIV element with a button click on ReactJs
- State is not loading in my basic React/Redux App
- use predefine react component from reagent?
- React datepicker and typescript
- How to fix "TypeError: Cannot read property 'inherits' of undefined" in React project?
- Destructure a Flow object type?
- Problem with setState ReactJS, done in todoList
- What is the better way to update other state variable which is dependent on another state variable while re-rendering?
- What is the difference between babel-plugin-* and babel-preset-*
- Dropdown is not listing countries imported from https://disease.sh/ in React js
- The most "react" way to pass state to other components
- Unit test in React requires construct or call signatures
- Select control created in React does not reset on browser refresh (IE11 and Edge)
- how to set placeholder as an object in textarea element?
- Django authorizing request even if permissions.AllowAny is set
- React router, pass data when navigating programmatically?
- How can I create a HTML/JavaScript element that when I click it it shows a different element in React.js?
- How can I mock useAxios hook from axios-hooks using Jest? (Error: Uncaught [TypeError: undefined is not a function])
- Handling a timer in React/Flux