Strategies for pages

In this Next.js 14 codebase, we employ several strategies to enhance page performance using async/await and Suspense. These techniques are crucial for optimizing data fetching and rendering, ensuring a smooth user experience.

  1. Asynchronous Data Fetching in Server Components

Server components in Next.js can execute asynchronously, allowing us to fetch data on the server before rendering the component. This approach reduces bundle sizes and leverages server CPU power for faster data fetching.

For example, in the roadmap page, we asynchronously fetch data for different roadmap sections like planned, in progress, and completed tasks using async functions before rendering the components.

async function PlannedCards() {
  const plannedCards = await anonGetPlannedRoadmapFeedbackList();
  return (
    <div className=" space-y-3">
      {plannedCards.length ? ( => (
      ) : (
        <T.Subtle className="italic text-xs text-slate-400 dark:text-slate-500">
  1. Parallel Data Fetching with Suspense

We use React's Suspense component to fetch multiple data sources in parallel and render child components as soon as the data is available. This technique allows the page to start rendering without waiting for all data to be fetched, improving perceived performance.

For instance, the blog list page fetches blog posts and tags in parallel, wrapped in Suspense, enhancing the user experience by streaming rendering.

Server components have the added benefit of streaming rendering. Consider the page responsible for rendering the blog list page at /blog. 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: Tags and BlogList.

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.

import { T } from '@/components/ui/Typography';
import {
} from '@/data/anon/internalBlog';
import { Suspense } from 'react';
import { PublicBlogList } from '../PublicBlogList';
import { TagsNav } from '../TagsNav';
export const metadata = {
  title: 'Blog List | Nextbase',
  description: 'Collection of the latest blog posts from the team at Nextbase',
  icons: {
    icon: '/images/logo-black-main.ico',
async function Tags() {
  const tags = await anonGetAllBlogTags();
  return <TagsNav tags={tags} />;
async function BlogList() {
  const blogPosts = await anonGetPublishedBlogPosts();
  return <PublicBlogList blogPosts={blogPosts} />;
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.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
        <Suspense fallback={<T.Subtle>Loading tags...</T.Subtle>}>
          <Tags />
      <Suspense fallback={<T.Subtle>Loading posts...</T.Subtle>}>
        <BlogList />
  1. Suspense for Incremental Rendering

We also use Suspense to incrementally render parts of the page as data becomes available. This approach is evident in the changelog page, where the changelog list is rendered asynchronously and wrapped in Suspense to display a loading state until the data is fetched.

      <div className="space-y-4 max-w-[768px]">
        <Suspense fallback={<T.Subtle>Loading...</T.Subtle>}>
          <ChangelogList />