1. Vercel
  2. /
  3. Why Vercel AI SDK chat history disappears between sessions
8 min readPublished May 6, 2026

Why AI chat history disappears between sessions

TL;DR useChat stores messages in React component state. When the component unmounts - on navigation, reload, or tab close - the history is gone. The AI SDK's built-in stream resumption covers one case: reconnecting to a generation that is still in progress after a page reload. Returning to a completed conversation is a different problem. A channel with persistent history covers initial load, live updates, and reconnect catch-up in a single mechanism, without separate infrastructure for each.

By default, useChat keeps messages in React component state. That means a page reload wipes them. There's no automatic write to localStorage or a database. Vercel made this a deliberate non-decision: how you store and retrieve conversation history depends on your stack and your sync requirements. The SDK doesn't pick for you.

Copy link to clipboard

What stream resumption covers

The AI SDK's built-in stream resumption covers one specific scenario: a page reload during an active generation. If you reload mid-stream, the resume option in useChat can reconnect to the active stream stored in Redis and pick up from where delivery stopped.

If the generation has already finished, there's nothing to reconnect to. The history doesn't exist anywhere to resume from. Coming back to a completed conversation needs a persistence layer. That's a different thing to build.

For a full breakdown of what stream resumption covers and where it stops, see Vercel AI SDK resumable-stream: what it covers and what it doesn't.

Copy link to clipboard

Storing messages yourself

The most common approach: write messages to a database after each exchange, then load them into useChat via the messages prop on the next visit. The AI SDK documentation covers this pattern in its message persistence guide.

It works well enough until requirements get more specific. A second device viewing the same conversation in realtime needs a sync layer on top. A colleague picking up the thread needs access control and live updates. A user who dropped out and reconnected needs you to decide what to replay and from where. None of these are included.

Most teams hit the same realization at roughly the same point: they've ended up building several things that could have been one.

Copy link to clipboard

Two message formats, one round trip

If you persist messages yourself, the AI SDK doesn't convert between the two message formats for you. useChat works with UIMessage objects, the shape the frontend renders. The model APIs work with ModelMessage objects, the shape sent to and returned from the LLM. Saving a conversation means going from UIMessage to ModelMessage; loading it back means going the other way.

The AI SDK provides convertToModelMessages() for the outbound direction, but the return trip, turning stored ModelMessage data back into something useChat can render, is not a single documented function call. Teams that build their own persistence layer typically hit this in exactly the place you would expect: it works for saving, then breaks on load, because the format the database has is not the format the messages prop expects.

This is a big enough source of friction that it is one of the most-discussed persistence issues in the AI SDK's GitHub repository (issue #7180: 21 reactions, 34 comments), with developers describing it as three incompatible formats to reconcile rather than one. It is also the reason a channel-based transport with native history avoids the problem rather than solving it: if the transport replays messages in UIMessage shape directly, there is no conversion step to get wrong on the way back in.

Copy link to clipboard

Using a channel with persistent history

A channel with history stores every message as it's published and makes it available to subscribers. Persistent history is one piece of durable sessions for Vercel AI SDK applications. A client that connects late, or drops and reconnects, gets the messages it missed from where it left off.

That takes care of a few things at once: the initial load, live updates, reconnect catch-up, and syncing across devices. You're not managing separate systems for each. The session state lives in the channel rather than spread across a database, a polling service, and a WebSocket connection.

Most purpose-built realtime platforms provide channel history as part of their infrastructure. Retention windows vary by provider and plan.

Copy link to clipboard

Why this matters more for AI applications

Message loss matters more in AI applications than in most chat apps. In a regular chat, losing a few messages on reconnect is annoying. In an AI conversation, losing the history means the model loses its context. A response generated on day one shapes the question you ask on day two. If that's gone, the next generation is working without the reasoning chain that makes the conversation coherent.

There are a couple of other scenarios worth keeping in mind. AI responses take time. People often come back to them later, on a different device or shared with a colleague. And if you're running agents that take minutes to complete, you need those results to reach the client even if the user navigates away mid-run.

Some further reading:

Copy link to clipboard

Connecting channel history to useChat

The ChatTransport interface in the AI SDK makes the transport layer pluggable. A transport connected to a channel with history handles history replay automatically on connect, so useChat receives messages without a separate fetch.

When you return to the session, the transport subscribes to the channel and rewinds to the start. The messages array fills in from history automatically. From the component's perspective, it's just messages arriving.

Copy link to clipboard

Choosing an approach

If you're building something simple for a single user on a single device, a database plus the messages prop is the direct path. The AI SDK persistence documentation covers it.

Channel history is worth the extra step when things get more complicated: multiple devices, colleagues sharing a session, agents running in the background while users are away. Basically, any scenario where the connection between the user and the generation is unreliable or non-linear.

For long-term retention, you'd typically combine both. Channel history covers the active window. A database handles everything older than that.

Copy link to clipboard

What to look for in a transport for persistent messaging

  • Channel history on subscribe. The transport should replay stored messages on connect without requiring a separate API call. A session that handles initial load, live updates, and reconnect catch-up from the same mechanism avoids building separate infrastructure for each. See history and replay.

  • Offset-based reconnection. A client returning after a disconnect should receive only the messages it missed, from its last known position. Full replay on every reconnect overstates the loss and creates unnecessary processing. See reconnection and recovery.

  • Multi-device fan-out. The same session should be accessible from any device with the right session ID. A second device joining mid-conversation should receive history and then live updates without additional infrastructure. See multi-device sessions. S

  • Configurable retention. Short windows cover reconnects. Longer windows cover returning sessions. For permanent storage, you'd combine channel history with a database — but the transport determines how much of that work you need to do yourself. See history and replay.

Ably AI Transport implements the Vercel AI SDK ChatTransport interface and handles persistent message history as part of the session layer, covering reconnects, multi-device sync, and delivery to clients that were offline when results arrived. Visit the Ably AI Transport overview, read the documentation, or sign up free to start building.

Sources: AI SDK UI chatbot message persistence; AI SDK stream resumption; resumable-stream; AI SDK UI Transport documentation. GitHub issues cited: #8390 (resume and stop incompatible, acknowledged by Vercel).

Copy link to clipboard

Frequently asked questions

Copy link to clipboard

Why does chat history disappear when I reload the page in Vercel AI SDK?

Because useChat stores messages in React component state. When the component unmounts (on page reload, navigation, or tab close), the state is gone. There's no automatic write to localStorage or a database — Vercel deliberately left persistence out of scope, since the right approach depends on your stack.

Copy link to clipboard

When is a database alone not enough for chat history?

A database alone breaks down when conversations span devices, colleagues, or background agents:

  • Multiple devices need to view the same conversation in real time

  • A colleague picking up the thread needs live updates

  • Long-running agents need results delivered even after the user navigates away

Copy link to clipboard

What is the difference between stream resumption and channel history?

Stream resumption reconnects to an active, in-progress stream after a page reload — it only works while generation is still running. Channel history stores every message as it's published and replays them to any client that connects, active or not. It covers:

  • Initial load

  • Live updates

  • Reconnect catch-up

  • Multi-device sync

All from a single mechanism, without a separate infrastructure for each.

Copy link to clipboard

Why do I get errors converting stored messages back into useChat's format?

Because saving a conversation and loading it back cross the same format boundary in opposite directions. useChat works with UIMessage objects; the model APIs work with ModelMessage objects. The AI SDK's convertToModelMessages() handles the outbound direction, but there's no single documented function for the return trip. Teams that build their own persistence layer typically hit this on load, not on save.


Join the Ably newsletter today

1000s of industry pioneers trust Ably for monthly insights on the realtime data economy.
Enter your email