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:

  1. Layout persistence: The onLayout callback (lines 33‑41) captures the new panel split percentages and writes them to react-resizable-panels:layout
  2. 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 ResizablePanel for the sidebar with defaultSize={layout[0]} and collapse support
  • A ResizableHandle with an optional grip UI when withHandle is true
  • A right ResizablePanel for main content with defaultSize={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>
  );
}

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 useMediaQuery hooks with useMemo calculations 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:layout and react-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.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →