mirror of
https://github.com/supabase/supabase.git
synced 2026-09-06 09:59:03 +08:00
The prerendered TanStack SPA shell (`_shell.html`) had a visually empty body, so every cold load showed a blank page until the JS bundle downloaded and hydrated. This bakes proper fallback states into the shell as static HTML — none of them rely on JS executing. **Added:** - `ShellFallback` component, rendered as the `ClientOnly` fallback around the root `<Outlet />` — during the shell prerender it serializes into `_shell.html`, and on the client it unmounts the moment the app mounts (no hydration mismatch: `ClientOnly` renders the fallback on the server and first client render) - Animated `LogoLoader` (Supabase logo outline) centered on screen — the stroke-dash animation is pure CSS so it runs before any JS executes - Stuck-load help text that fades in after 7s via CSS `animation-delay` (clear cookies / reload, contact support@supabase.com — the support email is gated behind `IS_PLATFORM` so self-hosted builds don't get it) - `noscript` message for JS-disabled browsers, which also hides the loader so users don't see an infinite spinner (uses `dangerouslySetInnerHTML` so React hydration never diffs noscript children) - `data-nosnippet` on both text blocks so Google doesn't surface the boilerplate as the search snippet for dashboard URLs (the one shell serves every route) ## To test All on the Vercel preview: - Open the preview — on a cold load you should catch the animated logo loader before the app mounts (throttle to "Slow 4G" in devtools if it flashes by too fast), and it never reappears on client-side navigation - In devtools, block the JS bundle (Network tab → right-click the `/assets/index-*.js` request → Block request URL) and reload — the loader animates on its own, and the help text (clear cookies / contact support) fades in after ~7s - Disable JavaScript (devtools command palette → "Disable JavaScript") and reload — no spinner, just the "requires JavaScript" message - View page source (or `curl` any preview URL) — the body contains the logo SVG, the help text, and the noscript block, all with `data-nosnippet` on the text <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added a client-aware loading shell for Studio during initialization. * Shows a branded loader with a help message that appears after a short delay. * Includes platform-specific support contact details when available. * **Bug Fixes** * Prevents partial or incomplete content from rendering before the app is ready. * Improves consistency for no-JavaScript fallback rendering to avoid hydration mismatches. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Alaister Young <10985857+alaister@users.noreply.github.com>
74 lines
2.6 KiB
TypeScript
74 lines
2.6 KiB
TypeScript
import { LogoLoader } from 'ui'
|
|
|
|
import { IS_PLATFORM } from '@/lib/constants'
|
|
|
|
// Baked into the prerendered SPA shell (_shell.html) — this is the static HTML
|
|
// shown on every cold load until the JS bundle hydrates, so nothing here can
|
|
// rely on JS: the loader animation, the 7s stuck-load help reveal, and the
|
|
// noscript message are all pure CSS/HTML.
|
|
export function ShellFallback() {
|
|
return (
|
|
<>
|
|
<style>{`
|
|
/* visibility (not just opacity) so the text is also hidden from
|
|
screen readers until the reveal. */
|
|
#studio-shell-help {
|
|
visibility: hidden;
|
|
opacity: 0;
|
|
animation: studio-shell-help-reveal 0.3s ease-out 7s forwards;
|
|
}
|
|
@keyframes studio-shell-help-reveal {
|
|
to {
|
|
visibility: visible;
|
|
opacity: 1;
|
|
}
|
|
}
|
|
`}</style>
|
|
<div
|
|
id="studio-shell-loader"
|
|
className="fixed inset-0 flex flex-col items-center justify-center gap-8 p-4"
|
|
>
|
|
{/* LogoLoader fills its parent, so give it a fixed-height box — otherwise
|
|
it stretches to the full column and pushes the help text offscreen. */}
|
|
<div className="h-[62px] w-full">
|
|
<LogoLoader />
|
|
</div>
|
|
{/* data-nosnippet keeps Google from surfacing this boilerplate as the
|
|
search snippet for dashboard URLs (the shell serves every route). */}
|
|
<p
|
|
id="studio-shell-help"
|
|
data-nosnippet=""
|
|
className="max-w-md text-center text-sm text-foreground-light"
|
|
>
|
|
Taking longer than expected? Try clearing your browser cookies and reloading the page.
|
|
{IS_PLATFORM && (
|
|
<>
|
|
{' '}
|
|
If the problem persists, contact{' '}
|
|
<a href="mailto:support@supabase.com" className="underline">
|
|
support@supabase.com
|
|
</a>
|
|
.
|
|
</>
|
|
)}
|
|
</p>
|
|
</div>
|
|
{/* dangerouslySetInnerHTML keeps React hydration from diffing noscript
|
|
children, which the server renders as real markup but the client
|
|
treats as raw text. */}
|
|
<noscript
|
|
dangerouslySetInnerHTML={{
|
|
__html: `
|
|
<style>#studio-shell-loader{display:none}</style>
|
|
<div data-nosnippet class="fixed inset-0 flex items-center justify-center p-4">
|
|
<p class="text-sm text-foreground-light text-center">
|
|
Supabase Studio requires JavaScript. Enable JavaScript in your browser settings and reload the page.
|
|
</p>
|
|
</div>
|
|
`,
|
|
}}
|
|
/>
|
|
</>
|
|
)
|
|
}
|