Island AI

Getting Started

Installation

bun add stream-hooks zod zod-stream react

Version 4 requires Zod 4, zod-stream 4, and React 18.3 or React 19. react-dom is not required by this package.

Progressive hook

import { useJsonStream } from "stream-hooks"
import { z } from "zod"
 
const schema = z.object({
  title: z.string(),
  details: z.object({ count: z.coerce.number() })
})
 
export function ExtractButton() {
  const { data, loading, startStream, stopStream } = useJsonStream({
    schema,
    onReceive(chunk) {
      console.log(chunk.title, chunk._meta._completedPaths)
    },
    onEnd(output) {
      console.log(output.details.count)
    }
  })
 
  const start = async () => {
    try {
      await startStream({
        url: "/api/extract",
        method: "POST",
        body: { prompt: "Extract this" }
      })
    } catch (error) {
      console.error(error)
    }
  }
 
  return (
    <>
      <button disabled={loading} onClick={start}>{data.title ?? "Start"}</button>
      <button onClick={stopStream}>Stop</button>
    </>
  )
}

onReceive gets progressive raw input plus completion metadata. onEnd runs only after final Zod validation and receives z.output<T>, including completed coercions and transforms. startStream rejects on HTTP, parser, source-stream, or validation errors. Aborted streams do not call onEnd.

Framework-independent consumption

import { consumeJsonStream } from "stream-hooks"
 
const { data, lastChunk } = await consumeJsonStream({
  stream: response.body!,
  schema,
  onReceive(chunk) {
    renderPartial(chunk)
  }
})

This utility has the same progressive and final-validation behavior without requiring a mounted React component.

Migration from 3.x

  1. Upgrade to Zod 4 and zod-stream 4.
  2. Use React 18.3+ or React 19.
  3. Await startStream and handle rejection at the call site.
  4. Use onEnd for validated completion; undocumented onComplete/onError options are not part of the API.

On this page