Dashboard Chat Best Practices
SkillCommunicationGuides your agent to build and refine a polished dashboard-style chat interface in Next.js using proven UX patterns.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the Dashboard Chat Best Practices skill
About this capability
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 identi
What this skill tells your AI
The instructions your AI receives, as published by vercel/eve in apps/templates/eve-chat-template/.agents/skills/dashboard-chat-best-practices/SKILL.md and read by ahel’s review.
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: trueenabled innext.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.tsxwait 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, andgetInitialSetupStatus(). - Load expensive or request-bound data inside hidden Suspense children:
ResolvedChatBootstrapfor setup/viewer/sidebar history andExistingChatfor active chat data. - Use sync components to hand resolved server data to the client shell.
AgentChatBootstrapSyncshould update setup status, viewer, and history.AgentChatRouteSyncshould update the active chat for the current route. - Keep Suspense fallbacks
nullfor 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.
AgentChatShellshould 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_incookie is a display hint only; the head script setsdata-eve-auth-displayso 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
suppressHydrationWarningonly 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
AuthDisplayLoggedInandAuthDisplayLoggedOut; 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
viewerstate: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
ChatComposercontrolled by the page:value,onChange,onSubmit,disabled,disabledReason,isBusy, andisPreparing. - 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-labelfor 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 typecheckandpnpm buildafter 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.
Signals
- GitHub stars
- 5k
- Forks
- 572
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
dashboard-chat-best-practices- Source
- github.com/vercel/eve