How the DefaultLayout Component Handles Responsive Resizable Panels in refine-shadcn
The DefaultLayout component in refine-shadcn uses a combination of media query hooks, memoized size calculations, and cookie persistence to create breakpoint-aware resizable panels that remember user preferences across sessions.
DefaultLayout serves as the primary layout shell for the refine-shadcn UI framework, integrating react-resizable-panels with responsive logic to deliver a fluid sidebar experience. By detecting viewport changes and dynamically adjusting panel constraints, the component ensures optimal space allocation across mobile, tablet, and desktop breakpoints while preserving user-defined layouts.
Responsive Breakpoint Detection
The component detects four viewport ranges—xs, sm, md, and lg—using the useMediaQuery hook from @react-hook/media-query. These breakpoints are defined at lines 38‑45 in packages/theme/src/layouts/default.tsx, enabling the layout to respond immediately to viewport changes without polling the DOM.
// From packages/theme/src/layouts/default.tsx (lines 38-45)
const isXl = useMediaQuery("(min-width: 1280px)");
const isLg = useMediaQuery("(min-width: 1024px)");
const isMd = useMediaQuery("(min-width: 768px)");
const isSm = useMediaQuery("(min-width: 640px)");
Each boolean flag feeds into downstream memo hooks, triggering recalculations of panel proportions whenever the user resizes their browser window.
Dynamic Panel Size Calculations
Two useMemo hooks compute the initial layout distribution and sidebar constraints based on the active breakpoint. The first hook (lines 51‑64) generates the initial split array for the horizontal panel group, assigning the sidebar a larger percentage on larger screens:
- xs:
[15, 85](15% sidebar) - sm:
[20, 80](20% sidebar) - md/lg/xl:
[25, 75](25% sidebar)
The second hook (lines 66‑88) calculates the sidebar's minimum and maximum sizes (SidebarSizes). On medium screens (md), for example, the sidebar constrains to minSize: 15 and maxSize: 25, while desktop views (xl) expand the range to minSize: 15, maxSize: 30.
Collapsible Sidebar State Management
A boolean state flag isCollapsed drives the collapsible behavior, initialized either from the defaultCollapsed prop or derived from media queries. The hasCollapsed memo (lines 90‑92) determines whether the sidebar should render in its collapsed state based on this flag.
When users interact with the resize handle, the onCollapse and onExpand callbacks toggle isCollapsed and persist the boolean to a cookie named react-resizable-panels:collapsed. The ResizablePanel component receives dynamic props including collapsedSize, collapsible, minSize, and maxSize to enforce these constraints visually.
Persisting User Preferences
User interactions are preserved across page reloads through two cookie mechanisms implemented in packages/theme/src/layouts/default.tsx:
- Layout persistence: The
onLayoutcallback (lines 33‑41) captures the new panel split percentages and writes them toreact-resizable-panels:layout - Collapse state: The collapse toggle functions write to
react-resizable-panels:collapsed
On subsequent mounts, react-resizable-panels reads these cookies automatically, restoring the exact sidebar width and collapsed state without additional configuration.
UI Primitive Integration
The actual resizable functionality relies on react-resizable-panels, wrapped in thin abstractions located in packages/theme/src/ui/resizable.tsx. These wrappers—ResizablePanelGroup, ResizablePanel, and ResizableHandle—inject Tailwind-compatible class names while preserving the underlying library's API.
The DefaultLayout renders a horizontal ResizablePanelGroup containing:
- A left
ResizablePanelfor the sidebar withdefaultSize={layout[0]}and collapse support - A
ResizableHandlewith an optional grip UI whenwithHandleis true - A right
ResizablePanelfor main content withdefaultSize={layout[1]}
Usage Examples
Basic Dashboard Implementation
Import DefaultLayout and provide the required props to enable responsive resizable panels:
// src/app/dashboard/page.tsx
import { DefaultLayout } from "@/layouts/default";
import Dashboard from "@/pages/Dashboard";
export default function DashboardPage() {
return (
<DefaultLayout
navCollapsedSize={4}
defaultCollapsed={false}
logo={{
default: <img src="/logo.svg" alt="Logo" />,
collapsed: <img src="/logo-mini.svg" alt="Mini" />,
}}
>
<Dashboard />
</DefaultLayout>
);
}
Custom Navigation and Footer
Inject custom elements into the layout's header and footer regions:
<DefaultLayout
navbar={{
leftSide: <UserMenu />,
rightSide: <SettingsButton />,
}}
footer={<span>© 2026 My Company</span>}
>
<YourPageContent />
</DefaultLayout>
The navbar prop renders inside the top-right header of the content panel, while footer anchors the bottom of the same panel.
Inspecting Persisted State
Verify that panel sizes survive reloads by checking the browser cookies:
# In browser console
document.cookie
# Output: "...react-resizable-panels:layout=[30,70];react-resizable-panels:collapsed=true;..."
Summary
- DefaultLayout combines
useMediaQueryhooks withuseMemocalculations to create breakpoint-responsive panel layouts - Panel splits are calculated dynamically in
packages/theme/src/layouts/default.tsx(lines 51‑88), allocating 15‑25% width to the sidebar depending on viewport size - User preferences persist automatically via cookies (
react-resizable-panels:layoutandreact-resizable-panels:collapsed) - The collapsible sidebar toggles between expanded and
navCollapsedSize(default 4%) states, storing the boolean in cookies - All resizable primitives are thin wrappers around react-resizable-panels located in
packages/theme/src/ui/resizable.tsx
Frequently Asked Questions
How does DefaultLayout determine the initial sidebar width on mobile devices?
On mobile devices (xs breakpoint below 640px), the layout memo in packages/theme/src/layouts/default.tsx (lines 51‑64) sets the initial split to [15, 85], giving the sidebar 15% of the container width by default. The SidebarSizes memo simultaneously constrains the panel to minSize: 15, maxSize: 20, preventing users from expanding the sidebar beyond 20% on small screens.
What cookie names does refine-shadcn use to remember panel layouts?
According to the source code in packages/theme/src/layouts/default.tsx, the component writes to two specific cookies: react-resizable-panels:layout stores the array of panel percentages (e.g., [25,75]), while react-resizable-panels:collapsed stores a boolean string indicating whether the sidebar is collapsed. These are read automatically by the underlying library on mount.
Can I disable the collapsible behavior while keeping the resizable panels?
Yes. The collapsible behavior is controlled by the isCollapsed state and the onCollapse/onExpand callbacks. To disable collapsing, you can omit the collapsible prop from the sidebar's ResizablePanel or set defaultCollapsed={false} and prevent the toggle UI from rendering. However, modifying this requires forking or wrapping the component, as the current implementation in lines 90‑103 of default.tsx tightly couples the resize handle with collapse functionality.
Which files should I modify to customize the resize handle appearance?
The visual styling of the resize handle is defined in packages/theme/src/ui/resizable.tsx. This file exports ResizableHandle, which wraps the primitive from react-resizable-panels and adds Tailwind CSS classes for the grip icon and hover states. Modifying this file changes the handle appearance across all layouts using the resizable primitives, while packages/theme/src/layouts/default.tsx controls the logical behavior (collapse on click, persistence, etc.).
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →