Files
supabase/apps/studio/components/ui/BootTimeoutFallback/BootTimeoutFallback.tsx
Monica Khoury 1b5a806963 fix: show support contact message if Studio fails to load (FE-4460) (#50872)
## Summary
- Adds a fallback message ("Taking longer than expected?... contact
support@supabase.io") shown after 7s if Studio fails to fully load, for
the Next.js runtime — mirrors the existing TanStack-only
`ShellFallback`, which had no Next.js equivalent
- Fixes the support email in the existing TanStack `ShellFallback` (was
`support@supabase.com`, should be `support@supabase.io`)
- Extracts the shared copy (message, email, delay) into one file so both
fallbacks stay in sync

## Why
Linear FE-4460: users reported the Dashboard going completely blank with
no way to reach support when a JS chunk failed to load. The Next.js
runtime (the current default) had no fallback at all for this case.

## Test plan
- [ ] Normal page load: fallback never appears
- [ ] Simulated stuck boot (mount signal disabled): fallback appears
after 7s with correct copy/email, no layout bugs
- [ ] Same two checks on the TanStack runtime
(`STUDIO_FRAMEWORK=tanstack`)
- [ ] `pnpm --filter studio run typecheck` / `lint:ratchet` pass

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
* Added a loading fallback that appears if the app takes too long to
load, with guidance to clear browser cookies and reload.
* On self-hosted platforms, the fallback includes a support contact
link.
  * The fallback is automatically hidden once the app loads.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-28 18:21:34 +03:00

76 lines
2.4 KiB
TypeScript

import {
BOOT_FALLBACK_MESSAGE,
BOOT_FALLBACK_REVEAL_DELAY_SECONDS,
BOOT_FALLBACK_SUPPORT_EMAIL,
} from '@/lib/boot-fallback-copy'
import { IS_PLATFORM } from '@/lib/constants'
const FALLBACK_ID = 'studio-boot-fallback'
// Next.js equivalent of the TanStack build's ShellFallback (see
// components/interfaces/App/ShellFallback.tsx): a safety net for a stuck
// chunk load (FE-4460). Next always server-renders real page content into
// <Main />, so — unlike TanStack's ClientOnly fallback, which shows nothing
// until hydration — this can't be a full-screen overlay shown by default; it
// has to reveal itself only once it's clear the page is actually stuck.
//
// The reveal is pure CSS (a delayed animation), not a JS timer, so it still
// fires even if every JS chunk fails to load. Hiding it on a successful
// mount necessarily needs JS — see `clearBootTimeoutFallback` — but a
// missed hide is harmless: the reveal only starts after
// BOOT_FALLBACK_REVEAL_DELAY_SECONDS, by which point a successful mount has
// long since hidden it.
export function BootTimeoutFallback() {
return (
<>
<style>{`
#${FALLBACK_ID} {
position: fixed;
inset: 0;
z-index: 9999;
display: flex;
align-items: center;
justify-content: center;
padding: 24px;
background: #1c1c1c;
pointer-events: none;
visibility: hidden;
opacity: 0;
animation: studio-boot-fallback-reveal 0.3s ease-out ${BOOT_FALLBACK_REVEAL_DELAY_SECONDS}s forwards;
}
#${FALLBACK_ID} p {
color: #fff;
font-family: sans-serif;
font-size: 14px;
text-align: center;
}
@keyframes studio-boot-fallback-reveal {
to {
visibility: visible;
opacity: 1;
pointer-events: auto;
}
}
`}</style>
<div id={FALLBACK_ID}>
<p>
{BOOT_FALLBACK_MESSAGE}
{IS_PLATFORM && (
<>
{' '}
If the problem persists, contact{' '}
<a href={`mailto:${BOOT_FALLBACK_SUPPORT_EMAIL}`}>{BOOT_FALLBACK_SUPPORT_EMAIL}</a>.
</>
)}
</p>
</div>
</>
)
}
/** Call once the app has actually mounted, to hide the fallback above. */
export function clearBootTimeoutFallback() {
const el = document.getElementById(FALLBACK_ID)
if (el) el.style.display = 'none'
}