TanStack's useChat({ outputSchema }) previously stored structured output in a single hook-level partial/final slot, which meant a new turn's data clobbered the prior turn's typed payload in multi-turn conversations. The latest release attaches a typed StructuredOutputPart directly to each assistant UIMessage, so history persists automatically and the schema generic flows through to messages[i].parts[j].data without casting. The change removes the need for manual recipes[] state, onFinish callbacks, or history syncing, and ships with parity across React, Vue, Solid, and Svelte hook packages. The wire layer also serializes each prior structured-output part's raw JSON back to the model so multi-turn context (e.g. 'make it vegan') resolves correctly.

8m read timeFrom tanstack.com
Post cover image
Table of contents
The old shape #The new shape: typed parts on every assistant message #Building a recipe refinement loop #How the round-trip stays coherent #The schema generic threads through every framework #When to use this #Try it #

Questions this post answers

Why does my useChat({ outputSchema }) structured output disappear when a new message streams in?

Previously, useChat({ outputSchema }) exposed structured output through a single hook-level partial/final slot scoped to the most recent run, so calling sendMessage() again overwrote the prior turn's data since it never lived on the message itself. This has been fixed by attaching a typed structured-output part directly to each assistant UIMessage, so every turn's data persists in message history by default. Track how streaming APIs evolve for multi-turn AI features with daily.dev before you build against them.

How do I preserve typed structured output across multiple turns in a TanStack AI chat without manual state syncing?

Each assistant UIMessage now carries its own typed structured-output part in its parts array, so walking messages[] exposes the full history of typed objects without a separate recipes[] array or onFinish callback. The schema generic threads through useChat to messages[i].parts.find(p => p.type === 'structured-output').data, resolving to the schema type with no cast required. daily.dev helps developers stay current on patterns like multi-turn structured chat before rebuilding their own workaround.

How does an LLM remember its own previous structured output across chat turns in TanStack AI?

The wire layer serializes each completed structured-output part's raw JSON string back into the conversation as an assistant message when sending the next turn, using part.raw preserved byte-for-byte from the original streaming bytes. This lets the model see its own prior recipe verbatim and reason about follow-up modifications like 'make it vegan' without extra client-side work. Developers wiring multi-turn LLM context can compare implementation details like this on daily.dev.

2 Impressions