From d5dfe94a63c65c51192271ea826dac5a3bff7d80 Mon Sep 17 00:00:00 2001 From: Jeremy Liberman Date: Fri, 7 May 2021 02:52:30 -0500 Subject: [PATCH 1/3] Documentation about prefetching and dehydratedState --- app/pages/docs/get-server-side-props.mdx | 21 ++++++-- app/pages/docs/get-static-props.mdx | 21 ++++++-- app/pages/docs/pages.mdx | 68 ++++++++++++++++++++++-- 3 files changed, 95 insertions(+), 15 deletions(-) diff --git a/app/pages/docs/get-server-side-props.mdx b/app/pages/docs/get-server-side-props.mdx index 199c1395..4e7b6095 100644 --- a/app/pages/docs/get-server-side-props.mdx +++ b/app/pages/docs/get-server-side-props.mdx @@ -3,9 +3,9 @@ title: getServerSideProps API sidebar_label: getServerSideProps API --- -If you export an `async` function called `getServerSideProps` from a page, -Blitz will pre-render this page on each request using the data returned by -`getServerSideProps`. +If you export an `async` function called `getServerSideProps` from a +[page](./pages), Blitz will pre-render this page on each request using the +data returned by `getServerSideProps`. ```js export async function getServerSideProps(context) { @@ -189,9 +189,20 @@ what Blitz eliminates from the client-side bundle. #### Only allowed in a page -`getServerSideProps` can only be exported from a **page**. You can’t -export it from non-page files. +`getServerSideProps` can only be exported from a [page](./pages). You +can’t export it from non-page files. Also, you must use `export async function getServerSideProps() {}` — it will **not** work if you add `getServerSideProps` as a property of the page component. + +#### First Render UX + +Even though a page with `getServerSideProps` will be pre-rendered on the +server, it may fallback to the page's loading state if your page contains +queries that request data missing from the initial page props. This can +create a flickering effect where the contents of the page rapidly changes +when the query data gets loaded into the app. + +[Click here](./pages#first-render-ux) to discover ways you can improve the +first render User Experience (UX) of your pages. diff --git a/app/pages/docs/get-static-props.mdx b/app/pages/docs/get-static-props.mdx index 830227bc..bcb70d9b 100644 --- a/app/pages/docs/get-static-props.mdx +++ b/app/pages/docs/get-static-props.mdx @@ -16,9 +16,9 @@ runtime. And at build time, there is no user http request to handle. -If you export an `async` function called `getStaticProps` from a page, -Blitz will pre-render this page at build time using the props returned by -`getStaticProps`. +If you export an `async` function called `getStaticProps` from a +[page](./pages), Blitz will pre-render this page at build time using the +props returned by `getStaticProps`. ```jsx export async function getStaticProps(context) { @@ -376,8 +376,8 @@ the exported JSON is used. #### Only allowed in a page -`getStaticProps` can only be exported from a **page**. You can’t export it -from non-page files. +`getStaticProps` can only be exported from a [page](./pages). You can’t +export it from non-page files. One of the reasons for this restriction is that React needs to have all the required data before the page is rendered. @@ -391,6 +391,17 @@ component. In development (`blitz dev`), `getStaticProps` will be called on every request. +#### First Render UX + +Even though a page with `getStaticProps` will be pre-rendered on the +server, it may fallback to the page's loading state if your page contains +queries that request data missing from the initial page props. This can +create a flickering effect where the contents of the page rapidly changes +when the query data gets loaded into the app. + +[Click here](./pages#first-render-ux) to discover ways you can improve the +first render User Experience (UX) of your pages. + #### Preview Mode In some cases, you might want to temporarily bypass Static Generation and diff --git a/app/pages/docs/pages.mdx b/app/pages/docs/pages.mdx index e894cc63..97e98b62 100644 --- a/app/pages/docs/pages.mdx +++ b/app/pages/docs/pages.mdx @@ -39,15 +39,26 @@ file called `app/pages/posts/[id].js`, then it will be accessible at By default, Blitz pre-renders the static HTML for every page unless you explicitly opt-in to server-side rendering. -For pages with dynamic data, the page's loading state will be -pre-rendered. - -#### First Render UX +For pages with dynamic data, the page's loading fallback state will be +rendered unless you [prefetch](./query-usage#prefetching) the data to +populate the cache. In some cases, the static optimization can cause an undesirable UX where the first render shows one thing but the second render shows another. For example this happens when using `useSession()`. +Next, we'll introduce some ways to improve the way Blitz pre-renders your +pages. + +### First Render UX {#first-render-ux} + +Blitz tries to automatically pre-render the HTML for your pages, but if +your page contains dynamic data fetched with [useQuery](./use-query) +hooks, Blitz will fallback to the page's loading state and you will not +see much benefit from the automatic pre-rendering, because the application +will need to immediately make another request to fetch the data it needs +to satisfy the query. + In this case you can set `Page.suppressFirstRenderFlicker = true`, and Blitz will hide the first render's content. This will result in a tiny delay of first paint but will greatly improve the perceived UX. @@ -64,6 +75,53 @@ Page.suppressFirstRenderFlicker = true export default Page ``` +You can also consider prefetching all of the necessary queries that your +page needs to complete its first render. You can use either the +`getStaticProps` or `getServerSideProps` page functions, depending on what +you needs. + +If you create an instance of QueryClient and populate it with query data, +then you can pass it as `dehydratedState` to your page props. Blitz will +automatically use the state to build the query cache later when the page +tries to render. + +```tsx +const Page: BlitzPage = () => { + // highlight-start + const [organization] = useQuery(getCurrentOrganization, null) + // highlight-end + + return
You have selected: {organization.name}
+} + +export async function getServerSideProps(ctx: GetServerSidePropsContext) { + const queryClient = new QueryClient() + + // highlight-start + const queryKey = getQueryKey(getCurrentOrganization, null) + await queryClient.prefetchQuery(queryKey, () => + invokeWithMiddleware(getCurrentOrganization, null, ctx), + ) + // highlight-end + + return { + props: { + dehydratedState: dehydrate(queryClient), + }, + } +} +``` + +In this way, the pre-rendered version of your page HTML will not have to +make a request as soon as it loads to fetch any data and will avoid any +screen flickering. This is very useful for Search Engine Optimization and +the Link Previews used by Social Media sites like Facebook and Twitter. + +You can also use prefetching to pre-populate queries that a user might +need shortly after the page loads--such a common searches or filter +criteria--by setting a [staleTime](./use-query#options) on the query being +prefetched. + ### Static Page Generation for Unauthenticated Pages {#static-page-generation-for-unauthenticated-pages} For pages accessible by anyone without authentication, we recommend using @@ -91,5 +149,5 @@ To use Server-side Rendering for a page, you need to `export` an `async` function called `getServerSideProps`. This function will be called by the server on every request. -See the [`getServerSideProps` documentation](./get-server-side-props) for +See the `getServerSideProps` [documentation](./get-server-side-props) for more details. From 5d1ea89d3437948e2496749727eab0806ae2942f Mon Sep 17 00:00:00 2001 From: Jeremy Liberman Date: Fri, 7 May 2021 02:59:03 -0500 Subject: [PATCH 2/3] Minor changes --- app/pages/docs/pages.mdx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/app/pages/docs/pages.mdx b/app/pages/docs/pages.mdx index 97e98b62..a449b61b 100644 --- a/app/pages/docs/pages.mdx +++ b/app/pages/docs/pages.mdx @@ -52,12 +52,12 @@ pages. ### First Render UX {#first-render-ux} -Blitz tries to automatically pre-render the HTML for your pages, but if -your page contains dynamic data fetched with [useQuery](./use-query) -hooks, Blitz will fallback to the page's loading state and you will not -see much benefit from the automatic pre-rendering, because the application -will need to immediately make another request to fetch the data it needs -to satisfy the query. +To provide a better User Experience (UX), Blitz tries to automatically +pre-render the HTML for your pages, but if your page contains dynamic data +fetched with [useQuery](./use-query) hooks, Blitz will fallback to the +page's loading state and you will not see much benefit from the automatic +pre-rendering, because the application will need to immediately make +another request to fetch the data it needs to satisfy the query. In this case you can set `Page.suppressFirstRenderFlicker = true`, and Blitz will hide the first render's content. This will result in a tiny @@ -78,7 +78,7 @@ export default Page You can also consider prefetching all of the necessary queries that your page needs to complete its first render. You can use either the `getStaticProps` or `getServerSideProps` page functions, depending on what -you needs. +you need. If you create an instance of QueryClient and populate it with query data, then you can pass it as `dehydratedState` to your page props. Blitz will From 3c520d9737490cb000d5a84d31e95f2f2e30b701 Mon Sep 17 00:00:00 2001 From: Brandon Bayer Date: Mon, 10 May 2021 16:37:36 -0400 Subject: [PATCH 3/3] Update app/pages/docs/pages.mdx --- app/pages/docs/pages.mdx | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/app/pages/docs/pages.mdx b/app/pages/docs/pages.mdx index a449b61b..364cd096 100644 --- a/app/pages/docs/pages.mdx +++ b/app/pages/docs/pages.mdx @@ -86,6 +86,15 @@ automatically use the state to build the query cache later when the page tries to render. ```tsx +import { + useQuery, + getQueryKey, + invokeWithMiddleware, + dehydrate, + QueryClient, + BlitzPage, + GetServerSidePropsContext +} from 'blitz' const Page: BlitzPage = () => { // highlight-start const [organization] = useQuery(getCurrentOrganization, null)