diff --git a/.changeset/funny-ravens-run.md b/.changeset/funny-ravens-run.md new file mode 100644 index 000000000..d79104d79 --- /dev/null +++ b/.changeset/funny-ravens-run.md @@ -0,0 +1,47 @@ +--- +'@faustwp/core': minor +--- + +Feat: Added support `next/dynamic` imports for templates to reduce initial bundle size in a way that's backwards compatible with static imports. + +This solves a known issue in Faust where all defined templates are bundled together and loaded on every WordPress page. by enabling the use of dynamic importing of templates this issue is resolved. Now templates are only loaded as needed per route. + +It's recommended you migrate to dynamic imports by updating your template file. Here's an example: + +```js title=src/wp-templates/index.js +// Old Static Templates +import category from './category'; +import tag from './tag'; +import frontPage from './front-page'; +import page from './page'; +import single from './single'; + +export default { + category, + tag, + 'front-page': frontPage, + page, + single, +}; + +// New Dynamic Templates +import dynamic from 'next/dynamic'; + +const category = dynamic(() => import('./category.js')); +const tag = dynamic(() => import('./tag.js')); +const frontPage = dynamic(() => import('./front-page.js')); +const page = dynamic(() => import('./page.js')); + +// The above examples assume use of default exports. If you are using named exports you'll need to handle that: +const single = dynamic(() => import('./single.js').then(mod => mod.Single)); + +export default { + category, + tag, + 'front-page': frontPage, + page, + single, +}; +``` + +For further info see the Next.js docs on the use of [`next/dynamic`](https://nextjs.org/docs/pages/guides/lazy-loading#nextdynamic-1). diff --git a/docs/how-to/basic-setup/index.md b/docs/how-to/basic-setup/index.md index 611a174be..81449ce44 100644 --- a/docs/how-to/basic-setup/index.md +++ b/docs/how-to/basic-setup/index.md @@ -175,13 +175,13 @@ In the `SingleTemplate` component, we receive the props, destructure the `title` Finally, we have to make Faust.js aware that this template exists. To do that, create an `index.js` file inside the `wp-templates` folder with this code inside: ```js title="wp-templates/index.js" -import SingleTemplate from "./single"; +import dynamic from 'next/dynamic'; -const templates = { - single: SingleTemplate, -}; +const category = dynamic(() => import('./category.js')); -export default templates; +export default { + single, +}; ``` ### C. Create a catch-all route diff --git a/examples/next/faustwp-getting-started/wp-templates/index.js b/examples/next/faustwp-getting-started/wp-templates/index.js index 3cabd3e14..0e8e6b51e 100644 --- a/examples/next/faustwp-getting-started/wp-templates/index.js +++ b/examples/next/faustwp-getting-started/wp-templates/index.js @@ -1,13 +1,29 @@ -import category from './category'; -import tag from './tag'; -import frontPage from './front-page'; -import page from './page'; -import single from './single'; +import dynamic from 'next/dynamic'; + +const category = dynamic(() => import('./category.js'), { + loading: () =>

Loading Category Template...

, +}); + +const tag = dynamic(() => import('./tag.js'), { + loading: () =>

Loading Tag Template...

, +}); + +const frontPage = dynamic(() => import('./front-page.js'), { + loading: () =>

Loading Front Page Template...

, +}); + +const page = dynamic(() => import('./page.js'), { + loading: () =>

Loading Page Template...

, +}); + +const single = dynamic(() => import('./single.js'), { + loading: () =>

Loading Single Post Template...

, +}); export default { - category, - tag, - 'front-page': frontPage, - page, - single, + category, + tag, + 'front-page': frontPage, + page, + single, }; diff --git a/packages/faustwp-core/src/getTemplate.ts b/packages/faustwp-core/src/getTemplate.ts index d08ec3ca7..1c5850445 100644 --- a/packages/faustwp-core/src/getTemplate.ts +++ b/packages/faustwp-core/src/getTemplate.ts @@ -146,10 +146,23 @@ export function getPossibleTemplates(node: SeedNode) { return possibleTemplates; } +type DynamicComponent = { + render: { + preload: () => Promise<{ default: C }>; + displayName?: string; + }; +}; + +export function isDynamicComponent( + component: C | DynamicComponent, +): component is DynamicComponent { + return (component as DynamicComponent).render?.preload !== undefined; +} + export function getTemplate( seedNode: SeedNode | null | undefined, templates: { [key: string]: WordPressTemplate }, -): WordPressTemplate | null { +): WordPressTemplate | DynamicComponent | null { if (!seedNode) { return null; } diff --git a/packages/faustwp-core/src/getWordPressProps.tsx b/packages/faustwp-core/src/getWordPressProps.tsx index 6128ccaf6..de81b8fcf 100644 --- a/packages/faustwp-core/src/getWordPressProps.tsx +++ b/packages/faustwp-core/src/getWordPressProps.tsx @@ -6,7 +6,11 @@ import { GetServerSidePropsContext, GetStaticPropsContext } from 'next'; import { addApolloState, getApolloClient } from './client.js'; import { FaustTemplateProps } from './components/WordPressTemplate.js'; import { getConfig } from './config/index.js'; -import { getPossibleTemplates, getTemplate } from './getTemplate.js'; +import { + getPossibleTemplates, + getTemplate, + isDynamicComponent, +} from './getTemplate.js'; import { SEED_QUERY, SeedNode } from './queries/seedQuery.js'; import { debugLog, infoLog } from './utils/log.js'; import { hooks } from './wpHooks/index.js'; @@ -142,12 +146,19 @@ export async function getWordPressProps( getPossibleTemplates(seedNode), ); - const template = getTemplate(seedNode, templates); + let template = getTemplate(seedNode, templates); if (!template) { return createNotFound(ctx, revalidate); } + if (isDynamicComponent(template)) { + + const dynamicTemplate = await template.render.preload(); + + template = dynamicTemplate.default ?? dynamicTemplate; + } + if (template.query && template.queries) { throw new Error( '`Only either `Component.query` or `Component.queries` can be provided, but not both.',