Agno Agents
Scroll to Bottom
Recipe for showing a "new messages" button when users scroll up during streaming with Agno agents.
This recipe shows how to show a scroll-to-bottom button when the user has scrolled away from the latest messages when using Agno agents on the backend. You'll use the isAtBottom state from useAutoScroll to conditionally render a button that jumps to new content.
Problem
When users scroll up to read earlier messages, new streaming content appears below the visible area. You need to:
- Detect when the user is not at the bottom
- Show a button to quickly jump to new messages
- Maintain scroll position during streaming
- Provide clear visual feedback
Solution
Use useAutoScroll's isAtBottom return value combined with a positioned button:
import { useStreamChat, useAutoScroll } from "@deltakit/react";
function Chat() {
const { messages, sendMessage } = useStreamChat({ api: "/api/chat-agno" });
const { ref, scrollToBottom, isAtBottom } = useAutoScroll([messages]);
return (
<div className="relative flex h-screen flex-col">
{/* Scrollable message area */}
<div ref={ref} className="flex-1 overflow-y-auto p-4">
{messages.map((msg) => (
<div key={msg.id} className="mb-4">
<strong className="text-sm text-neutral-500">{msg.role}:</strong>
<div className="mt-1">
{msg.parts
.filter((p) => p.type === "text")
.map((p) => p.text)
.join("")}
</div>
</div>
))}
</div>
{/* Scroll to bottom indicator */}
{!isAtBottom && (
<button
onClick={scrollToBottom}
className="absolute bottom-20 left-1/2 -translate-x-1/2 rounded-full bg-neutral-800 px-4 py-2 text-sm text-neutral-200 shadow-lg hover:bg-neutral-700"
>
<span className="flex items-center gap-2">
<svg className="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 14l-7 7m0 0l-7-7m7 7V3" />
</svg>
New messages below
</span>
</button>
)}
{/* Input form */}
<form
className="border-t border-neutral-800 p-4"
onSubmit={(e) => {
e.preventDefault();
const input = e.currentTarget.elements.namedItem("message") as HTMLInputElement;
if (!input.value.trim()) return;
sendMessage(input.value);
input.value = "";
}}
>
<div className="flex gap-2">
<input
name="message"
placeholder="Type a message..."
className="flex-1 rounded border border-neutral-700 bg-neutral-800 px-3 py-2"
/>
<button type="submit" className="rounded bg-neutral-100 px-4 py-2 text-neutral-900">
Send
</button>
</div>
</form>
</div>
);
}How It Works
useAutoScrolltracks position — The hook monitors scroll position and setsisAtBottomtofalsewhen the user scrolls up- Conditional rendering — When
isAtBottomisfalse, show the indicator button - Jump to bottom — Clicking the button calls
scrollToBottom()to re-pin the view - Re-pins automatically — If the user scrolls back near the bottom,
isAtBottombecomestrueand the button hides
Customization
Badge with Message Count
Show how many new messages arrived while scrolled up:
const [lastSeenCount, setLastSeenCount] = useState(0);
const newMessageCount = messages.length - lastSeenCount;
// Update when scrolling to bottom
const handleScrollToBottom = () => {
scrollToBottom();
setLastSeenCount(messages.length);
};
// In render
{!isAtBottom && newMessageCount > 0 && (
<button onClick={handleScrollToBottom}>
{newMessageCount} new message{newMessageCount > 1 ? 's' : ''}
</button>
)}Animated Appearance
Add a smooth fade-in animation:
<button
className={`absolute bottom-20 left-1/2 -translate-x-1/2 transition-all duration-300 ${
isAtBottom ? 'opacity-0 translate-y-2 pointer-events-none' : 'opacity-100 translate-y-0'
}`}
>
New messages below
</button>Position Variants
Place the indicator in different locations:
// Bottom-right corner
<button className="absolute bottom-20 right-4 ...">
// Centered with full width
<button className="absolute bottom-20 left-4 right-4 ...">
// Floating action button style
<button className="absolute bottom-20 right-4 rounded-full w-12 h-12 ...">
<ArrowDownIcon />
</button>Best Practices
- Position carefully — Place above the input area so it doesn't overlap typing
- Clear labeling — Use "New messages" or show the count, not just an arrow
- Auto-dismiss — Hide immediately when user scrolls to bottom manually
- Respect user intent — Don't auto-scroll if the user has deliberately scrolled up to read
Related
- Auto Scroll Guide — Understanding the auto-scroll behavior
- useAutoScroll API — Full hook reference