Streaming server rendering

In our Next.js 14 pages, we leverage streaming server rendering primarily through the use of server components and the Suspense component for data fetching and rendering optimization. This approach allows us to start rendering the page even before all the data is fetched, improving the user experience by displaying content as soon as it's available.

Example of Streaming Server Rendering

In the blog list page, we demonstrate streaming server rendering by fetching and rendering two sets of data in parallel: the list of blog posts and the list of tags. This is facilitated by moving the data fetching into two separate components, Tags and BlogList, which are server components. By wrapping these components in Suspense, we can render them in parallel and stream the rendering of the page as soon as the data is available, without waiting for all data to be fetched.

Server components have the added benefit of streaming rendering. Consider the page responsible for rendering the blog list page at /blog route. This page has to fetch two sets of data: the list of blog posts and the list of tags. We can fetch both of these data sets in parallel and stream the rendering of the page as soon as the data is available. In this case, we have moved the data fetching into two separate components:

src/app/(static-pages)/(developer-resources)/blog/(list)/page.tsx
async function Tags() {
  const tags = await anonGetAllBlogTags();
  return <TagsNav tags={tags} />;
}
 
async function BlogList() {
  const blogPosts = await anonGetPublishedBlogPosts();
  return <PublicBlogList blogPosts={blogPosts} />;
}

Both of these components are server components and since they are wrapped in Suspense they can be rendered in parallel. Also, the page does not have to wait for the data to be fetched before it starts rendering. It can start rendering and render the child components as soon as the data is available.

src/app/(static-pages)/(developer-resources)/blog/(list)/page.tsx
export default async function BlogListPage() {
  return (
    <div className="space-y-8 w-full">
      <div className="flex items-center flex-col space-y-4">
        <div className="space-y-3 mb-6 text-center">
          <T.Subtle>Blog</T.Subtle>
          <T.H1>All blog posts</T.H1>
          <T.P className="text-xl leading-[30px] text-muted-foreground">
            Here is a collection of the latest blog posts from the team at
            Nextbase.
          </T.P>
        </div>
        <Suspense fallback={<T.Subtle>Loading tags...</T.Subtle>}>
          <Tags />
        </Suspense>
      </div>
      <Suspense fallback={<T.Subtle>Loading posts...</T.Subtle>}>
        <BlogList />
      </Suspense>
    </div>
  );
}

Benefits

  • Improved User Experience: The page starts rendering immediately, displaying the "All blog posts" title and placeholders for tags and posts until the actual data is fetched. This approach significantly enhances the perceived performance of the application.

  • Efficient Data Fetching: By fetching data in server components, we leverage the server's CPU power, leading to faster data retrieval and smaller bundle sizes since we don't have to ship the data fetching logic to the client. Implementation

The implementation of streaming server rendering in our Next.js 14 pages is a testament to the framework's capabilities in optimizing performance and enhancing the user experience. By judiciously using server components and Suspense, we ensure that our pages are not only fast but also provide immediate feedback to users, which is crucial for modern web applications.