Vibes DIY
Vibes DIY / Docs
creator docs · concepts · serving

How your app is served

We treat one number as the product: how fast someone first sees your app. Most of the machinery below exists to make that instant — but it also has behavior you'll notice as a creator ("I pushed a fix, why does the public URL still show the old one?"), so here is the whole path, plainly.

Two documents, one view

A vibe view involves two pages:

  • The viewer wrappervibes.diy/vibe/you/your-app. It draws the frame around your app (the Vibes chrome, the remix affordances) and hosts an iframe.
  • Your app's own document — served from its sandbox origin (your-app--you.…). This is the HTML your App.jsx becomes.

The wrapper pins a version onto every iframe it renders: a query param that tells your app exactly which build of the platform runtime to load, stamped per deploy. That pin is why a platform release cuts over instantly — a new deploy mints a new stamp, which is a guaranteed cache miss, and every view after it loads the fresh runtime.

The first paint is server-rendered — with your data in it

Your app's HTML doesn't arrive empty and then fetch. The server renders it with real content already inside:

  • The data is the anonymous view. The rendered page may be cached and shown to anyone, so the seed is exactly what a signed-out reader is allowed to see — resolved through the very same access rules that govern live reads. A signed-in visitor gets that public first paint, then their own view the moment the app hydrates.
  • The styles come compiled. The utility classes your app's first frame uses are compiled on the server and inlined, and the active theme's full variable block — light and dark tokens — is inlined too. Your app's own theme code still loads and wins afterward; the server block is a floor, not a ceiling. (Dark mode is a second rendering target, not a color preference. We learned that one the hard way.)
  • World-readable apps paint immediately. If your app is public, there is no loading overlay while the permission check runs — the first paint is the app. The "not available" card appears only if the check comes back negative.

If you see the gray loading grid, it means exactly what it says: the platform is loading your app. It is the honest signal in both color schemes — not a crash.

The edge cache, and how fresh it keeps things

Public vibe HTML is served through a stale-while-revalidate cache at the edge:

  • Fresher than about ten minutes: served as-is, in milliseconds.
  • Ten minutes to a day old: served immediately, and re-rendered in the background so the next visitor gets the newer copy.
  • Older than a day: rendered fresh on the spot.

Entries are keyed per vibe, per platform deploy, per edge location — so different deploys never share a slot, and the first view after a release pays one full render, exactly what every view used to pay.

What this means for you in practice:

  • A just-pushed change can lag briefly on the public URL. Anonymous visitors may see the previous version for up to about ten minutes (usually one stale view triggers the background refresh and the copy after that is current). Your own signed-in session shows your live app after hydration — so "looks right to me, old for my friend" for a few minutes after a push is the cache working, not your push failing.
  • Data changes propagate through validation. The page's validator covers your documents, not just your code — a returning visitor's browser revalidates and picks up new content instead of being told "unchanged" forever.

What is never cached

  • Access-gated apps. Eligibility is decided at store time by the serve path that actually knows your app's visibility — nothing gated ever enters the cache, so a cache hit cannot leak by construction. One nuance when you flip a public app private: the edge stops storing new copies immediately and the location that processed the change purges right away, but other edge locations clear on their next refresh — so a previously cached public copy can still be served elsewhere for up to about ten minutes after the flip.
  • Personalized requests. Point a vibe at a custom package workspace and every view of it bypasses the cache entirely. Only the platform's own version pin is treated as part of the cache key; anything else that looks personal opts the request out.

What you might notice

  • First anonymous load right after a platform deploy takes a few seconds (the one full render), then everyone after gets the fast copy.
  • First paint of a heavy app can take a while on a cold view — grid, then app. Give it a beat before assuming something is wrong.
  • Your app's /_api/... endpoint (if you ship a backend) is live compute, not cached HTML — none of the above applies to it.

Something serving weird that this page doesn't explain? Ask in Discord or open an issue at VibesDIY/use-vibes.