Nuxt UI v3-alpha has been released!

Try it out

DashboardPanel

A responsive and resizable panel to build multi-column layouts.

Usage

The DashboardPanel component is the building block to create a multi-column layout.

<script setup lang="ts">
const links = [{
  label: 'Home',
  icon: 'i-heroicons-home'
}, {
  label: 'Inbox',
  icon: 'i-heroicons-inbox',
  badge: '4'
}, {
  label: 'Users',
  icon: 'i-heroicons-user-group'
}, {
  label: 'Settings',
  icon: 'i-heroicons-cog-8-tooth',
  children: [{
    label: 'General'
  }, {
    label: 'Members'
  }, {
    label: 'Notifications'
  }]
}]
</script>

<template>
  <UDashboardPanel :width="300" :resizable="{ min: 200, max: 400 }">
    <UDashboardNavbar>
      <template #left>
        <Logo class="w-auto h-5" />
      </template>
    </UDashboardNavbar>

    <UDashboardSidebar>
      <UDashboardSidebarLinks :links="links" />
    </UDashboardSidebar>
  </UDashboardPanel>
</template>

Width

By default, the DashboardPanel will take its content's width. You can use the width prop to set a fixed one.

<template>
  <UDashboardPanel :width="250" />
</template>

Grow

You can use the grow prop to make the DashboardPanel take the remaining space. It can be useful for the last panel on the right side of the layout.

<template>
  <UDashboardPanel grow />
</template>
When using the grow prop, the right border will be removed to make the layout look seamless.

Resizable

You can use the resizable prop to make the DashboardPanel resizable and set the min and max values to limit the resizing.

<template>
  <UDashboardPanel :width="300" :resizable="{ min: 200, max: 400 }" />
</template>

The width will be stored in a cookie to keep the layout consistent on refresh. You might want to use local storage instead by setting the storage key to local (this will only work if you've disabled ssr if your nuxt.config.ts).

<template>
  <UDashboardPanel :width="300" :resizable="{ min: 200, max: 400, storage: 'local' }" />
</template>

We use the useId composable to generate a unique id. When you have multiple resizable panels, it can be useful to set a custom one through the id prop.

<template>
  <UDashboardPanel id="inbox" :width="400" :resizable="{ min: 300, max: 500 }" />
</template>

When the resizable prop is used, a bar will be displayed on hover at the right of the panel. You can use the #handle slot to customize it as described in DashboardPanelHandle.

Collapsible

A two or three column layout will not be very usable on mobile. You can use the collapsible prop to transform the DashboardPanel into a Slideover on mobile.

<template>
  <UDashboardPanel collapsible />
</template>

In the useUIState composable, we store a isDashboardSidebarSlideoverOpen ref to control the state of the slideover on mobile. The DashboardPanel will inject this to the DashboardNavbar inside to display a toggle button.

If you want to control the state of the slideover yourself or if you have multiple panels, you can pass a v-model to the DashboardPanel.

<script setup lang="ts">
const selected = ref(null)

const isOpen = computed({
  get () {
    return !!selected.value
  },
  set (value: boolean) {
    if (!value) {
      selected.value = null
    }
  }
})
</script>

<template>
  <UDashboardPanel v-model="isOpen" collapsible />
</template>
It can be useful to change the icon of the toggle button as explained in DashboardNavbar.

Side

When using the collapsible prop, you can use the side prop to set the side of the panel. The default value is left.

<template>
  <UDashboardPanel v-model="isOpen" collapsible side="right" />
</template>

The DashboardPanel can be placed inside a DashboardLayout or a DashboardPage.

You can put a DashboardNavbar at the top followed by a DashboardSidebar, a DashboardToolbar or a DashboardPanelContent to display scrollable content for example.

layouts/default.vue
<template>
  <UDashboardLayout>
    <UDashboardPanel :width="250" :resizable="{ min: 200, max: 300 }" collapsible>
      <UDashboardNavbar />

      <UDashboardSidebar />
    </UDashboardPanel>

    <slot />
  </UDashboardLayout>
</template>
pages/inbox.vue
<script setup lang="ts">
const isOpen = ref(false)
</script>

<template>
  <UDashboardPage>
    <UDashboardPanel id="inbox" :width="400" :resizable="{ min: 300, max: 500 }">
      <UDashboardNavbar title="Inbox" />

      <UDashboardPanelContent />
    </UDashboardPanel>

    <UDashboardPanel v-model="isOpen" collapsible grow side="right">
      <UDashboardNavbar />

      <UDashboardToolbar />

      <UDashboardPanelContent />
    </UDashboardPanel>
  </UDashboardPage>
</template>

Props

id
string
undefined
ui
DeepPartial<{ wrapper: string; border: string; grow: string; collapsible: string; slideover: string; }>
{}
width
number
undefined
side
"right" | "left"
"left"
resizable
boolean | Record<string, any>
false
breakpoint
"sm" | "md" | "lg" | "xl" | "2xl"
"lg"
modelValue
boolean
undefined
grow
boolean
false
collapsible
boolean
false

Config

{
  wrapper: 'flex-col items-stretch relative w-full',
  border: 'border-b lg:border-b-0 lg:border-r border-gray-200 dark:border-gray-800 lg:w-[--width] flex-shrink-0',
  grow: 'flex-1',
  collapsible: 'hidden lg:flex',
  slideover: 'lg:hidden'
}