fix(studio): resolve layout shift on account preferences page (#42680)

## I have read the
[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
file.

YES

## What kind of change does this PR introduce?

Bug fix

## What is the current behavior?

Fixes #34967 

The `/account/me`
([https://supabase.com/dashboard/account/me](https://supabase.com/dashboard/account/me))
preferences page has layout shifting during profile loading.

## What is the new behavior?

- Loading skeletons now match the structure of the loaded content (one
card per dynamic section)
- Static sections only render in the non-error branch, preventing them
from appearing alongside error messages
- Removed unused `isSuccess` destructure from `useProfile()`
- CLS value now on good range <= 0.10.

<img width="1917" height="905" alt="Screenshot from 2026-02-11 00-39-19"
src="https://github.com/user-attachments/assets/1d8fa7e1-9ca3-49ae-b4b8-1c0f28ebbf93"
/>

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

## Summary by CodeRabbit

* **Refactor**
* Enhanced the loading state interface on the account settings page with
improved visual indicators. Updated the component structure for better
UI consistency and refined the visual feedback mechanism during profile
data retrieval.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Co-authored-by: Danny White <3104761+dnywh@users.noreply.github.com>
This commit is contained in:
Manuel Rubio
2026-04-09 19:42:03 -07:00
committed by GitHub
parent 0005d3742d
commit d1f54cd411

View File

@@ -1,4 +1,4 @@
import { Card, CardContent } from 'ui'
import { Card, CardContent, CardFooter } from 'ui'
import { PageContainer } from 'ui-patterns/PageContainer'
import {
PageHeader,
@@ -7,7 +7,15 @@ import {
PageHeaderSummary,
PageHeaderTitle,
} from 'ui-patterns/PageHeader'
import { GenericSkeletonLoader } from 'ui-patterns/ShimmeringLoader'
import {
PageSection,
PageSectionContent,
PageSectionDescription,
PageSectionMeta,
PageSectionSummary,
PageSectionTitle,
} from 'ui-patterns/PageSection'
import { ShimmeringLoader } from 'ui-patterns/ShimmeringLoader'
import { AccountConnections } from '@/components/interfaces/Account/Preferences/AccountConnections'
import { AccountDeletion } from '@/components/interfaces/Account/Preferences/AccountDeletion'
@@ -58,20 +66,12 @@ const PlatformPreferences = () => {
'profile:show_analytics_and_marketing',
'profile:show_account_deletion',
])
const { error, isLoading, isError, isSuccess } = useProfile()
const { error, isLoading, isError } = useProfile()
return (
<>
<PreferencesPageHeader description="Manage your account profile, connections, and dashboard experience." />
<PageContainer size="small">
{isLoading && (
<Card>
<CardContent className="p-4">
<GenericSkeletonLoader />
</CardContent>
</Card>
)}
{isError && (
<Card>
<CardContent className="p-4">
@@ -80,29 +80,110 @@ const PlatformPreferences = () => {
</Card>
)}
{isSuccess && (
{!isError && (
<>
{profileShowInformation ? <ProfileInformation /> : null}
<AccountIdentities />
{isLoading ? (
<ProfileLoadingSections showProfileInformation={profileShowInformation} />
) : (
<>
{profileShowInformation ? <ProfileInformation /> : null}
<AccountIdentities />
</>
)}
<AccountConnections />
<ThemeSettings />
<HotkeySettings />
<DashboardSettings />
{profileShowAnalyticsAndMarketing && <AnalyticsSettings />}
{profileShowAccountDeletion && <AccountDeletion />}
</>
)}
<AccountConnections />
<ThemeSettings />
<HotkeySettings />
<DashboardSettings />
{profileShowAnalyticsAndMarketing && <AnalyticsSettings />}
{profileShowAccountDeletion && <AccountDeletion />}
</PageContainer>
</>
)
}
const ProfileLoadingSections = ({
showProfileInformation,
}: {
showProfileInformation: boolean
}) => (
<>
{showProfileInformation && (
<PageSection>
<PageSectionMeta>
<PageSectionSummary>
<PageSectionTitle>Profile information</PageSectionTitle>
</PageSectionSummary>
</PageSectionMeta>
<PageSectionContent>
<Card>
<CardContent>
<ProfileFieldLoadingRow labelWidth="w-24" />
</CardContent>
<CardContent>
<ProfileFieldLoadingRow labelWidth="w-20" />
</CardContent>
<CardContent>
<ProfileFieldLoadingRow labelWidth="w-28" descriptionWidth="w-48" />
</CardContent>
<CardContent>
<ProfileFieldLoadingRow labelWidth="w-20" descriptionWidth="w-40" />
</CardContent>
<CardFooter className="justify-end">
<ShimmeringLoader className="h-8 w-16 rounded-md py-0" />
</CardFooter>
</Card>
</PageSectionContent>
</PageSection>
)}
<PageSection>
<PageSectionMeta>
<PageSectionSummary>
<PageSectionTitle>Account identities</PageSectionTitle>
<PageSectionDescription>
Manage the providers linked to your Supabase account and update their details.
</PageSectionDescription>
</PageSectionSummary>
</PageSectionMeta>
<PageSectionContent>
<Card>
<CardContent>
<ShimmeringLoader />
</CardContent>
</Card>
</PageSectionContent>
</PageSection>
</>
)
const ProfileFieldLoadingRow = ({
labelWidth,
descriptionWidth,
}: {
labelWidth: string
descriptionWidth?: string
}) => (
<div className="flex flex-col-reverse gap-2 md:gap-6 md:flex-row-reverse md:justify-between">
<div className="flex flex-col justify-center items-start md:items-end shrink-0 md:w-1/2 xl:w-2/5 md:min-w-100">
<ShimmeringLoader className="h-8 w-full rounded-md py-0" />
</div>
<div className="flex flex-col min-w-0 flex-grow">
<ShimmeringLoader className={`${labelWidth} h-4 py-0`} />
{descriptionWidth !== undefined && (
<ShimmeringLoader className={`${descriptionWidth} mt-2 h-3 py-0`} />
)}
</div>
</div>
)
const SelfHostedPreferences = () => {
return (
<>