ISR + real-time data
Unanswered
Wuchang bream posted this in #help-forum
Wuchang breamOP
is there some good way to combine ISR with refreshing the data if it has changed?
combining instant page show with realtime data
What I thought would be: showing cached data with ISR to have instant loading, refreshing the data in the background, updating the data to the user and cache if it was changed
combining instant page show with realtime data
What I thought would be: showing cached data with ISR to have instant loading, refreshing the data in the background, updating the data to the user and cache if it was changed
159 Replies
Wuchang breamOP
(my first post dont bite me, i will add more info later)
@Wuchang bream is there some good way to combine ISR with refreshing the data if it has changed?
combining instant page show with realtime data
What I thought would be: showing cached data with ISR to have instant loading, refreshing the data in the background, updating the data to the user and cache if it was changed
you can use a client component inside your ISR page. Wrap it with a suspense boundary and enjoy fast loading ^^
@B33fb0n3 you can use a client component inside your ISR page. Wrap it with a suspense boundary and enjoy fast loading ^^
Wuchang breamOP
will i get the same result? do you have any example?
@Wuchang bream will i get the same result? do you have any example?
I don't have an example. You should be able to test is pretty fast: one page.tsx with isr + a client component in it. Then load the page and check the network tab
@B33fb0n3 I don't have an example. You should be able to test is pretty fast: one page.tsx with isr + a client component in it. Then load the page and check the network tab
Wuchang breamOP
and refresh the isr component if the content changed? 

or thats not the way?
@Wuchang bream and refresh the isr component if the content changed? <:cpvpsus:938638845078016030>
the isr page will stay static, when you dont revalidate. The client component will be rendered dynamically
@B33fb0n3 the isr page will stay static, when you dont revalidate. The client component will be rendered dynamically
Wuchang breamOP
right, but it will render everytime, so for example you have ISR with data number of 5
CSR shows 7
ISR stays the same
next user gets the same result, 5 then 7
i would refresh the ISR if data has changed
CSR shows 7
ISR stays the same
next user gets the same result, 5 then 7
i would refresh the ISR if data has changed
@Wuchang bream right, but it will render everytime, so for example you have ISR with data number of 5
CSR shows 7
ISR stays the same
next user gets the same result, 5 then 7
i would refresh the ISR if data has changed
yea, but when you still revalidate, then you dont need a dynamic segment. Then you can also build a dynamic page and cache the functions that you call
@B33fb0n3 yea, but when you still revalidate, then you dont need a dynamic segment. Then you can also build a dynamic page and cache the functions that you call
Wuchang breamOP
you would want because only the next refresh will have fresh data
@Wuchang bream you would want because only the next refresh will have fresh data
yea, you only get fresh data when you not hit the cache
@B33fb0n3 yea, you only get fresh data when you not hit the cache
Wuchang breamOP
thats the issue
i know that you can revalidate, but you will still need to load the fresh data
So what do you want to do: revalidate when it’s needed (time based or manuelly) or a full dynamic page with cached functions?
@B33fb0n3 So what do you want to do: revalidate when it’s needed (time based or manuelly) or a full dynamic page with cached functions?
Wuchang breamOP
I would revalidate every 1 minute, during these 60 seconds there are no csr actions and users receive fast ISR page
after that the data gets checked and the first user updates ISR and data for himself
after that the data gets checked and the first user updates ISR and data for himself
something like that
@Wuchang bream I would revalidate every 1 minute, during these 60 seconds there are no csr actions and users receive fast ISR page
after that the data gets checked and the first user updates ISR and data for himself
Yea, then add a revalidation time of 60 to it. Then when you are within the 60 seconds, you get it fast (IRS cache). And it will automatically revalidate after 60 seconds and the next request will get stale-while-revalidate data and the next request after that gets new data. Getting instantly the new fresh data is not possible. You can add the 60 seconds revalidate like: „export const revalidate = 60“
@B33fb0n3 Yea, then add a revalidation time of 60 to it. Then when you are within the 60 seconds, you get it fast (IRS cache). And it will automatically revalidate after 60 seconds and the next request will get stale-while-revalidate data and the next request after that gets new data. Getting instantly the new fresh data is not possible. You can add the 60 seconds revalidate like: „export const revalidate = 60“
Wuchang breamOP
i would need some scheduled requests to keep fresh data in that way
or 1 to revalidate and 2nd to load
or can i revalidate and load the new data at once
@Wuchang bream or can i revalidate and load the new data at once
if you need nearly always dynamic data ISR is good, but a full dynamic page with cached funcitons sounds better. If I would be you, I would use a dynamic page
@B33fb0n3 if you need nearly always dynamic data ISR is good, but a full dynamic page with cached funcitons sounds better. If I would be you, I would use a dynamic page
Wuchang breamOP
but initial data wont exist and it would load slower, right?
and if i loaded data 1 second ago, i dont need it to load again so fast
you can cache functions and also use suspense boundaries. So even if you have a dynamic page and your dynamic function would take 10 seconds (!), with suspense boundaries it would still load it 0.0XXXms (very fast)
Wuchang breamOP
what would be shown to the user for these 10 seconds?
@Wuchang bream what would be shown to the user for these 10 seconds?
The component, that you pass to your suspense boundary would be visible:
<Suspense fallback={<LoadingUIOrSimilar />}>
<LongLoadingComponent />
</Suspense
@B33fb0n3 The component, that you pass to your suspense boundary would be visible:
tsx
<Suspense fallback={<LoadingUIOrSimilar />}>
<LongLoadingComponent />
</Suspense
Wuchang breamOP
but it wont update the fallback component?
@Wuchang bream but it wont update the fallback component?
the fallback is only a static shell around your dynamic component. It will switch automatically to the dynamic component (in the example the LongLoadingComponent) after the loading of the dynamic component is done
Wuchang breamOP
so if i have static "1" and the dynamic is "2" it just switches and next person has the same again
he doesnt start with static "2" and dynamic check if its still "2"
I cant follow you right now...
Wuchang breamOP
I load the page with data:
100 players online
When the component was loaded 10 hours and now there are 200 players
The user will see 100 players online, next user will see 200 players online
With your solution, the user will see 100 players online, then 200 players online
Next user will see the same, 100 and 200, when he could see the updated value of 200
100 players online
When the component was loaded 10 hours and now there are 200 players
The user will see 100 players online, next user will see 200 players online
With your solution, the user will see 100 players online, then 200 players online
Next user will see the same, 100 and 200, when he could see the updated value of 200
you are right. The next request after the validation see's
stale-while-revalidate
data. That's is default with nextjs and when using caching it's not possible to skip this. To prevent this, you need to not cache it and have instead a dynamic routeWuchang breamOP
thats not perfect
@Wuchang bream thats not perfect
create PR to change this behavior in next.js
American black bear
I use InstantDB for this.
Also if you need realtime players online, don't even ISR that number
@American black bear Also if you need realtime players online, don't even ISR that number
Wuchang breamOP
its not good?
@American black bear I use InstantDB for this.
Wuchang breamOP
i use redis but still
American black bear
If you need a realtime number a sync engine is the best bet
You never have to think about "refreshing" again
Everything is always up to date all the time and you don't have to think about it
A player signs in, immediately goes from 100 to 101 on everyone else's screen
@American black bear A player signs in, immediately goes from 100 to 101 on everyone else's screen
Wuchang breamOP
yes but you lose performance right?
American black bear
major performance win actually
If you haven't played around with InstantDB I highly suggest it especially for a realtime game
its free to use up to 1gb database size
unlimited bandwidth
@American black bear Everything is always up to date all the time and you don't have to think about it
sounds like a dynamic route
Wuchang breamOP
yes, i need the site to be super fast and lightweight, for users and search engines
right now i think of revalide isr + validate it again, can it be done in 1 call or without loading the whole site?
American black bear
Yea but you have to pay for server invocations for dynamic routes
You also have to manually invalidate them
It also requires you to code routes and data fetching logic etc
My solution is to use ISR for anything that needs to be indexed by Google and feed that as initialData to my InstantDB query hook. This also allows me to bundle my application for native apps with Tauri
It also enables realtime across all routes, if a user updates their profile it will instantly update for everyone anywhere they are viewing that user, whether that's in a game, in lobby, chat, notifications, etc
It also allows offline mutations for network drops on mobile connections, so you get optimistic results instantly for every mutation with no need to do useOptimistic etc
I don't think revalidateTag will cause other users pages to reload.. even then that would be an entire serverless invocation
Yea dynamic routes only reload for yourself if you revalidate the page or tag you're currently on
So that's not realtime multiplayer
Let me show you an example of a realtime blog with ISR and InstantDB
/app/blog/page.tsx
import { db } from "@/database/instant-admin"
import type { Article } from "@/types/entities"
import { Suspense } from "react"
import BlogView from "./view"
export const revalidate = 60 // ISR every 60 seconds for SEO
export const dynamic = "force-static"
export default async function BlogPage() {
if (provess.env.NEXT_PUBLIC_IS_EXPORT === "1") return <BlogView /> // We are running output: "export" build for native application
let articles: Article[] | undefined
try {
const data = await db.query({ articles: {} })
articles = data?.articles
} catch (error) {
console.log("[InstantDB] Error: ", error)
}
return (
<Suspense fallback={<BlogView />}>
<BlogView initialData={articles} />
</Suspense>
)
}
/app/blog/view.tsx
"use client"
import Image from "next/image"
import Link from "next/link"
import { useEffect } from "react"
import type { Article } from "@/types/entities"
import { db } from "@/database/instant-admin"
export default function BlogView({
initialData
}: {
initialData?: Article[]
}) {
const { data, isLoading } = db.useQuery({ articles: {} })
const articles = data?.articles || initialData
return (
<main className="container mx-auto max-w-3xl p-main">
{articles?.length ? (
<div className="grid gap-10 sm:grid-cols-2">
{articles.map((article, index) => (
<article
key={article.id}
className="group relative flex flex-col space-y-2"
>
{article.image && (
<Image
src={article.image}
alt={article.title}
width={804}
height={452}
className="rounded-md border bg-muted transition-colors"
priority
/>
)}
<h2 className="font-extrabold text-2xl">{article.title}</h2>
{article.description && (
<p className="text-muted-foreground">{article.description}</p>
)}
{article.createdAt && (
<p className="text-muted-foreground text-sm">
{article.createdAt.toLocaleString("en")}
</p>
)}
<Link href={`blog/${article.slug}`} className="absolute inset-0">
<span className="sr-only">View Article</span>
</Link>
</article>
))}
</div>
) : (
<p>No posts published.</p>
)}
</main>
)
}
With this setup, the initial page load is static, served super fast from edge cache, and refreshed every 60 seconds, this is great for SEO with Google. Then the view uses InstantDB useQuery hook to get the latest articles and uses those. This also skips Suspending to a loader because you can just use useQuery's isLoading, so if it's a subsequent visit you get an instant render from the local IndexedDB storage
If the article changes at any time, it is instantly updated for the user with no extra need to refetch or revalidate anything, and cached to IndexedDB automatically for subsequent page loads
This page technically doesn't even need a Skeleton for isLoading since the first render always has data
but a dynamic /blug/article-slug will need to use isLoading to show a Skeleton since it might not be perendered
Wuchang breamOP
but first load during cached isr may have old data
just like the next till the revalidation of isr
American black bear
It may but it will initially load and sync the correct data with useQuery
If it is a user profile for example, and you navigate there from a list of users, it will never be old data since it will use the recent data from the list
For something like a player count, I would not use ISR at all since Google doesn't need to index that number
@American black bear If it is a user profile for example, and you navigate there from a list of users, it will never be old data since it will use the recent data from the list
Wuchang breamOP
where is that part? it reloads the first component every 60 seconds and loads the fresh component everytime, right?
American black bear
useQuery syncs data in realtime
all the time
I highly suggest trying it out for yourself
I'm never fetching data again from the client, let's put it that way
Sync is future
Wuchang breamOP
ok but that part is a little bit slower
American black bear
If you want to see an example of InstantDB in action I have a demo here https://newtech.dev
Wuchang breamOP
but for example if you have 2 page enters in 2 seconds, you do that 2 times
thers no cache
American black bear
The Chat page and the blog comments are all realtime updated
Wuchang breamOP
i wouldnt need to check for fresh data every second
American black bear
You don't have to "check" for data, InstantDB just subscribes users to queries
The Todos page is also realtime, there's no email verification so you can make a fake email account
Wuchang breamOP
oh so its another level
American black bear
If you open 2 browser tabs and type into one of the todos on another tab you will see the value instantly updated
Wuchang breamOP
only chat messages are csr?
im thinking of improving that in my chat too
and like load only when its in the user's view
American black bear
The chat page also has typing indicators built in from InstantDB it's very cool
Wuchang breamOP
so just the data is csr
not whole window/component
American black bear
what do you mean CSR?
client side rendered?
The data is synced over websocket with InstantDB under the hood
Data that is purely dynamic and doesn't need to be indexed by Google can all be client side only via InstantDb useQuery (websocket)
or private user data that Google will never see (Todos)
Wuchang breamOP
yo what
American black bear
You only need SSR for SEO, like blog pages, home pages, etc
@American black bear client side rendered?
Wuchang breamOP
yes
@American black bear You only need SSR for SEO, like blog pages, home pages, etc
Wuchang breamOP
but you could render fast for users too right?
American black bear
There's nothing faster than rendering a static shell page from edge cache
So yea a user settings page should be purely static
They push SSR for $$
@American black bear They push SSR for $$
Wuchang breamOP
who?
American black bear
Nextjs docs etc
Wuchang breamOP
what you mean
American black bear
They profit off of you using dynamic SSR
Wuchang breamOP
how xd
American black bear
Vercel function invocations
Wuchang breamOP
btw making the components dynamic will make the site load faster? and making the client components as small as possible
@American black bear Vercel function invocations
Wuchang breamOP
but i have it on my dedicated server
American black bear
Nah, serving static components from cache is the fastest
I see
@American black bear Nah, serving static components from cache is the fastest
Wuchang breamOP
yea but the chat cant be serverside
American black bear
So you don't have edge cache?
Wuchang breamOP
and everywhere that the client can interact, yes?
@American black bear So you don't have edge cache?
Wuchang breamOP
cloudflare
American black bear
Oh then yea you don't want to use a web worker
unless absolutely necessary
Cloudflare serves static pages from cdn super fast
Wuchang breamOP
i use cf worker for chat
to not expose my api
American black bear
Yea I would just use InstantDB if its not too late
Cuz then you can serve the chat page from cdn cache
Wuchang breamOP
cached clientside component?
sounds like magic
American black bear
Yea
And useQuery syncs the chats realtime
from InstantDB directly
And you use their auth or integrate your existing auth
Wuchang breamOP
i would stick with redis or another local cache
but should i use dynamic imports and loading in the user's view?
and making the client component smaller, for example just messages, not the whole window?
American black bear
I'm not really sure what you mean
Wuchang breamOP
i mean the whole chat component is csr currently, the text, image, chat window, messages
i think about just doing the messages clientside and rest isr to improve the performance
American black bear
The chat page doesn't even need to be ISR? just static
server side rendered at build time as a shell
Wuchang breamOP
right
but there are buttons to pick the chat too
American black bear
You only need ISR for loading dynamic data that needs to be indexed for SEO
Wuchang breamOP
American black bear
what are you using for realtime messages?
Wuchang breamOP
exact implementation
eventsource
American black bear
I see
Wuchang breamOP
its just for reading it from the server, i dont need sending from the website at the moment and i would need to add login
but right now this whole component is csr
and my performance rating is around 50
when it was SSG and properly cached it was around 90
American black bear
Yea make it SSG
Also you have significant layout shift in your font
Wuchang breamOP
i think its just the translation
but yes this font could be preloaded better
for you i also recommend early hints, huge improvement when loading fonts etc
but idk how to split that because i have 3 client interactions: status, buttons and messages