Next.js Discord

Discord Forum

Suspense Server Component ?

Unanswered
Mini Satin posted this in #help-forum
Open in Discord
Mini SatinOP
Hi everyone,

After looking through the documentation : https://nextjs.org/docs/app/building-your-application/routing/loading-ui-and-streaming

I tried wrapping component A in a Suspense, this component (A) is an async Server Component that fetches data, which contains a Client Component (B) that is wrapping another Server Component (C) using children props. I am fetching data in component A and sending them to A and B. The problem is even using the Suspense at parent level of A, when I request the page, I have to wait 3s before anything appears on screen

Am I using it wrong ? Did I miss something ? Or maybe we can't use Suspense with Server Component that are executing async function such as fetching data.

4 Replies

Mini SatinOP
Parent ( page.tsx)
export const dynamic = "force-dynamic";

import Spotify from '@/components/spotify.tsx'

export default function Home() {
  return (
    <Suspense fallback={<h1>Loading data...</h1>}>
      <Spotify />
    </Suspense>
  );
}


Component A ( Server Component )

import SpotifyClientCard from "./SpotifyClientCard";
import SpotifyServerCard from "./SpotifyServerCard";
import { fetchSpotifySchema } from "@/lib/zod/schema";

async function getSpotify() {
  try {
    const response = await fetch("https://**********/api/spotify");
    const responseJson = await response.json();
    return fetchSpotifySchema.parse(responseJson);
  } catch (err) {
    return null;
  }
}

export default async function Spotify() {
  const data = await getSpotify();
  return (
    <>
      {data ? (
        <>
          <SpotifyClientCard playedAt={data.playedAt}>
            <SpotifyServerCard spotify={data} />
          </SpotifyClientCard>
        </>
      ) : (
        <></>
      )}
    </>
  );
}


Component B ( Client Component )

"use client";
import { motion } from "framer-motion";
const SpotifyClientCard = ({ children, playedAt }: any) => {

  return (
    <>
      <motion.div
        initial={{ translateY: 10, opacity: 0 }}
        animate={{ translateY: 0, opacity: [0, 1] }}
        transition={{ duration: 0.2 }}
        className="flex flex-col h-16"
      >
        {children}
        <p>{playedAt}</p>
      </motion.div>
    </>
  );
};

export default SpotifyClientCard;
Component C ( Server Component )

import Image from "next/image";

const SpotifyServerCard = ({ spotify }: { spotify: any}) => {
  return (
    <div className="...">
      <a
        href={spotify.songUrl}
        rel="noopener noreferrer"
        target="_blank"
        className="h-11 w-11"
      >
        <Image
          src={spotify.albumImageUrl}
          alt={spotify.title}
          height={50}
          width={50}
          placeholder="blur"
          blurDataURL={spotify.albumImageUrl}
          priority
          className="..."
        />
      </a>
      <div className="...">
        <a
          href={spotify.songUrl}
          rel="noopener noreferrer"
          target="_blank"
          className="..."
          title={spotify.title}
        >
          {spotify.title}
        </a>
        <p className="...">{spotify.album}</p>

        <p className="...">by {spotify.artist}</p>
      </div>
     
    </div>
  );
};

export default SpotifyServerCard;
In this particular case since the fetch data is being used for literally everything and there only one component in the main page.tsx, i would think that suspense wont work very well.

You'll have to use a loading.js instead i think. Since the only thing to fetch here loads data, it will wait till that fetch completes to show you the data directly. You might also want to check dynamic imports
Mini SatinOP
I removed extra code from page.tsx to make it look cleaner, but yeah I will check that thanks for the reply !