28.8.2023

NextJS 13 and React Server Components

NextJS 13 was released last week (https://nextjs.org/blog/next-13) and it's biggest feature is a new app directory with a new file based routing system. The new folder also supports React Server Components.

This is the next step for React server side rendering. Previously, server side rendering with React looked like this:

  • server loads all data needed for the page (either at build time or when the request is made)
  • server renders all react components to html
  • client loads all html
  • client loads javascript code
  • client "hydrates" html, rerendering all react components and comparing it with the received html
  • page is interactive

With server components, it looks something like this:

  • server renders inital html skeleton, to first Suspense boundary and starts fetching data
  • skeleton html is sent to client
  • as the server finishes data loading, components are rendered on the server and streamed to client as html
  • client loads react and only react client components
  • client hydrates only the react client components
  • page is interactive

So the client has to load much less javascript, only for interactive components.

To try the server components, I created a simple gallery viewer for HelloPaint:

Deployed here: https://next-13-test-three.vercel.app/

Github: https://github.com/lucasmerlin/next-13-test

Things I learned during the project:

  • I couldn't get mui components (material-ui) to render as server components (I think emotion is not supported for now?)
  • Tailwind and DaisyUI worked well
  • Data is loaded with the use() react hook, which accepts a promise and waits until the promise is resolved.

Data loading with graphql, urql, and graphql-codegen looks like this:

import {graphql} from "../../lib/gql";
import {Client} from "@urql/core";
import { use } from "react";
import Image from "next/image";
import {Like} from "./like";

const client = new Client({
  url: "https://hellopaint.io/api/gateway/graphql",
});


const galleryQuery = graphql(/* GraphQL */`
  query GalleryPosts {
      galleryPosts(query: {limit: 30}) {
          id
          title
          imageUrl
          description
      }
  }
`)

const load = async () => {
  return client.query(galleryQuery, {}).toPromise();
}


export default function Home() {

  const data = use(load());

  [...]

If we try to use useState from a server component we get the following error:

You're importing a component that needs useState. It only works in a Client Component but none of its parents are marked with "use client", so they're Server Components by default.

   ,----
 1 | import {useState} from "react";
   :         ^^^^^^^^
   `----

To make it a client component, add 'use client'; to the top of the file, and it will work like a normal react component.

https://nextjs.org/blog/next-13

Lucas

Softwareentwickler

Zur Übersicht

Standort Hannover

newcubator GmbH
Bödekerstraße 22
30161 Hannover

Standort Dortmund

newcubator GmbH
Westenhellweg 85-89
44137 Dortmund