Server Side Rendering
NextJS
We've optimized integration into NextJS with a custom Document and NextJS specific wrapper for App
- NPM
 - Yarn
 
yarn add @rest-hooks/ssr @rest-hooks/redux redux
npm install --save @rest-hooks/ssr @rest-hooks/redux redux
pages/_document.tsx
import { RestHooksDocument } from '@rest-hooks/ssr/nextjs';
export default RestHooksDocument;
pages/_app.tsx
import { AppCacheProvider } from '@rest-hooks/ssr/nextjs';
import type { AppProps } from 'next/app';
export default function App({ Component, pageProps }: AppProps) {
  return (
    <AppCacheProvider>
      <Component {...pageProps} />
    </AppCacheProvider>
  );
}
Further customizing Document
To further customize Document, simply extend from the provided document.
Make sure you use super.getInitialProps() instead of Document.getInitialProps()
or the Rest Hooks code won't run!
pages/_document.tsx
import { Html, Head, Main, NextScript } from 'next/document'
import { RestHooksDocument } from '@rest-hooks/ssr/nextjs';
export default class MyDocument extends RestHooksDocument {
  static async getInitialProps(ctx) {
    const originalRenderPage = ctx.renderPage
    // Run the React rendering logic synchronously
    ctx.renderPage = () =>
      originalRenderPage({
        // Useful for wrapping the whole react tree
        enhanceApp: (App) => App,
        // Useful for wrapping in a per-page basis
        enhanceComponent: (Component) => Component,
      })
    // Run the parent `getInitialProps`, it now includes the custom `renderPage`
    const initialProps = await super.getInitialProps(ctx)
    return initialProps
  }
  render() {
    return (
      <Html>
        <Head />
        <body>
          <Main />
          <NextScript />
        </body>
      </Html>
    )
  }
}
CSP Nonce
Rest Hooks Document serializes the store state in a script tag. In case you have
Content Security Policy restrictions that require use of a nonce, you can override
RestHooksDocument.getNonce.
Since there is no standard way of handling nonce in NextJS, this allows you to retrieve any nonce you created in the DocumentContext to use with Rest Hooks.
pages/_document.tsx
import { RestHooksDocument } from '@rest-hooks/ssr/nextjs';
export default class MyDocument extends RestHooksDocument {
  static getNonce(ctx: DocumentContext) {
    // this assumes nonce has been added here - customize as you need
    return ctx.res.nonce;
  }
}
Express JS
When implementing your own server using express.
- NPM
 - Yarn
 
yarn add @rest-hooks/ssr @rest-hooks/redux redux
npm install --save @rest-hooks/ssr @rest-hooks/redux redux
Server side
import express from 'express';
import { renderToPipeableStream } from 'react-dom/server';
import {
  createPersistedStore,
  createServerDataComponent,
} from '@rest-hooks/ssr';
const rootId = 'react-root';
const app = express();
app.get('/*', (req: any, res: any) => {
  const [ServerCacheProvider, useReadyCacheState, controller] =
    createPersistedStore();
  const ServerDataComponent = createServerDataComponent(useReadyCacheState);
  controller.fetch(NeededForPage, { id: 5 });
  const { pipe, abort } = renderToPipeableStream(
    <Document
      assets={assets}
      scripts={[<ServerDataComponent key="server-data" />]}
      rootId={rootId}
    >
      <ServerCacheProvider>{children}</ServerCacheProvider>
    </Document>,
    {
      onCompleteShell() {
        // If something errored before we started streaming, we set the error code appropriately.
        res.statusCode = didError ? 500 : 200;
        res.setHeader('Content-type', 'text/html');
        pipe(res);
      },
      onError(x: any) {
        didError = true;
        console.error(x);
        res.statusCode = 500;
        pipe(res);
      },
    },
  );
  // Abandon and switch to client rendering if enough time passes.
  // Try lowering this to see the client recover.
  setTimeout(abort, 1000);
});
app.listen(3000, () => {
  console.log(`Listening at ${PORT}...`);
});
Client
import { hydrateRoot } from 'react-dom';
import { awaitInitialData } from '@rest-hooks/ssr';
const rootId = 'react-root';
awaitInitialData().then(initialState => {
  hydrateRoot(
    document.getElementById(rootId),
    <CacheProvider initialState={initialState}>{children}</CacheProvider>,
  );
});