We have a requirement to use Loadable Components in a new NextJS app we are building. I believe in NextJS for this use case you tend to use their native dynamic import feature but we are pulling in code from our mature codebase with extensive use of 'Loadable Components' throughout so replacement with dynamic imports is pretty impractical (PR is here in our main codebase: https://github.com/bbc/simorgh/pull/10305).
I have put together a representive example in a repo to demonstrate an issue we are having: https://github.com/andrewscfc/nextjs-loadable
In this example I have introduced a loadable component to split the Layout
component into its own bundle:
import * as React from 'react';
import Link from 'next/link';
import loadable from '@loadable/component';
const LayoutLoadable = loadable(() => import('../components/Layout'));
const IndexPage = () => (
<LayoutLoadable title="Home | Next.js + TypeScript Example">
<h1>Hello Next.js 👋</h1>
<p>
<Link href="/about">
<a>About</a>
</Link>
</p>
</LayoutLoadable>
);
export default IndexPage;
You can run this repo by running: yarn && yarn dev
(or equivalent npm
commands)
If you navigate to http://localhost:3000/
the page body looks like this:
<body>
<div id="__next" data-reactroot=""></div>
<script src="/_next/static/chunks/react-refresh.js?ts=1663916845500"></script>
<script id="__NEXT_DATA__" type="application/json">
{
"props": { "pageProps": {} },
"page": "/",
"query": {},
"buildId": "development",
"nextExport": true,
"autoExport": true,
"isFallback": false,
"scriptLoader": []
}
</script>
</body>
Notice there is no html in the body other than the root div the clientside app is hydrated into: <div id="__next" data-reactroot=""></div>
The SSR is not working correctly but the app does hydrate and show in the browser so the clientside render works.
If you then change to a regular import:
import * as React from 'react';
import Link from 'next/link';
import Layout from '../components/Layout';
const IndexPage = () => (
<Layout title="Home | Next.js + TypeScript Example">
<h1>Hello Next.js 👋</h1>
<p>
<Link href="/about">
<a>About</a>
</Link>
</p>
</Layout>
);
export default IndexPage;
The body SSRs correctly:
body>
<div id="__next" data-reactroot="">
<div>
<header>
<nav>
<a href="/">Home</a>
<!-- -->|<!-- -->
<a href="/about">About</a>
<!-- -->|<!-- -->
<a href="/users">Users List</a>
<!-- -->| <a href="/api/users">Users API</a>
</nav>
</header>
<h1>Hello Next.js 👋</h1>
<p><a href="/about">About</a></p>
<footer>
<hr />
<span>I'm here to stay (Footer)</span>
</footer>
</div>
</div>
<script src="/_next/static/chunks/react-refresh.js?ts=1663917155976"></script>
<script id="__NEXT_DATA__" type="application/json">
{
"props": { "pageProps": {} },
"page": "/",
"query": {},
"buildId": "development",
"nextExport": true,
"autoExport": true,
"isFallback": false,
"scriptLoader": []
}
</script>
</body>
I have attempted to configure SSR as per Loadable Component's documentation in a custom _document
file:
import Document, { Html, Head, Main, NextScript } from 'next/document';
import * as React from 'react';
import { ChunkExtractor } from '@loadable/server';
import path from 'path';
export default class AppDocument extends Document {
render() {
const statsFile = path.resolve('.next/loadable-stats.json');
const chunkExtractor = new ChunkExtractor({
statsFile,
});
return chunkExtractor.collectChunks(
<Html>
<Head />
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
}
This is not working correctly and imagine it maybe because there is no where to call renderToString(jsx)
as per their docs; I think this call happens internally to NextJS.
Has anyone successfully configured loadable components in NextJS with SSR? I can't seem to find the right place to apply Loadable Component's SSR instructions?
from How do you configure SSR with Loadable Components on NextJS?
No comments:
Post a Comment