[PR #14515] app/ui: skip sidebar transition animation on initial page load #45955

Open
opened 2026-04-25 01:32:48 -05:00 by GiteaMirror · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/ollama/ollama/pull/14515
Author: @umutkeltek
Created: 2/28/2026
Status: 🔄 Open

Base: mainHead: fix/sidebar-animation-on-load


📝 Commits (1)

  • aab857b app/ui: skip sidebar transition animation on initial page load

📊 Changes

1 file changed (+15 additions, -5 deletions)

View changed files

📝 app/ui/app/src/components/layout/layout.tsx (+15 -5)

📄 Description

Problem

When the app loads with sidebarOpen persisted as true in settings, CSS transition classes cause the sidebar to visibly animate from closed → open. This happens because:

  1. useSettings() uses useQuery, so sidebarOpen initially defaults to false while the async settings fetch is in-flight
  2. When the query resolves with sidebarOpen: true, the sidebar width changes from w-0w-64
  3. All five transition classes (transition-[width], transition-[left], transition-opacity, transition-all) are applied unconditionally, so the browser animates this state change

The result is a slide-open animation every time the app loads — the sidebar should appear instantly in its saved state.

Solution

Defer CSS transition classes until after the first paint using a hasMounted state flag:

const [hasMounted, setHasMounted] = useState(false);

useEffect(() => {
  requestAnimationFrame(() => {
    setHasMounted(true);
  });
}, []);

All five transition class locations are conditionally applied:

className={`... ${hasMounted ? "transition-[width] duration-300" : ""} ...`}
  • Before mount: No transition classes → sidebar renders at its correct width instantly
  • After mount: Transition classes applied → sidebar toggle animates smoothly as before

Using requestAnimationFrame (instead of bare useEffect) ensures the transition suppression survives the full browser paint cycle, preventing any flash of animation even on slower renders.

Changes

  • app/ui/app/src/components/layout/layout.tsx — 1 file, +15/-5 lines
    • Added hasMounted state with useEffect + requestAnimationFrame
    • Conditionally applied all 5 transition classes only when hasMounted === true

Testing

  • TypeScript: tsc -b — zero errors
  • Vitest: 27/27 tests passing, zero regressions
  • Vite build: builds successfully
  • ESLint: passes
  • Prettier: passes

Behavior

Scenario Before After
App loads with sidebar open Sidebar animates open (slide from left) Sidebar appears instantly
User toggles sidebar after load Smooth animation Smooth animation (unchanged)
App loads with sidebar closed No animation (already closed) No animation (unchanged)

Fixes #12954


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/ollama/ollama/pull/14515 **Author:** [@umutkeltek](https://github.com/umutkeltek) **Created:** 2/28/2026 **Status:** 🔄 Open **Base:** `main` ← **Head:** `fix/sidebar-animation-on-load` --- ### 📝 Commits (1) - [`aab857b`](https://github.com/ollama/ollama/commit/aab857bff94ef44aa6896998843732db7c86dd8e) app/ui: skip sidebar transition animation on initial page load ### 📊 Changes **1 file changed** (+15 additions, -5 deletions) <details> <summary>View changed files</summary> 📝 `app/ui/app/src/components/layout/layout.tsx` (+15 -5) </details> ### 📄 Description ## Problem When the app loads with `sidebarOpen` persisted as `true` in settings, CSS transition classes cause the sidebar to visibly animate from closed → open. This happens because: 1. `useSettings()` uses `useQuery`, so `sidebarOpen` initially defaults to `false` while the async settings fetch is in-flight 2. When the query resolves with `sidebarOpen: true`, the sidebar width changes from `w-0` → `w-64` 3. All five transition classes (`transition-[width]`, `transition-[left]`, `transition-opacity`, `transition-all`) are applied unconditionally, so the browser animates this state change The result is a slide-open animation every time the app loads — the sidebar should appear instantly in its saved state. ## Solution Defer CSS transition classes until after the first paint using a `hasMounted` state flag: ```tsx const [hasMounted, setHasMounted] = useState(false); useEffect(() => { requestAnimationFrame(() => { setHasMounted(true); }); }, []); ``` All five transition class locations are conditionally applied: ```tsx className={`... ${hasMounted ? "transition-[width] duration-300" : ""} ...`} ``` - **Before mount:** No transition classes → sidebar renders at its correct width instantly - **After mount:** Transition classes applied → sidebar toggle animates smoothly as before Using `requestAnimationFrame` (instead of bare `useEffect`) ensures the transition suppression survives the full browser paint cycle, preventing any flash of animation even on slower renders. ## Changes - `app/ui/app/src/components/layout/layout.tsx` — 1 file, +15/-5 lines - Added `hasMounted` state with `useEffect` + `requestAnimationFrame` - Conditionally applied all 5 transition classes only when `hasMounted === true` ## Testing - **TypeScript:** `tsc -b` — zero errors - **Vitest:** 27/27 tests passing, zero regressions - **Vite build:** builds successfully - **ESLint:** passes - **Prettier:** passes ## Behavior | Scenario | Before | After | |----------|--------|-------| | App loads with sidebar open | Sidebar animates open (slide from left) | Sidebar appears instantly | | User toggles sidebar after load | Smooth animation | Smooth animation (unchanged) | | App loads with sidebar closed | No animation (already closed) | No animation (unchanged) | Fixes #12954 --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
GiteaMirror added the pull-request label 2026-04-25 01:32:48 -05:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/ollama#45955