Skip to main content

Server-Side Rendering with React

Helper functions#

These functions are meant to be used together, in different steps of your application.

prepareReactRender#

Function designed to be used before the React Render is being made, for example, in places like getStaticProps or getServerSideProps of Next.js

Internally it uses react-ssr-prepass

useHydrateCache#

Function designed to be placed at the very top of the hydrated component

Example#

Made with Next.js SSR Data Fetching in mind.

It's exactly the same usage for getServerSideProps If you are using staleWhileRevalidate in your application, You should always set 'shouldRefetch' as false, since that feature will always do the refetch anyways.

import { PropsWithServerCache } from '@gqless/react';
import { GetStaticProps } from 'next';
import { prepareReactRender, useHydrateCache, useQuery } from '../gqless';
type PageProps = PropsWithServerCache<{
name: string;
}>;
export default function ExamplePage({ cacheSnapshot, name }: PageProps) {
useHydrateCache({
cacheSnapshot,
// If it should refetch everything after the component is mounted
// By default 'shouldRefetch' is `false` (You can change it in the 'defaults' option)
shouldRefetch: false,
});
const query = useQuery();
return (
<div>
<p>{query.hello({ name })}</p>
</div>
);
}
export const getStaticProps: GetStaticProps<PageProps> = async (_ctx) => {
const name = 'John';
const { cacheSnapshot } = await prepareReactRender(
<ExamplePage name={name} />
);
return {
props: {
cacheSnapshot,
name,
},
};
};

Using Suspense#

Please check Suspense in SSR

Using Next.js Router#

If you are using Next.js useRouter inside a component of a Next.js project, you might encounter errors like:

  • TypeError: Cannot destructure property 'pathname' of 'Object(...)(...)' as it is null.

Since with prepareReactRender you are rendering outside of the Next.js tree, you don't have access to it's router, and it's not even created yet.

We suggest a couple of solutions:

Add a default object before destructuring#

You will need to check if you really have any router property available

import { useRouter } from 'next/router';
// ...
const { pathname } = useRouter() || {};
// pathname == undefined while "prepareReactRender"

Mocking Router Data#

Here you can either manually set the values of the router, or simply let it be {}, and all it's properties are simply going to be undefined.

import type { PropsWithServerCache } from '@gqless/react';
import type { GetServerSideProps } from 'next';
import { useHydrateCache, prepareReactRender } from '../gqless';
import { useRouter } from 'next/router';
import { RouterContext } from 'next/dist/next-server/lib/router-context';
export const getServerSideProps: GetServerSideProps<PropsWithServerCache> = async ({}) => {
const { cacheSnapshot } = await prepareReactRender(
<RouterContext.Provider
value={
{
pathname: '/page',
} as any
}
>
<Page />
</RouterContext.Provider>
);
return {
props: {
cacheSnapshot,
},
};
};
export default function Page({ cacheSnapshot }: PropsWithServerCache) {
// ...
const {
// ...
} = useRouter();
// ...
}
Last updated on by github-actions[bot]