Skeletons in Next.js
Unanswered
Tonkinese posted this in #help-forum
TonkineseOP
what's the correct way of setting up a page in the latest next version that has a sidebar with a list of "suggested products" and a primary container with a list of search results? (the base elements of the sidebar and the main container should appear on render using skeletons, and then once the fetch request is finished the skeletons should be replaced with the data)
60 Replies
client component
TonkineseOP
i'm guessing this is the relevant documentation: https://nextjs.org/docs/app/building-your-application/routing/loading-ui-and-streaming
i.e. I use
Suspense for any fetch data@Tonkinese what's the correct way of setting up a page in the latest next version that has a sidebar with a list of "suggested products" and a primary container with a list of search results? (the base elements of the sidebar and the main container should appear on render using skeletons, and then once the fetch request is finished the skeletons should be replaced with the data)
Siberian Rubythroat
I recommend you to build this project clone
https://www.youtube.com/watch?v=c_-b_isI4vg
There Antonio use
And he return null if !hasMounted
So instead of null you may return some compnent like [here](https://github.com/nicitaacom/23_store/blob/development/app/components/ClientOnly.tsx)
[layout.tsx](https://github.com/nicitaacom/23_store/blob/development/app/layout.tsx)
https://www.youtube.com/watch?v=c_-b_isI4vg
There Antonio use
<ClientOnly/> (as I remember)And he return null if !hasMounted
So instead of null you may return some compnent like [here](https://github.com/nicitaacom/23_store/blob/development/app/components/ClientOnly.tsx)
[layout.tsx](https://github.com/nicitaacom/23_store/blob/development/app/layout.tsx)
@Tonkinese i'm guessing this is the relevant documentation: https://nextjs.org/docs/app/building-your-application/routing/loading-ui-and-streaming
you can have something like this in the component
return loading ? <LoadingSkeleton /> : <Data />@SharpieMaster you can have something like this in the component
tsx
return loading ? <LoadingSkeleton /> : <Data />
TonkineseOP
where
<Data /> is a use client component?both have to be client components in this case
TonkineseOP
ah so
use client components can still render on first load?there a cursed way you can do this though, where you have the skeleton component as a server component and when the client component loads you change the display property on the original server component skeleton to none, or just remove it from the html.
But I recommend reading the docs for a better solution
But I recommend reading the docs for a better solution
@Tonkinese ah so `use client` components can still render on first load?
Siberian Rubythroat
actually yes
But without interactivity during SSR
it will be just HTML CSS
But without interactivity during SSR
it will be just HTML CSS
TonkineseOP
@Siberian Rubythroat actually yes
But without interactivity during SSR
it will be just HTML CSS
TonkineseOP
that's fine, it's just skeletons anyway
yea, usually you use server components for SEO
TonkineseOP
ok so if i currently have something like
const HomePage = () => {
const { data, loading, error } = useQuery()
if (loading) <p>loading<p>
if (error) <p>error</p>
return (
<div>
<Sidebar data={data} />
<Content data={data} />
</div>
)
}, how would you suggest I refactor that to integrate skeleton elements?would you just pass the data, loading, and error variables to each child component and render their respective elements inside?
@SharpieMaster yea, usually you use server components for SEO
Siberian Rubythroat
Actually if you send a lot of interactivity (JS) like useEffect useState onClick etc - anyway initial page load for GPS will not the best because you use a lot of JS
const HomePage = () => {
const { data, loading, error } = useQuery()
return (
error ? :
(loading ? <Skeleton /> :
<div>
<Sidebar data={data} />
<Content data={data} />
</div>) : <h1>Error happened bruh</h1>
)
}Siberian Rubythroat
In case you need performance for SEO - you need to create static landing page like here
https://28-jotion-clone.vercel.app/
https://28-jotion-clone.vercel.app/
But it will be bad UX cause user need to click on some button to start use your service
TonkineseOP
e.g.
const Sidebar = ({ data, error, loading }) => {
return (
<div id="sidebar">
<ul className="border border-gray-500">
{data ? (
<>
{data.products.map((product) => {
<NormalProduct product={product} />
}))
</>
) : (<SkeletonProduct />)}
</ul>
</div>
)
}@Tonkinese what's the correct way of setting up a page in the latest next version that has a sidebar with a list of "suggested products" and a primary container with a list of search results? (the base elements of the sidebar and the main container should appear on render using skeletons, and then once the fetch request is finished the skeletons should be replaced with the data)
Siberian Rubythroat
I don't understand - do you need loading skeleton for entire website like here
https://23-store.vercel.app/
https://23-store.vercel.app/
Siberian Rubythroat
Or you just need some skeleton when you click on button and show loading state?
@Siberian Rubythroat I don't understand - do you need loading skeleton for entire website like here
https://23-store.vercel.app/
TonkineseOP
"(the base elements of the sidebar and the main container should appear on render using skeletons, and then once the fetch request is finished the skeletons should be replaced with the data)"
Siberian Rubythroat
its conditional rendering
@Tonkinese "(the base elements of the sidebar and the main container should appear on render using skeletons, and then once the fetch request is finished the skeletons should be replaced with the data)"
Siberian Rubythroat
You just:
1. Fetch in server component
2. Then pass data to client component
3. Then in client component you may render skeleton if data === null
data its data that you pass through props
1. Fetch in server component
2. Then pass data to client component
3. Then in client component you may render skeleton if data === null
data its data that you pass through props
TonkineseOP
not in the best environment for a call atm
but basically this is what you mean? https://nextjs-forum.com/post/1208307093401243658#message-1208311638307110942
TonkineseOP
i.e. moving the query state inside the child components rather than having them all in the parent component
@Tonkinese but basically this is what you mean? https://discord.com/channels/752553802359505017/1208307093401243658/1208311638307110942
Siberian Rubythroat
yes you can do something similar to that
TonkineseOP
I want it so that when the user first loads my app, it immediately shows the product list with skeletons, until the data has been fetched
Siberian Rubythroat
and it would be better if you will use TypeScript
@Tonkinese I want it so that when the user first loads my app, it immediately shows the product list with skeletons, until the data has been fetched
the delay will be so small that you prob couldnt even notice
because the skeleton doesnt have any aditional delay
TonkineseOP
yeah great, will give that a shot then, thanks for the hand guys
oh one other question, do you guys suggest fetching everything in a single query, or breaking the query up for each child component?
depends what you want
theres no "best" way to do it
TonkineseOP
for example, the Sidebar component is show "trending" products, whereas the Content component is showing the products from a search query. I'm using graphql, so returning both in the same query is easy, just curious what is considered best practice
obviously a single query might be slighty faster, as it's just one request, but maybe there's some other benefit from the query-per-component approach
@Tonkinese oh one other question, do you guys suggest fetching everything in a single query, or breaking the query up for each child component?
idk how you would even fetch it for each child
TonkineseOP
I would just make two useQuery calls instead of one?
so instead of a single
useQuery inside the parent component, Sidebar and Content would each get their own useQuery call (one for "trending products", the other to "search results")no but like for what
how could the child know what to fetch
I think its better to do it all at once
TonkineseOP
not sure I follow, why wouldn't the child know what to fetch?
depends on how you make it
TonkineseOP
the two queries are unrelated to eachother
so they don't need to be aware of what the other is doing
its better to do it all at once, since the fetch delay is much longer than the time it takes to execute the query on the server side
again, depends on how you design ur api
TonkineseOP
like I said i'm using graphql, so it doesn't really make a difference
not the database, but how you design your routes
you would have to have something like getNthTrending()
vs getAllTreding()