<< All versions

Skill v1.0.0

currentTrusted Publisher100/100
vercel/eve/dashboard-chat-best-practices
──Details
PublishedOctober 1, 2026 at 12:57 PM
Content Hashsha256:0b5c2230e8c39419...
Git SHA
──Files
Files (1 file, 8.3 KB)
SKILL.md8.3 KBactive
SKILL.md · 91 lines · 8.3 KB

version: "1.0.0" name: dashboard-chat-best-practices description: 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.


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.
All versions