Prerender
Some of your pages don't have dynamic content; it'd be great if you could render them ahead of time, making for a faster experience for your end users.
We thought a lot about what the developer experience should be for route-based prerendering. The result is one of the smallest APIs imaginable!
How's Prerendering different from SSR/SSG/SWR/ISSG/...?
As Danny said in his Prerender demo at our Community Meetup, the thing all of these have in common is that they render your markup in a Node.js context to produce HTML. The difference is when (build or runtime) and how often.
Prerendering a Page
Prerendering a page is as easy as it gets. Just add the prerender
prop to the Route that you want to prerender:
<Route path="/" page={HomePage} name="home" prerender/>
Then run yarn rw build
and enjoy the performance boost!
Prerendering all pages in a Set
Just add the prerender
prop to the Set that wraps all Pages you want to prerender:
<Set prerender>
<Route path="/" page={HomePage} name="home" />
<Route path="/about" page={AboutPage} name="hello" />
</Set>
Not found page
You can also prerender your not found page (a.k.a your 404 page). Just add—you guessed it—the prerender
prop:
- <Route notfound page={NotFoundPage} />
+ <Route notfound page={NotFoundPage} prerender/>
This will prerender your NotFoundPage to 404.html
in your dist folder. Note that there's no need to specify a path.
Cells, Private Routes, and Dynamic URLs
How does Prerendering handle dynamic data? For Cells, Redwood prerenders your Cells' <Loading/>
component. Similarly, for Private Routes, Redwood prerenders your Private Routes' whileLoadingAuth
prop:
<Private >
// Loading is shown while we're checking to see if the user's logged in
<Route path="/super-secret-admin-dashboard" page={SuperSecretAdminDashboard} name="ssad" whileLoadingAuth={() => <Loading />} prerender/>
</Private>
Right now prerendering won't work for dynamic URLs. We're working on this. If you try to prerender one of them, nothing will break, but nothing happens.
<Route path="/blog-post/{id}" page={BlogPostPage} name="blogPost" prerender />
Prerender Utils
Sometimes you need more fine-grained control over whether something gets prerendered. This may be because the component or library you're using needs access to browser APIs like window
or localStorage
. Redwood has three utils to help you handle these situations:
<BrowserOnly>
useIsBrowser
isBrowser
Heads-up!
If you're prerendering a page that uses a third-party library, make sure it's "universal". If it's not, try calling the library after doing a browser check using one of the utils above.
Look for these key words when choosing a library: universal module, SSR compatible, server compatible—all these indicate that the library also works in Node.js.
<BrowserOnly/>
component
This higher-order component is great for JSX:
import { BrowserOnly } from '@redwoodjs/prerender/browserUtils'
const MyFancyComponent = () => {
<h2>👋🏾 I render on both the server and the browser</h2>
<BrowserOnly>
<h2>🙋♀️ I only render on the browser</h2>
</BrowserOnly>
}
useIsBrowser
hook
If you prefer hooks, you can use the useIsBrowser
hook:
import { useIsBrowser } from '@redwoodjs/prerender/browserUtils'
const MySpecialComponent = () => {
const browser = useIsBrowser()
return (
<div className="my-4 p-5 rounded-lg border-gray-200 border">
<h1 className="text-xl font-bold">Render info:</h1>
{browser ? <h2 className="text-green-500">Browser</h2> : <h2 className="text-red-500">Prerendered</h2>}
</div>
)
}
isBrowser
boolean
If you need to guard against prerendering outside React, you can use the isBrowser
boolean. This is especially handy when running initializing code that only works in the browser:
import { isBrowser } from '@redwoodjs/prerender/browserUtils'
if (isBrowser) {
netlifyIdentity.init()
}