Hi,
I have a Meteor website that on a experimental branch to explore code splitting with SSR I have introduced this package → https://github.com/CaptainN/npdev-react-loadable.
Previously I had used lazy and suspense from React to load pages in my routes file but SSR didn’t work, when I viewed the source of the page I just got my loading component, and I want to return all the html in the body for SEO.
After switching to npdev-react-loadable this seemed to reduce my bundle size, currently the bundle size is 2.7mb, it used to be 3.7mb but removing some packages and implementing react-loadable seems to help.
I am wondering if there are other ways to reduce my bundle size, the node_modules seem to be a majority of the issue. The largest module on the homepage is react-strap
which is buried in another component I tried to only include relevant code for now.
Currently on this experimental branch I have this client/index.js
file
import React from 'react';
import { hydrateRoot } from 'react-dom/client';
import { Router } from 'react-router-dom';
import routes from '../both/routes';
import { preloadLoadables } from 'meteor/npdev:react-loadable';
preloadLoadables().then(() => {
const container = document.getElementById('app');
const html = hydrateRoot(
container,
<>
<Router history={history}>
<div>{routes}</div>
</Router>
</>,
);
});
and this server/index.js
file
import React from 'react';
import { renderToString } from 'react-dom/server';
import { onPageLoad } from 'meteor/server-render';
import { StaticRouter } from 'react-router';
import { Helmet } from 'react-helmet';
import { ServerStyleSheet } from 'styled-components';
import {
LoadableCaptureProvider,
preloadAllLoadables,
} from 'meteor/npdev:react-loadable';
preloadAllLoadables().then(() => {
onPageLoad(async (sink) => {
const context = {};
const sheet = new ServerStyleSheet();
const routes = (await import('../both/routes.js')).default;
const loadableHandle = {};
const App = (props) => (
<StaticRouter location={props.location} context={context}>
{routes}
</StaticRouter>
);
const html = renderToString(
<LoadableCaptureProvider handle={loadableHandle}>
<App />
</LoadableCaptureProvider>,
);
sink.renderIntoElementById('app', renderToString(html));
sink.appendToBody(loadableHandle.toScriptTag());
sink.appendToHead(sheet.getStyleTags());
const helmet = Helmet.renderStatic();
sink.appendToHead(helmet.meta.toString());
sink.appendToHead(helmet.title.toString());
sink.appendToHead(helmet.link.toString());
});
});
Now I created a Loadable component at Loading/Loadable.js
import React from "react"
import {Loadable} from 'meteor/npdev:react-loadable';
const LoadableComponent = opts => Loadable({
loading: () => <p>Loading</p>,
...opts
});
export default LoadableComponent;
And in my routes.js
I am loading my homepage component with the loadableComponent
const HomePage = LoadableComponent({
loader: () => import('../../ui/containers/Pages/Homepage.container'),
})
and then in that same file calling it in the route
export default (
<Switch>
<Route exact name="index" path="/" component={HomePage} />
...
Now on that homePage component I have the following
import LoadableComponent from '../Loading/Loadable';
const PageWrapper = LoadableComponent({
loader: () => import('../Global/PageWrapper'),
});
const StickyFooter = LoadableComponent({
loader: () => import('../Global/StickyFooter'),
});
export default function HomePage(props) {
const [state, setState] = React.useState({});
let page = false;
if (Meteor.isServer) {
page = props.page;
}
if (Meteor.isClient) {
React.useEffect(() => {
if (props) {
setState({ ...props });
}
}, [props]);
page = state.page;
}
if (page) {
return (
<PageWrapper
noMargin
type={'page'}
user={props.user}
page={page}
>
<main
itemType="https://schema.org/Organization"
className="position-relative"
>
</main>
</PageWrapper>
);
}
}
// export default HomePage;
Now what is happening in this experimental branch is has a bunch of html markup like this
&lt;div class=&quot;sc-bCfvAP position-relative&quot;&gt;&lt;div class=&quot;sc-dPWrhe bciHYw page-wrapper-container&quot;&gt;&lt;div class=&quot;page-wrapper... more content missing
it seems to have html with character entities but it cuts off at some point and just shows … as seen at the end of this Gist html · GitHub
Is this a limitation of this package.
Previously my two server/index.js and client/index.js files looked like this with normal imports and routes, now it is somehow working but is mixing readLoadable and npdev react Loadable packages. I didn’t set it up like this.
server
import React from 'react';
import { renderToString } from 'react-dom/server';
import { onPageLoad } from 'meteor/server-render';
import { StaticRouter } from 'react-router';
import { Helmet } from 'react-helmet';
import Loadable from 'react-loadable';
import { ServerStyleSheet } from "styled-components"
onPageLoad(async (sink) => {
const context = {};
const sheet = new ServerStyleSheet();
const routes = (await import('../both/routes.js')).default;
const App = props => (
<StaticRouter location={props.location} context={context}>
{routes}
</StaticRouter>
);
const modules = [];
// const html = renderToNodeStream((
const html = renderToString((
<Loadable.Capture report={(moduleName) => { modules.push(moduleName); }}>
<App location={sink.request.url} />
</Loadable.Capture>
));
sink.renderIntoElementById('app', html);
sink.appendToHead(sheet.getStyleTags());
const helmet = Helmet.renderStatic();
sink.appendToHead(helmet.meta.toString());
sink.appendToHead(helmet.title.toString());
sink.appendToHead(helmet.link.toString());
});
client
import { onPageLoad } from 'meteor/server-render';
import React from 'react';
import { hydrateRoot } from 'react-dom/client';
import { Router } from 'react-router-dom';
import routes from '../both/routes';
import { preloadLoadables } from 'meteor/npdev:react-loadable';
preloadLoadables().then(() => {
const container = document.getElementById('app');
const html = hydrateRoot(
container,
<>
<Router history={history}>
<div>{routes}</div>
</Router>
</>,
);
});
However the reason I am showing the current branch is because it does render the html in the source like so
I guess I have a few questions here.
- Why is the previous way of doing this working mixing react-loadable and npdev react loadable
- What should I do, choose the way I setup before or after experimental branch
- What would you recommend to tackle code splitting or reducing my bundles size and properly setting up my server and client app rendering.
Any help would be greatly appreciated.