Once the browser's data cache has been seeded from the server render, what decides whether the client trusts that entry or refetches it?
answer
- a seed is just a cache write
- as-of time, not insertion time
- trust, revalidate quietly, or refetch
- framework, data layer, author all decide
basics
~20 sA seeded entry is an ordinary cache entry: its worth depends on when the server actually read the value and on the freshness policy applied. Trust it while that window holds; revalidate when it lapses.
solid answer
~50 sSeeding is a cache write, so the same staleness rules govern it as govern anything the client fetched itself. Two things decide the outcome. First, the entry's **as-of time**: if the handoff records when the *server* read the value, the client can judge its real age; if it records the moment of insertion, a value read at build time or reused from a shared server-side copy looks brand new. Second, the **policy** — trust until something invalidates it, show it and revalidate in the background, or treat it as stale on arrival. Who sets that policy varies: the framework may pick a default for route-level reads, the data layer supplies a staleness model, and the author overrides per read. The common mistake is a default that refetches everything on startup, which pays for the seed and discards it.
go deeper
Hold on to one idea: the handed-over data is a starting value, not a permanent truth. Whether the browser asks again depends on how fresh that value is considered to be.
Explain the mechanics: the entry's recorded read time plus a freshness window plus revalidation triggers. Be ready to say why a prerendered seed stamped with the insertion time misleads the policy.
Show judgment per read rather than per app — trust build-time content, revalidate slow-moving data in the background, keep fast or personal data out of long-lived trust, and make writes invalidate.
Own the layering question: framework default, data-layer staleness model, and author override all set policy, and an app with no stated convention gets an accidental one that nobody can review.
Seeding gets the browser's first render for free. What happens *next* is a separate decision, and it is the one that distinguishes a page that settles from one that flickers or shows yesterday's numbers. ## A seed is a cache write, not a special mode Once the handoff has run, the entry is indistinguishable from one the client fetched itself. It has a key, a value, and whatever metadata the data layer keeps. Everything the layer does to ordinary entries — expiring them, revalidating them when the tab regains focus or the network reconnects, refetching on an interval, dropping them when a write invalidates them — applies here too. There is no separate rule for seeded data, and expecting one is where most confusion starts. What *is* special is the entry's provenance: it was produced by a different process, possibly at a very different time, and the browser has no way to know that unless the handoff tells it. ## The as-of time is the part people get wrong A cache entry is only judgeable if it carries the moment its value was read. A handoff carries one only if it is written to — and when it is not, the client stamps the entry with the moment of insertion, which is *now*. That is correct only when the server read the value during this very request. It is wrong whenever: - **The page was prerendered ahead of time.** The render, and therefore the seed, is as old as the build or the last regeneration — potentially hours or days. - **The server read from a copy it had already kept.** The value may have been fetched from the origin long before this request; the seed inherits that age, not the request's. - **The response streamed and the seed arrived late.** The value was read early in the render even though its payload reached the browser at the end. A seed stamped `now` in any of those cases is a lie the client cannot detect, and the user sees stale data that the freshness policy sincerely believes is fresh. ## Three policies, and who chooses | Policy | What the user sees | When it fits | |---|---|---| | Trust until something invalidates it | the seeded value, unchanged, until a write or an explicit invalidation replaces it | content that only changes when this app changes it | | Show the seed, revalidate in the background | the seeded value immediately, quietly replaced if the server's answer differs | most reads: no spinner, bounded staleness | | Treat as stale on arrival | the seeded value, then a refetch straight away — often a visible reload | data that moves fast enough that a second-old snapshot is worthless | Ownership is layered. The framework may choose a default for data returned by its route-level read. The data layer supplies the staleness model — how long an entry counts as fresh, what triggers revalidation. The author overrides per read, because the right answer differs between a marketing blurb and an account balance. On any real page all three are in play, which is why 'who set this policy?' is a fair interview follow-up. ## Matching the policy to the kind of data - **Build-time content** (documentation, marketing copy): trust the seed. Its whole point is that it does not change between deploys. - **Per-request but slow-moving data** (a product listing, a dashboard summary): show the seed and revalidate in the background. The user never waits, and the value converges within a moment. - **Fast-moving or personal data** (balances, queues, presence): keep the freshness window short, or do not seed it at all and fetch it in the browser where it can be refreshed on a schedule. - **Anything the user just changed**: the seed is pre-write. A write must invalidate the entry or the page will confidently redisplay the old value. ## How each mistake shows up - **Refetch-on-startup as a blanket default**: the page still shows a loading state over already-rendered content, and the seed's bytes were wasted. The fix is not to disable revalidation but to make it a *background* revalidate that leaves the seed on screen. - **Infinite trust on a prerendered seed**: the number on screen is as old as the last build and nothing in the client will ever correct it while the tab is open. - **An insertion-time stamp on an old value**: staleness windows are measured from the wrong instant, so a short window provides none of the protection it appears to. - **Seeding something you then discard**: if the policy refetches immediately regardless, drop the seed and save the payload.
- A page is prerendered ahead of time and its seed is trusted indefinitely. What does the user experience?They see the data as it was when the page was built, and nothing in the browser will correct it for as long as the tab is open. Either record the real read time so the freshness window is measured from it, or revalidate that entry in the background after the first paint.
- How do you keep a revalidation from undoing the benefit of seeding?Keep the seeded value on screen while the request is in flight and swap it only when a differing answer arrives. A revalidate that clears the entry first turns the seed back into a loading state, which is exactly the flicker the handoff was meant to prevent.
- The user submits a change and the page still shows the previous value. How does the seed contribute?The seed is a snapshot from before the write, so any read that still resolves against it displays pre-write data. The write path has to invalidate or replace that entry; relying on a time-based window means the stale value stands until the window happens to lapse.
saying these in an interview costs you the question
- Thinks seeded entries are exempt from the normal staleness rules.
- Stamps the seed with insertion time even for a prerendered render.
- Refetches everything on startup and calls the seed useless.
- Assumes the framework's default policy fits every read on the page.
- Believes a seeded value stays correct after the user writes to it.