Next.js Discord

Discord Forum

General Help Around Fetching on the Client with Suspense

Unanswered
Silver Fox posted this in #help-forum
Open in Discord
Silver FoxOP
hi there!

I'm learning Next.js, and I wanted to ask for some help and guidance on how I should approach fetching data on the client with Suspense.

So, let's say I have a client component and I want to fetch some data (based on some user interaction). Next.js recommends the use hook, or using libraries like useSWR or React Query, which come with bonus features like deduping network requests .

Cool! Let's start using useSWR. I follow the docs, and I set it up and it works! Now of course, to enhance the UX, I want to surround my client component with Suspense and return a fallback while I'm fetching the data.

This is where the difficulty starts.

Turns out, you need to enable suspense mode on useSWR. You also need fallback data... Then I dived into data URLs and placeholder images and I just thought it can't be that difficult...

Then, I turned towards React Query. Got it setup and it ofc fetches the data I want! Now, to use it with Suspense, you need to use the alternative useSuspenseQuery. Ok, easy enough. But, when I run the dev server (pnpm run dev), I get the error that /api/xxxx is not a valid URL.

Turns out, Next.JS is rendering my client component on the server, so useSuspenseQuery is fetching from /api/xxxx on the server, which doesn't really exist yet.

That's when I fell into the Tanstack rabbit hole of using some experimental nextjs packages and streaming and all that jazz, but I still got the same error.

Obvious solution? Disable SSR. Ok, so let's import the component dynamically.... and Suspense stops working.

So, I came here to ask for help because I can't imagine many people are facing the same issue, which tells me that I got lost in the way somehow and I probably wanna do something else.

TLDR: Trying to fetch data on the client with Suspense and it turned out more difficult than expected. So, I'm asking if I should probably be doing something differently...

5 Replies

This is the simplest way imo.
Use regular useQuery from TanStack query and instead of using Suspense just do some condtional rendering within the component.
For example
"use client";

export default function UsersRooms() {
    const { data: res, isLoading } = useQuery({
        queryKey: ["rooms"],
        queryFn: () => api.user.rooms.get(),
    });

    return (
        <aside className={styles.aside}>
            <p>
                {isLoading
                    ? "Loading rooms"
                    : res?.data?.rooms.length === 0
                    ? "No open rooms"
                    : "Open Rooms"}
            </p>
            {/* Render list */}
        </aside>
    );
}
then u can try useSuspenseQuery and then hardcode base url
Silver FoxOP
hardcoding a url makes the code uglier (if (dev) url = .... else url = ....), and harder to maintain (you have to update the urls when it changes).
i know you're suggesting that solution because im a beginner, so it's simpler to me. but i was wondering what yall do in the same scenario?
@Silver Fox hardcoding a url makes the code uglier (if (dev) url = .... else url = ....), and harder to maintain (you have to update the urls when it changes). i know you're suggesting that solution because im a beginner, so it's simpler to me. but i was wondering what yall do in the same scenario?
1. hardcoding URL isn't that bad you store it in public ENV variable or generate it at runtime using window.location.origin
2. You said
so you can show the fallback until all child components have resolved`
You can easily do the same thing with conditional rendering and it'll give u js as much flexibility if not more.

TLDR; If u really want to use Suspense then go back to useSuspenseQuery and generate url using window.location.origin or an ENV var.