# Beyond the Spinner: Building Responsive AI Apps with Genkit Streaming

DevFeed: [Beyond the Spinner: Building Responsive AI Apps with Genkit Streaming](<https://devfeed.tech/articles/beyond-the-spinner-building-responsive-ai-apps-with-genkit-streaming-23892.md>)

Original publisher: [Read original article](<https://medium.com/firebase-developers/streaming-made-easy-with-genkit-a6f9da52a76a?source=rss----8e8b7dc6774d---4>)

Author: Pavel J

Published: 2025-10-28T10:35:17Z

Content type: tutorial

Language: en

Sources: [Firebase Developers - Medium](<https://devfeed.tech/sources/firebase-developers-medium.md>)

Topics: [Genkit](<https://devfeed.tech/topics/genkit.md>), [Streaming](<https://devfeed.tech/topics/streaming.md>), [Large Language Model](<https://devfeed.tech/topics/llm.md>), [User experience (UX)](<https://devfeed.tech/topics/ux.md>), [Structured-data](<https://devfeed.tech/topics/structured-data.md>)

Tags: [ai](<https://devfeed.tech/tags/ai.md>), [await](<https://devfeed.tech/tags/await.md>), [genkit](<https://devfeed.tech/tags/genkit.md>), [json](<https://devfeed.tech/tags/json.md>), [llm](<https://devfeed.tech/tags/llm.md>), [process](<https://devfeed.tech/tags/process.md>), [streaming](<https://devfeed.tech/tags/streaming.md>), [ux](<https://devfeed.tech/tags/ux.md>)

## AI overview

A practical guide to using Genkit streaming for responsive AI applications. It covers streaming LLM response chunks with generateStream and callbacks, awaiting final response metadata, parsing incomplete streamed JSON, and streaming custom AI logic from flows.

## Source excerpt

Better AI UXA practical guide to streaming LLM responses, partial JSON, and custom status messages to eliminate perceived latency. In the new age of AI, streaming structured data has suddenly become useful and mainstream. The main reason is that LLMs can be slow, and a good user experience (UX) strives to reduce this perceived latency. We could show a spinner for a few seconds, but that feels slow. Instead, if we start rendering content as soon as we receive the first few tokens from the LLM, the user can watch the response "grow in front of their eyes." Even if the end-to-end time is the same, it feels faster because the user sees activity immediately. BTW, this sample app (streaming side only) is available at: https://github.com/genkit-ai/samples/tree/main/simple-chatbot Streaming is foundational to Genkit's design. Everything in Genkit is built on top of actions -- simple, function-like constructs that, among other things, can stream. Models are actions, and flows are actions, so they can all stream. LLM streams Let's start with receiving streams from LLMs. The most common and useful way is to use the generateStream function: const { stream, response } = ai.generateStream({ prompt: 'Tell me a story about AI', }); for await (const chunk of stream) { process.stdout.write(chunk.text); } // optional const finalResponse = await response; console.log(finalResponse.usage); console.log(finalResponse.messages); // history In addition to the stream of "generate response chunks," we can optionally await the response promise. This is useful because it contains final usage data (like token counts), the complete message array (great for tracking history), and other metadata. If you don't like using the for await syntax and prefer callbacks, there's another less known way to stream using the generate function, by providing the onChunk callback: const response = await ai.generate({ prompt: 'Tell me a story aboud AI', onChunk: (chunk) => process.stdout.write(chunk.text), }); conso