Kliping
KlipingBerandaDokumentasiKomponenBlocksVariasiTemplateDice UIHooks
Bagian
  • Pengenalan
  • Komponen
  • Instalasi
  • Tema & Kustomisasi
  • Mode Gelap
  • CLI
  • RTL
  • Skills
  • Server MCP
  • Form
  • Catatan Perubahan
Instalasi
  • Vite
  • Nuxt
  • Astro
  • Laravel
  • Manual
Mode Gelap
  • Vite
  • Nuxt
  • Vitepress
  • Astro
Form
  • VeeValidate
  • TanStack Form
  • Formisch
Registry
  • Ikhtisar
  • Memulai
  • Contoh
  • registry.json
  • registry-item.json
  • Tanya Jawab
Utilitas
  • scroll-fade
  • shimmer
Lainnya
  • components.json
  • JavaScript
  • Figma
  • Direktori Registry
  • Warna
  • Typeset
  • Dokumentasi Lama
  • Tentang
  • Kredit
  • Kontribusi
Komponen
  • Accordion
  • Alert
  • Alert Dialog
  • Aspect Ratio
  • Attachment
  • Avatar
  • Badge
  • Breadcrumb
  • Bubble
  • Button
  • Button Group
  • Calendar
  • Card
  • Carousel
  • Chart
  • Checkbox
  • Collapsible
  • Combobox
  • Command
  • Context Menu
  • Data Table
  • Date Picker
  • Dialog
  • Drawer
  • Dropdown Menu
  • Empty
  • Field
  • Form
  • Hover Card
  • Input
  • Input Group
  • Input OTP
  • Item
  • Kbd
  • Label
  • Marker
  • Menubar
  • Message
  • Message Scroller
  • Native Select
  • Navigation Menu
  • Number Field
  • Pagination
  • Pin Input
  • Popover
  • Progress
  • Questionnaire
  • Radio Group
  • Range Calendar
  • Resizable
  • Scroll Area
  • Select
  • Separator
  • Sheet
  • Sidebar
  • Skeleton
  • Slider
  • Sonner
  • Spinner
  • Stepper
  • Switch
  • Table
  • Tabs
  • Tags Input
  • Textarea
  • Toast
  • Toggle
  • Toggle Group
  • Tooltip
  • Typography

Message Scroller

SebelumnyaBerikutnya

Wadah gulir untuk transkrip percakapan: menahan posisi giliran bicara, mengikuti balasan yang mengalir, memulihkan riwayat yang disisipkan di atas, dan melompat ke pesan tertentu.

New Chat

How can I help you today?

Morning, shadcn!

What are we working on today? Press send to start a new conversation

I'm building a chat for our app and the scroll behavior is driving me nuts. Every time the AI streams a reply, the whole thread jumps around.
Demo is read only. Press send to send messages.

MessageScroller ​

Area gulir chat yang baik harus mengurus banyak hal sekaligus: menempel di ujung terbaru selagi balasan mengalir, tapi tidak melawan pembaca yang sedang menggulir ke atas; menambatkan tiap giliran bicara baru di dekat bagian atas dengan sedikit sisa percakapan sebelumnya; mempertahankan posisi saat riwayat lama dimuat di atasnya; serta menyediakan perintah untuk melompat ke mana pun di dalam utas. MessageScroller menangani bagian-bagian sulit itu, jadi daftar pesan Anda tidak perlu memikirkannya.

Ia tidak mengurus pesan, state AI, transport, atau model Anda — ia sekadar wadah gulir headless yang Anda bungkuskan pada baris-baris buatan Anda sendiri.

Instalasi ​

bash
npx shadcn-vue@latest add message-scroller

Salin dan tempel kode sumber di GitHub ke project Anda.

Sesuaikan path import dengan struktur project Anda.

Penggunaan ​

vue
<script setup lang="ts">
import {
  MessageScroller,
  MessageScrollerButton,
  MessageScrollerContent,
  MessageScrollerItem,
  MessageScrollerProvider,
  MessageScrollerViewport,
} from '@/components/ui/message-scroller'
</script>

<template>
  <MessageScrollerProvider auto-scroll default-scroll-position="last-anchor">
    <MessageScroller>
      <MessageScrollerViewport>
        <MessageScrollerContent>
          <MessageScrollerItem
            v-for="message in messages"
            :key="message.id"
            :message-id="message.id"
            :scroll-anchor="message.role === 'user'"
          >
            <!-- Message / Bubble / Marker goes here -->
          </MessageScrollerItem>
        </MessageScrollerContent>
      </MessageScrollerViewport>
      <MessageScrollerButton direction="end" />
    </MessageScroller>
  </MessageScrollerProvider>
</template>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

Provider-nya harus punya tinggi yang dibatasi (atau induk yang tingginya terbatas) supaya area tampilannya bisa digulir.

Komposisi ​

text
MessageScrollerProvider
└── MessageScroller
    ├── MessageScrollerViewport
    │   └── MessageScrollerContent
    │       └── MessageScrollerItem
    └── MessageScrollerButton

Konsep Dasar ​

Menambatkan Giliran Bicara ​

Giliran bicara adalah bagian percakapan yang memulai pertukaran baru — biasanya pesan pengguna beserta balasan asisten sesudahnya. Anchor adalah baris yang dianggap sebagai awal giliran tersebut. Tandai baris itu dengan scrollAnchor. Saat anchor baru ditambahkan, area tampilan memindahkannya ke dekat bagian atas dan menyisakan sedikit item sebelumnya di atasnya, supaya giliran baru itu tidak terasa terputus dari konteksnya.

vue
<MessageScrollerItem
  :message-id="message.id"
  :scroll-anchor="message.role === 'user'"
>
  <!-- ... -->
</MessageScrollerItem>

Scroll anchor tidak terikat pada peran pesan. Baris apa pun bisa Anda jadikan anchor: pesan pengguna, penanda sistem, peristiwa serah terima, atau apa pun yang memulai giliran bicara yang bermakna.

Anchoring Turns

Choose which role settles near the top edge.

No anchored messages yet

Send the first message to see the selected role anchor.

Toggle the anchor role, then send messages to compare where turns settle.

Percakapan Grup ​

Di percakapan grup, batas giliran bicara sering berupa pesan yang meminta model membalas, atau penanda seperti "Marcus bergabung ke percakapan". Indikator "sedang mengetik" dan kontrol riwayat biasanya tidak perlu jadi anchor. Karena penambatan tidak bergantung peran, penanda bisa dijadikan anchor semudah pesan biasa.

Group Chat

A group chat with several participants and an assistant. The Marker is marked as a turn.

@mary, the astrophage line keeps matching Venus energy output. Can you check my math?
Mary (Agent)
Yes. Confirmed. The curve points to a microorganism harvesting stellar energy and breeding near carbon dioxide. If @rocky agrees, this is the clue we need.
ping @rocky

This will create a marker and make it the anchor

When a user joins, a marker is created. scrollAnchor on the marker marks it as the next turn

Menjaga Konteks Tetap Terlihat ​

Saat giliran bicara baru dimulai, percakapannya harus tetap terasa menyambung. scrollPreviousItemPeek menyisakan sepotong item sebelumnya tetap terlihat di atas anchor, supaya pembaca tidak merasa percakapannya dimulai ulang di halaman kosong.

Keeping Context Visible

New turns keep part of the previous reply in view.

I'm building a chat for our app and the scroll behavior is driving me nuts. Every time the AI streams a reply, the whole thread jumps around.

That's the classic streaming scroll problem. Wrap your message list in `MessageScroller` and turn on `autoScroll` — the viewport pins to the bottom as tokens arrive, so users always see the latest text land in place.

The important part: it only auto-scrolls while the reader is already at the bottom. The moment they scroll up to read something earlier, auto-scroll backs off and their position is preserved. You get smooth streaming without fighting the user's intent.

Okay, but when someone sends a new message the view still feels jarring — like the whole conversation reloads from the top.
64px
Adjust the slider and send. Observe the previous message peek

Mengikuti Ujung Terbaru ​

Saat pembaca berada di ujung terbaru, autoScroll menjaga balasan yang mengalir tetap terlihat selagi bertambah panjang. Begitu pembaca menggulir menjauh — lewat roda tetikus, sentuhan, keyboard, atau menyeret scrollbar — tampilannya dilepas, jadi potongan baru tetap datang tanpa menggeser posisi baca. autoScroll bekerja berdampingan dengan penambatan giliran: saat giliran baru tertambat di dekat atas, tampilannya diam sementara balasan mengalir ke ruang di bawahnya.

Streaming Messages

Auto-scroll follows the live edge of the conversation.

Ready to Stream

Press send to stream a scripted launch summary.

I'm building a chat for our app and the scroll behavior is driving me nuts. Every time the AI streams a reply, the whole thread jumps around.
Streaming is simulated. `autoScroll` is enabled.

Membuka Utas Tersimpan ​

Membuka kembali utas tersimpan tepat di ujung paling akhir sering membuat pembaca kehilangan konteks. Nilai bawaan yang lebih baik adalah "last-anchor": tampilkan giliran bicara bermakna yang terakhir — misalnya pesan terbaru pengguna — beserta balasannya di bawahnya.

Opening Position

Choose where a saved transcript opens.

This is the first message the user sent in the conversation.

Workspace creation rose 8%, but first invite completion only rose 2%.

This is the last message the user sent in the conversation.

Start with the invite step. Teams are creating workspaces but waiting to add collaborators.

Recommended follow-up:

1. Compare invite drop-off by account size. 2. Check whether users who skip invites still return within 24 hours. 3. Review the empty-state copy on the first project screen. 4. Segment activation by template, since template users may not need invites right away.

If that pattern holds, the next experiment should make collaboration useful earlier instead of prompting for invites harder.

Toggle the defaultScrollPosition to see where the transcript starts when you open the thread

Memuat Pesan Sebelumnya ​

Memuat pesan lama tidak boleh menggeser percakapan yang sedang dibaca. Saat baris lama disisipkan di atas transkrip, MessageScrollerViewport mempertahankan baris yang terlihat supaya posisi baca tidak berubah selagi riwayat dimuat di atasnya. Perilaku ini aktif secara bawaan lewat preserveScrollOnPrepend.

Load History

Prepended messages keep your place.

Only the export queue worker changed. The deploy moved large CSV jobs onto the shared retry policy, which made each failed attempt hold a worker slot longer than before.

The app deploy did not include checkout, pricing, or billing API changes.

Do we need to roll back?

Not yet. Queue depth is recovering after we reduced retry concurrency, and the oldest pending job is now under five minutes old.

Keep rollback ready if the queue starts climbing again, but the current trend points toward recovery.

Keep watching for customer-visible issues.

I will watch the queue and support tags for another 15 minutes. I am tracking export failures, delayed download requests, and any support thread that mentions missing reports.

If those stay quiet through the next batch window, we can close this as an internal degradation.

End of Conversation

Restore earlier messages while keeping your place.

Click Load History to load the entire conversation

Menganimasikan Pesan Baru ​

Pola yang umum di aplikasi chat adalah menganimasikan pesan pengguna saat dikirim, lalu membiarkan balasan asisten mengalir ke baris biasa di bawahnya. Pertahankan messageId dan scrollAnchor pada item yang dianimasikan, dan pakai transform serta opacity untuk animasi masuknya — hindari menganimasikan height, margin, atau padding karena bisa berbenturan dengan penempatan posisi oleh scroller.

Animation

Choose how user messages are animated when they are added to the conversation.

No Messages Yet

Click the button below to send the first message.

Select an animation then click send to see it in action.

Melompat ke Pesan Tertentu ​

Hasil pencarian, permalink, daftar isi, dan tombol toolbar sering perlu menggerakkan transkrip dari luar daftar pesan. Pakai useMessageScroller untuk kontrol semacam itu — composable-nya membaca dari MessageScrollerProvider, jadi bisa dipakai di komponen mana pun di dalam provider tersebut.

vue
<script setup lang="ts">
import { useMessageScroller } from '@/components/ui/message-scroller'

const { scrollToMessage, scrollToEnd, scrollToStart } = useMessageScroller()
</script>

Commands

Drive the transcript from outside.

We're seeing activation dip after workspace creation. Can you help me find the likely step?

The sharpest drop is between creating the workspace and inviting the first teammate.

Workspace creation is still healthy, but the invite step is where users pause. That suggests the product is asking for collaboration before the user has enough confidence in the workspace.

What should I compare before we change the onboarding flow?

Compare three cohorts:

1. Users who choose a template before inviting teammates. 2. Users who start from a blank workspace. 3. Users who skip invites and return within 24 hours.

If template users invite faster, the fix is probably better first-run guidance rather than a louder invite prompt.

Can you turn that into an experiment?

Yes. Create a variant that shows a short checklist after workspace creation:

- Pick a template. - Add one project detail. - Invite a teammate when the workspace has context.

Measure first invite completion, 24-hour return rate, and whether teams create a second project.

What's the risk if we delay the invite prompt?

The main risk is reducing team creation for accounts that already know who they want to invite.

To protect that path, keep the invite action visible in the header and only change the primary empty-state guidance. That gives confident teams a direct route without forcing uncertain users through the invite step too early.

Use the controls to jump to any message in the conversation.

Melacak Posisi Baca Pengguna ​

Pakai useMessageScrollerVisibility untuk melacak posisi baca pengguna — misalnya daftar isi atau menu lompat yang menyorot giliran bicara yang sedang tertambat. currentAnchorId menjawab "saya sedang di mana" dan nilainya bertahan meski anchor itu sudah tergulir ke atas layar; visibleMessageIds menjawab "apa yang sedang tampil", sesuai urutan dokumen.

Transcript Outline

Track the current anchored turn.

Review the incident handoff and tell me what to read first.

Start with the summary and the impact section. The regression affected the upload queue, but the recovery path completed for every queued job.

What was the customer impact?

Impact was limited to delayed processing.

No records were dropped, and the reconciliation worker confirmed each retry batch. Support saw confusion from two customers, but there were no checkout or billing errors.

What actions are open?

Keep the retry window enabled until the next deploy, then add a queue-depth alert as the long-term fix.

The alert should fire on sustained queue growth, not a single short spike.

Give me the follow-up checklist.

After that, compare the queue recovery graph with the deploy timeline so the handoff shows exactly when processing returned to baseline. That makes it easier for support and engineering to answer the same customer questions without re-reading the whole incident thread.

I would also add a short owner note beside each follow-up item. The checklist is small, but ownership keeps the retry-window decision, alert tuning, and support macro from drifting into separate follow-up conversations.

Keep the retry window enabled until the next deploy, then add a queue-depth alert as the long-term fix.

The alert should fire on sustained queue growth, not a single short spike.

Open the outline to jump between anchored turns as you read.

Membaca State Gulir ​

Pakai useMessageScrollerScrollable kalau Anda butuh state gulir di JavaScript, misalnya untuk indikator status atau tombol "lompat ke terbaru" buatan sendiri. Ia melaporkan ke arah tepi mana saja area tampilan masih bisa digulir.

Scroll Status

Where the reader can scroll to based on current scroll position.

Review scroll checkpoint 1.

Checkpoint 2 is synced. The scrollable hook updates as the viewport moves.

When the reader is at the first message, the footer should only point them down. Once they move into the middle of the transcript, it should explain that both directions are available.

At the latest message, the footer should switch again and only point them back up.

Review scroll checkpoint 3.

Checkpoint 4 is synced. The scrollable hook updates as the viewport moves.

When the reader is at the first message, the footer should only point them down. Once they move into the middle of the transcript, it should explain that both directions are available.

At the latest message, the footer should switch again and only point them back up.

Review scroll checkpoint 5.

Checkpoint 6 is synced. The scrollable hook updates as the viewport moves.

When the reader is at the first message, the footer should only point them down. Once they move into the middle of the transcript, it should explain that both directions are available.

At the latest message, the footer should switch again and only point them back up.

Review scroll checkpoint 7.

Checkpoint 8 is synced. The scrollable hook updates as the viewport moves.

When the reader is at the first message, the footer should only point them down. Once they move into the middle of the transcript, it should explain that both directions are available.

At the latest message, the footer should switch again and only point them back up.

Review scroll checkpoint 9.

Checkpoint 10 is synced. The scrollable hook updates as the viewport moves.

When the reader is at the first message, the footer should only point them down. Once they move into the middle of the transcript, it should explain that both directions are available.

At the latest message, the footer should switch again and only point them back up.

Review scroll checkpoint 11.

Checkpoint 12 is synced. The scrollable hook updates as the viewport moves.

When the reader is at the first message, the footer should only point them down. Once they move into the middle of the transcript, it should explain that both directions are available.

At the latest message, the footer should switch again and only point them back up.

All messages fit in the viewport.
Scroll the transcript to see the footer update.

Referensi API ​

MessageScrollerProvider ​

Memegang state dan perilaku gulir. Sediakan lewat provide/inject supaya composable gulirnya bisa dipakai komponen turunan.

PropTypeDefaultDescription
autoScrollbooleanfalseFollow the live edge while the reader is pinned to the bottom.
defaultScrollPosition'start' | 'end' | 'last-anchor''end'Opening position for the transcript.
scrollEdgeThresholdnumber8Distance in px from an edge before it is considered scrollable.
scrollPreviousItemPeeknumber64Amount in px of the previous turn kept visible when anchoring.
scrollMarginnumber0Extra offset in px applied when scrolling to an element.

MessageScrollerViewport ​

PropTypeDefaultDescription
preserveScrollOnPrependbooleantrueKeep the current view when messages are added above.

Ditampilkan sebagai wadah gulir bawaan browser dengan role="region", aria-label="Messages", dan bisa di-focus (tabindex="0").

MessageScrollerItem ​

PropTypeDefaultDescription
messageIdstring—Stable id used for anchoring, visibility, jumps.
scrollAnchorbooleanfalseMarks this row as the start of a turn.

MessageScrollerButton ​

PropTipeBawaanDescription
direction'start' | 'end''end'Direction the button scrolls toward.
behaviorScrollBehavior'smooth'Scroll behavior for the jump.
variantButtonVariants'secondary'Button variant.
sizeButtonVariants'icon-sm'Button size.

Menyediakan data-active untuk keperluan style, dan menjadi inert dengan tabindex="-1" saat tidak ada lagi yang bisa digulir.

Composable ​

useMessageScroller() ​

ts
const { scrollToMessage, scrollToEnd, scrollToStart } = useMessageScroller()
  • scrollToMessage(id, options?) — menggulir ke item dengan messageId yang cocok. Mengembalikan true kalau berhasil ditangani (diantre kalau itemnya belum ter-mount), dan false kalau id-nya tidak ditemukan setelah semua baris ter-mount.
  • scrollToEnd(options?) / scrollToStart(options?) — menggulir ke ujung terbaru atau ke bagian paling atas.

useMessageScrollerVisibility() ​

ts
const visibility = useMessageScrollerVisibility()
// visibility.value.currentAnchorId, visibility.value.visibleMessageIds

Pelacakan hanya berjalan selama ada yang berlangganan, dan tiap baris perlu messageId supaya ikut terlacak.

useMessageScrollerScrollable() ​

ts
const scrollable = useMessageScrollerScrollable()
// scrollable.value.start, scrollable.value.end

Melaporkan ke arah tepi mana saja area tampilan masih bisa digulir. Untuk men-style scroller-nya sendiri, lebih baik pakai atribut data-scrollable.

MessageNative Select

Di halaman ini

MessageScrollerInstalasiPenggunaanKomposisiKonsep DasarMenambatkan Giliran BicaraPercakapan GrupMenjaga Konteks Tetap TerlihatMengikuti Ujung TerbaruMembuka Utas TersimpanMemuat Pesan SebelumnyaMenganimasikan Pesan BaruMelompat ke Pesan TertentuMelacak Posisi Baca PenggunaMembaca State GulirReferensi APIMessageScrollerProviderMessageScrollerViewportMessageScrollerItemMessageScrollerButtonComposable
Dirilis di bawah lisensi MIT. Ditulis ulang untuk Vue dari shadcn-vue, shadcn-ui-blocks, dan shadcn-hooks. Hak cipta © 2026 Kliping