-
-
Notifications
You must be signed in to change notification settings - Fork 10.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Improvements to ssr:false + prerender scenarios #12948
Conversation
…render combinations
🦋 Changeset detectedLatest commit: 9c8a9bf The changes in this PR will be included in the next version bump. This PR includes changesets to release 11 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
43853ec
to
9778c3d
Compare
0df6d0c
to
b9cf540
Compare
if (route.id === "root" && exp === "loader") { | ||
return false; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Allow the root route to have a loader
in SPA mode
(Array.isArray(reactRouterConfig.prerender) && | ||
reactRouterConfig.prerender.length === 1 && | ||
reactRouterConfig.prerender[0] === "/")) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This went a bit too far - ssr:false, prerender:['/']
is an explicit opt-into prerendering the /
route and should trigger full SSG of the /
route and prerender past the root.
let request = new Request(`http://localhost${reactRouterConfig.basename}`, { | ||
headers: { | ||
// Enable SPA mode in the server runtime and only render down to the root | ||
"X-React-Router-SPA-Mode": "yes", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We use this during build-time request handling now so we can render a SPA fallback even when build.isSpaMode
is false because we're prerendering some MPA pages
@@ -21,7 +21,11 @@ export interface ServerBuild { | |||
assetsBuildDirectory: string; | |||
future: FutureConfig; | |||
ssr: boolean; | |||
/** | |||
* @deprecated This is now done via a custom header during prerendering | |||
*/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No longer used by our server - but since ServerBuild
is arguably public API we can just deprecate it
validatePrerenderedResponse(response, html, "SPA Mode", "/"); | ||
validatePrerenderedHtml(html, "SPA Mode"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Inlined these assertions for clarity
// they can serve. Otherwise it can be the main entry point. | ||
let isPrerenderSpaFallback = build.prerender.includes("/"); | ||
let filename = isPrerenderSpaFallback | ||
? "__spa-fallback__.html" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Write out this spa fallback file when the user chose ssr:false + prerender:['/']
as a way for them to load/hydrate into non-prerendered paths without a runtime server
let routesToPrerender: string[]; | ||
if (typeof reactRouterConfig.prerender === "boolean") { | ||
invariant(reactRouterConfig.prerender, "Expected prerender:true"); | ||
routesToPrerender = determineStaticPrerenderRoutes( | ||
routes, | ||
viteConfig, | ||
true | ||
); | ||
} else if (typeof reactRouterConfig.prerender === "function") { | ||
routesToPrerender = await reactRouterConfig.prerender({ | ||
getStaticPaths: () => | ||
determineStaticPrerenderRoutes(routes, viteConfig, false), | ||
}); | ||
} else { | ||
routesToPrerender = reactRouterConfig.prerender || ["/"]; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was lifted and is now available on build.prerender
loader: route.module.loader ? () => null : undefined, | ||
action: undefined, | ||
handle: route.module.handle, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
None of this is needed on the routes, we just need to know id
/index
/path
/children
to make our prerender decisions
@@ -347,7 +361,6 @@ export function createClientRoutes( | |||
"No `routeModule` available for critical-route loader" | |||
); | |||
if (!routeModule.clientLoader) { | |||
if (isSpaMode) return null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These are unneccesary because fetchServerLoader
already returns null
if no loader exists
if ( | ||
!_build.ssr && | ||
_build.prerender.length > 0 && | ||
normalizedPath !== "/" && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note to self - ensure we have correct behavior for hard reloads on /
in dev mode when it's not prerendered
This reverts commit 8fafeea. No longer needed now that we do export detecton during the virtual route manifest module creation - since that gets invalidated on config change
a2f3576
to
9c8a9bf
Compare
🤖 Hello there, We just published version Thanks! |
…d-route-typegen * upstream/dev: bump patch to minor for new API: `href` Type-safe href (remix-run#12994) docs: prerender/ssr:false (remix-run#13005) Skip action-only resource routes with prerender:true (remix-run#13004) Update docs for spa/prerendering Improvements to ssr:false + prerender scenarios (remix-run#12948)
🤖 Hello there, We just published version Thanks! |
per remix-run/react-router#12948 improvements
🤖 Hello there, We just published version Thanks! |
@brookslybrand and I did a deep dive on some of the current nuances/pain points with
ssr:false
andprerender
scenarios. In Remix v2, we only had thessr
config sossr:false
alone implied "SPA Mode". When we addedprerender
in RR v7, we didn't quite nail all of the changes to ensure that differences betweenssr:false
versus true "SPA Mode" were correctly updated.HydrateFallback
on the server and then loads the proper client-side matched routes viaroute.lazy
during hydration. This is what you get withssr:false
and noprerender
config.loader
because it does always run on the server and makesloaderData
available toHydrateFallback
as an optional prop so you can render a more full-featured loading page.prerender
config, you're opting into pre-rendering non-SPA (effectively MPA) pages for those routes - and those will be able to run the loaders below the root and prerender complete HTML docs in SSG fashion. Those HTML docs still hydrate into client-side-navigable applications.ssr:false + prerender:['/']
, you lost your ability to serve SPA mode any longer because the mainindex.html
file was now an MPA that includedroutes/_index
. In these scenarios,prerender
will now output a special__spa-fallback__.html
file that you can confiugure your static server to serve for any paths you didn't prerender. This is powerful because you can still usessr:false
, fully prerender some paths for perf/SEO, and still allow users to enter your app on any other path via the SPA fallback.A few other small updates:
action
/header
exports when usingssr:false
ssr:false
and aprerender
config to better align with production behaviorTODO: