mcpbeat

Dashboard Chat Best Practices

vercel-labs/dashboard-chat-best-practices

Apply dashboard-grade chat UX patterns in the Eve chat template. Use when changing Next.js Cache Components/PPR behavior, static shell layout, Suspense bootstrap boundaries, theme hydration, auth display hydration, signed-in/signed-out static shell behavior, optimistic messages, streaming row identity, composer/sidebar interactions, thinking/tool rendering, responsive styling, visual polish, or Vercel dashboard chat comparisons.

2k tokens
context cost
the whole folder, loaded on every use
2
files
instructions only
0
copies elsewhere
how many repositories repackaged it
127
stars on the repo
on the repository, not the skill itself

Install

one command, takes just this skill from the repository
npx skills add https://github.com/vercel-labs/eve-chat-template --skill dashboard-chat-best-practices

What comes with it

272 bytes besides the instruction
agents/openai.yaml

The instruction itself

11 sections, as written by the author

Dashboard Chat Best Practices

Overview

Use this skill to keep the template's web chat feeling like a polished dashboard surface: stable, quiet, fast to first paint, and predictable under streaming state.

Product Shape

  • Build the actual chat surface, not a marketing page. The first screen should be a usable composer, sidebar, or chat route.
  • Keep the aesthetic restrained and work-focused: dense but readable spacing, quiet borders, predictable controls, and minimal decoration.
  • Prefer existing shadcn/Tailwind primitives and local components over new abstractions.
  • Use icons for compact commands, with accessible labels/tooltips. Avoid explanatory in-app text for behavior that good controls can imply.

Cache Components And PPR Basics

  • Keep cacheComponents: true enabled in next.config.ts. Treat the chat route as a static shell with dynamic data streamed into Suspense boundaries.
  • Keep the shell cheap and deterministic. Do not make app/(chat)/layout.tsx wait on auth, database, chat history, or per-chat data before rendering the shell.
  • Pass cheap initial state into AgentChatShell: empty history, initialNextCursor: null, viewer: null, and getInitialSetupStatus().
  • Load expensive or request-bound data inside hidden Suspense children: ResolvedChatBootstrap for setup/viewer/sidebar history and ExistingChat for active chat data.
  • Use sync components to hand resolved server data to the client shell. AgentChatBootstrapSync should update setup status, viewer, and history. AgentChatRouteSync should update the active chat for the current route.
  • Keep Suspense fallbacks null for these sync boundaries when the visible shell already has stable placeholders. Do not add loading panels that push the chat frame around.
  • Do not call notFound() until the dynamic boundary has enough real viewer/setup/chat data to prove the chat is inaccessible. The outer shell should still render while that decision is loading.
  • Keep root and session routes visually distinct but structurally compatible. The root page can center the composer; the session page can pin it near the bottom. Both should live inside the same shell.
  • Treat PPR/static shell work as a latency feature and a correctness feature. The goal is first paint without auth/database blocking, then precise data reconciliation after bootstrap.

Static Shell And Hydration

  • Preserve the shell-first strategy. AgentChatShell should paint the sidebar frame, top controls, and route body before auth, setup, or chat history finishes loading.
  • Keep bootstrap data out of the blocking route path. Use hidden Suspense sync components to hydrate setup status, viewer, sidebar history, and active chat data.
  • Preserve theme pre-hydration. Theme should be resolved before React paints so dark/light mode does not flash or mismatch.
  • Preserve auth display pre-hydration. The eve_logged_in cookie is a display hint only; the head script sets data-eve-auth-display so CSS can hide the wrong signed-in/signed-out branch before viewer data resolves.
  • Never treat the auth hint as authority. Server actions, chat loading, Eve auth, and rate limits must use the real Better Auth session.
  • Keep suppressHydrationWarning only where it protects known pre-hydration document mutations such as theme/auth display hints.

Same Route, Signed In Or Out

  • Render one shared route shell for signed-in and signed-out states. Do not fork the page into separate auth routes just to show different controls.
  • While bootstrap is loading, render both possible placeholders inside AuthDisplayLoggedIn and AuthDisplayLoggedOut; let the document dataset hide the wrong branch.
  • For signed-out users, show the Sign in affordance immediately. For signed-in users, show a neutral user-menu placeholder instead of flashing Sign in.
  • After bootstrap resolves, render from real viewer state: viewer ? <UserMenu /> : <SidebarSignInButton />.
  • When a signed-out user submits text, save the draft to sessionStorage, open the auth modal, and restore the draft after sign-in.

Optimistic Messaging And Row Identity

  • Show an optimistic user bubble immediately on send, but keep persisted Eve events as the source of truth.
  • Clear the optimistic user bubble only when the reduced event log contains the real latest user message.
  • Keep live chat state centralized where possible. Avoid rendering from multiple competing sources in the same frame.
  • Prefer stable row identities for streaming responses. A "thinking" or "streaming reply" row should become the assistant row without remounting into a different component path.
  • Use a separate thinking presence only when there is no meaningful assistant text yet or a turn is still finalizing. Fade it out rather than abruptly removing it.
  • Avoid letting final snapshot writes fight live state. During streaming, prefer the live source; after settle, reconcile with the canonical snapshot.

Composer And Sidebar

  • Keep ChatComposer controlled by the page: value, onChange, onSubmit, disabled, disabledReason, isBusy, and isPreparing.
  • Enter should submit. Shift+Enter should insert a newline. Disabled states should expose actionable tooltip text.
  • Busy state should show Stop. Preparing state should show a spinner. Empty or over-limit input should disable send without moving layout.
  • Root and session pages can place the composer differently, but they should share the same component and control contract.
  • Persist desktop sidebar open state in a cookie and write a pre-hydration document hint to avoid open-then-collapse flicker.
  • Keep sidebar history cursor-paginated and optimistic. Use touchChat, removeChat, and title updates for sidebar state; do not make sidebar state the transcript source of truth.
  • Keep mobile sidebar overlay behavior separate from desktop persistent state.

Message Rendering

  • Render user messages as compact right-aligned bubbles. Render assistant text as full-width left-aligned prose.
  • Use Streamdown for assistant markdown. Keep code blocks, lists, and inline code readable in both themes.
  • Group related dynamic tool parts into compact rows. Expand details only when there is useful input, output, error, or HITL control.
  • Render reasoning as a collapsible block. Label active reasoning as "Thinking..." and settled reasoning as "Reasoning".
  • Render connection authorization as a structured card, not prose embedded in assistant text.
  • Keep text inside controls and cards from overflowing at mobile widths. Prefer stable dimensions for buttons, tool rows, counters, and composer controls.

Styling Rules

  • Use the existing token system: background, foreground, muted, border, card, accent, and local opacity patterns.
  • Keep cards modest. Do not nest cards inside cards. Use cards for messages, modals, repeated items, or framed controls, not whole-page decoration.
  • Keep radius consistent with the template. Avoid large pill shapes unless the local component already uses them.
  • Avoid large decorative gradients, orbs, blobs, and marketing-style hero layouts in the chat app.
  • Use lucide icons when an icon exists. Add aria-label for icon-only buttons and tooltip text where meaning is not obvious.
  • Avoid layout shifts from loading, hover, or stream state. Reserve stable space for composer controls, sidebar rows, auth placeholders, and thinking/tool rows.

Verification

  • Run pnpm typecheck and pnpm build after UI or app-router changes.
  • Manually check root signed-out, root signed-in, auth modal draft restore, first-message navigation, follow-up optimistic send, streaming assistant row, Connect/Skip auth cards, theme first paint, desktop sidebar cookie restore, and mobile sidebar overlay when touched.
  • Use screenshots or browser checks for visual changes across desktop and mobile. Watch specifically for auth flicker, theme flash, composer jumps, sidebar jumps, and message remount flicker.

How to use it

Copy the folder

Take vercel-labs/dashboard-chat-best-practices from the repository into ~/.claude/skills for personal use, or into .claude/skills inside a project.

Check the name does not clash

The agent identifies a skill by the name field in its header. Two skills with the same name cannot sit side by side — one of them will be ignored.