ScrollArea ​

A scroll container with scrollbars that look the same in every browser. The scrollbars show while the pointer is over the area.

Item 1
Item 2
Item 3
Item 4
Item 5
Item 6
Item 7
Item 8
Item 9
Item 10
Item 11
Item 12
Item 13
Item 14
Item 15
Item 16
Item 17
Item 18
Item 19
Item 20
Item 21
Item 22
Item 23
Item 24
Item 25
Item 26
Item 27
Item 28
Item 29
Item 30
vue
<script setup lang="ts">
import { ScrollArea } from 'frappe-ui'

const items = Array.from({ length: 30 }, (_, i) => `Item ${i + 1}`)
</script>

<template>
  <ScrollArea class="h-64 w-64 rounded-5 border bg-surface-base">
    <div class="space-y-1 p-3">
      <div
        v-for="item in items"
        :key="item"
        class="rounded-4 px-2 py-1.5 text-base text-ink-gray-8 hover:bg-surface-gray-2"
      >
        {{ item }}
      </div>
    </div>
  </ScrollArea>
</template>

Examples ​

Wide table ​

orientation="both" lets a table scroll in both directions, with one scrollbar per axis.

Region JanFebMarAprMayJunJulAugSepOctNovDec
North America10k 23k 36k 49k 62k 75k 88k 11k 24k 37k 50k 63k
South America17k 30k 43k 56k 69k 82k 95k 18k 31k 44k 57k 70k
Western Europe24k 37k 50k 63k 76k 89k 12k 25k 38k 51k 64k 77k
Eastern Europe31k 44k 57k 70k 83k 96k 19k 32k 45k 58k 71k 84k
Middle East38k 51k 64k 77k 90k 13k 26k 39k 52k 65k 78k 91k
Africa45k 58k 71k 84k 97k 20k 33k 46k 59k 72k 85k 98k
South Asia52k 65k 78k 91k 14k 27k 40k 53k 66k 79k 92k 15k
East Asia59k 72k 85k 98k 21k 34k 47k 60k 73k 86k 99k 22k
Southeast Asia66k 79k 92k 15k 28k 41k 54k 67k 80k 93k 16k 29k
Oceania73k 86k 99k 22k 35k 48k 61k 74k 87k 10k 23k 36k
Central America80k 93k 16k 29k 42k 55k 68k 81k 94k 17k 30k 43k
Caribbean87k 10k 23k 36k 49k 62k 75k 88k 11k 24k 37k 50k
vue
<script setup lang="ts">
import { ScrollArea } from 'frappe-ui'

const months = [
  'Jan',
  'Feb',
  'Mar',
  'Apr',
  'May',
  'Jun',
  'Jul',
  'Aug',
  'Sep',
  'Oct',
  'Nov',
  'Dec',
]
const regions = [
  'North America',
  'South America',
  'Western Europe',
  'Eastern Europe',
  'Middle East',
  'Africa',
  'South Asia',
  'East Asia',
  'Southeast Asia',
  'Oceania',
  'Central America',
  'Caribbean',
]
const rows = regions.map((region, r) => ({
  region,
  values: months.map((_, m) => ((r * 7 + m * 13) % 90) + 10),
}))
</script>

<template>
  <ScrollArea
    orientation="both"
    class="h-72 w-full max-w-lg rounded-5 border bg-surface-base"
  >
    <table class="text-base text-ink-gray-8">
      <thead>
        <tr class="border-b text-ink-gray-5">
          <th class="whitespace-nowrap px-3 py-2 text-left font-medium">
            Region
          </th>
          <th
            v-for="month in months"
            :key="month"
            class="px-3 py-2 text-right font-medium"
          >
            {{ month }}
          </th>
        </tr>
      </thead>
      <tbody>
        <tr v-for="row in rows" :key="row.region" class="border-b">
          <td class="whitespace-nowrap px-3 py-2">{{ row.region }}</td>
          <td
            v-for="(value, i) in row.values"
            :key="i"
            class="px-3 py-2 text-right tabular-nums"
          >
            {{ value }}k
          </td>
        </tr>
      </tbody>
    </table>
  </ScrollArea>
</template>

Behavior ​

Orientation ​

orientation picks which scrollbars render. vertical (the default) and horizontal render one. both renders two, one per axis, for content that can overflow either way, such as a wide table or a canvas.

Hiding the scrollbars ​

The scrollbars hide scrollHideDelay milliseconds after the pointer leaves the area. The default is 600.

Viewport classes ​

The element that scrolls is inside the root, so a class on ScrollArea does not reach it. viewportClass adds classes to that element, for layout that has to sit on the scrolling element itself: padding, a grid, a minimum width, or h-full on its child.

Where it is used ​

ScrollArea is a general component, not only part of the app shell. DesktopShell uses it for its content area, and SettingsBody uses it for a settings panel.

API Reference ​

Show types
typescript
export interface ScrollAreaProps {
  /** Which scrollbars to render. */
  orientation?: 'vertical' | 'horizontal' | 'both'
  /** Idle delay (ms) before the thumb fades out. */
  scrollHideDelay?: number
  /** Extra classes for the scrolling viewport (e.g. padding). */
  viewportClass?: string
}

export interface ScrollBarProps {
  /** Which axis this bar scrolls. */
  orientation?: 'vertical' | 'horizontal'
}

export interface ScrollAreaExposed {
  /** The element that actually scrolls. Null until mounted. */
  viewportElement: HTMLElement | null
}
orientation
= "vertical"
"vertical" | "horizontal" | "both"

Which scrollbars to render.

scrollHideDelay
= 600
number

Idle delay (ms) before the thumb fades out.

viewportClass
string

Extra classes for the scrolling viewport (e.g. padding).

default
—

The scrolling content.

viewportElement
HTMLElement | null

The element that actually scrolls. Null until mounted.