# How the DefaultLayout Component Handles Responsive Resizable Panels in refine-shadcn

> Discover how refine-shadcn's DefaultLayout component manages responsive resizable panels using media queries, size calculations, and cookie persistence for a seamless user experience.

- Repository: [Ferdi ÜNAL/refine-shadcn](https://github.com/ferdiunal/refine-shadcn)
- Tags: deep-dive
- Published: 2026-03-01

---

**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`](https://github.com/ferdiunal/refine-shadcn/blob/main/packages/theme/src/layouts/default.tsx), enabling the layout to respond immediately to viewport changes without polling the DOM.

```tsx
// 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`](https://github.com/ferdiunal/refine-shadcn/blob/main/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`](https://github.com/ferdiunal/refine-shadcn/blob/main/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:

```tsx
// 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:

```tsx
<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:

```bash

# 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`](https://github.com/ferdiunal/refine-shadcn/blob/main/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`](https://github.com/ferdiunal/refine-shadcn/blob/main/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`](https://github.com/ferdiunal/refine-shadcn/blob/main/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`](https://github.com/ferdiunal/refine-shadcn/blob/main/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`](https://github.com/ferdiunal/refine-shadcn/blob/main/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`](https://github.com/ferdiunal/refine-shadcn/blob/main/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`](https://github.com/ferdiunal/refine-shadcn/blob/main/packages/theme/src/layouts/default.tsx) controls the logical behavior (collapse on click, persistence, etc.).