From 26702fe078c05610e9bb1421857fb67d0456ed8e Mon Sep 17 00:00:00 2001 From: Thomas Camlong Date: Mon, 18 May 2026 14:47:53 +0200 Subject: [PATCH] perf(boards): remove loading overlay and show board immediately MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The board was hidden behind a LoadingOverlay until every section reported ready. This delays first paint unnecessarily — the server already prefetches widget data so the board can render on arrival. Also removes dead duplicate exports (updateBoardName, useUpdateBoard) that already live in @homarr/boards/updater. --- .../app/[locale]/boards/(content)/_client.tsx | 47 +------------------ .../boards/(content)/_ready-context.tsx | 13 ----- 2 files changed, 2 insertions(+), 58 deletions(-) diff --git a/apps/nextjs/src/app/[locale]/boards/(content)/_client.tsx b/apps/nextjs/src/app/[locale]/boards/(content)/_client.tsx index e9551af52..0ba502a9a 100644 --- a/apps/nextjs/src/app/[locale]/boards/(content)/_client.tsx +++ b/apps/nextjs/src/app/[locale]/boards/(content)/_client.tsx @@ -1,70 +1,27 @@ "use client"; -import { useCallback, useRef } from "react"; -import { Box, LoadingOverlay, Stack } from "@mantine/core"; +import { Box, Stack } from "@mantine/core"; -import type { RouterOutputs } from "@homarr/api"; -import { clientApi } from "@homarr/api/client"; import { useCurrentLayout, useRequiredBoard } from "@homarr/boards/context"; import { BoardCategorySection } from "~/components/board/sections/category-section"; import { BoardEmptySection } from "~/components/board/sections/empty-section"; import { BoardBackgroundVideo } from "~/components/layout/background"; -import { fullHeightWithoutHeaderAndFooter } from "~/constants"; -import { useIsBoardReady } from "./_ready-context"; - -let boardName: string | null = null; - -export const updateBoardName = (name: string | null) => { - boardName = name; -}; - -type UpdateCallback = (prev: RouterOutputs["board"]["getHomeBoard"]) => RouterOutputs["board"]["getHomeBoard"]; - -export const useUpdateBoard = () => { - const utils = clientApi.useUtils(); - - const updateBoard = useCallback( - (updaterWithoutUndefined: UpdateCallback) => { - if (!boardName) { - throw new Error("Board name is not set"); - } - utils.board.getBoardByName.setData({ name: boardName }, (previous) => - previous ? updaterWithoutUndefined(previous) : previous, - ); - }, - [utils], - ); - - return { - updateBoard, - }; -}; export const ClientBoard = () => { const board = useRequiredBoard(); const currentLayoutId = useCurrentLayout(); - const isReady = useIsBoardReady(); const fullWidthSortedSections = board.sections .filter((section) => section.kind === "empty" || section.kind === "category") .sort((sectionA, sectionB) => sectionA.yOffset - sectionB.yOffset); - const ref = useRef(null); - return ( - - + {fullWidthSortedSections.map((section) => section.kind === "empty" ? ( - // Unique keys per layout to always reinitialize the gridstack ) : ( diff --git a/apps/nextjs/src/app/[locale]/boards/(content)/_ready-context.tsx b/apps/nextjs/src/app/[locale]/boards/(content)/_ready-context.tsx index 606a296ea..709813f62 100644 --- a/apps/nextjs/src/app/[locale]/boards/(content)/_ready-context.tsx +++ b/apps/nextjs/src/app/[locale]/boards/(content)/_ready-context.tsx @@ -8,7 +8,6 @@ import { clientApi } from "@homarr/api/client"; import { useRequiredBoard } from "@homarr/boards/context"; const BoardReadyContext = createContext<{ - isReady: boolean; markAsReady: (id: string) => void; } | null>(null); @@ -18,7 +17,6 @@ export const BoardReadyProvider = ({ children }: PropsWithChildren) => { const board = useRequiredBoard(); const [readySections, setReadySections] = useState([]); - // Reset sections required for ready state useEffect(() => { return () => { setReadySections([]); @@ -36,7 +34,6 @@ export const BoardReadyProvider = ({ children }: PropsWithChildren) => { return ( @@ -54,13 +51,3 @@ export const useMarkSectionAsReady = () => { return context.markAsReady; }; - -export const useIsBoardReady = () => { - const context = useContext(BoardReadyContext); - - if (!context) { - throw new Error("BoardReadyProvider is required"); - } - - return context.isReady; -};