Docs

Sink

Render streamed output into recycled pages instead of fresh allocations.

Open Markdown

SinkWriter<N> is a WriteBuf backed by a request-scoped PagePool<N>. It checks out fixed-capacity pages, writes into them, flushes them through a StreamChunkFlusher, and returns them to the pool.

It was the writer behind in-order streaming, built so a streamed Suspense child does not allocate a fresh large Vec<u8> for every boundary. It is now deprecated: built-in streaming renders into the standard RenderBuffer, which owns and recycles its own pages, and flushes through the same StreamChunkFlusher.

Most app code should never name it. Use framework streaming responses or the Markdown stream driver; SinkWriter remains only for legacy adapters and benchmarks that explicitly selected the pool-backed writer.

The page pool cycle

A page moves through four states, and the fourth returns it to the first:

  1. Checkout. SinkWriter takes a SinkPage<N> from the pool.
  2. Write. The child renders into that page through the shared WebContext.
  3. Flush. The transport sees a StreamChunkView, a borrowed view over ordered page slices.
  4. Return. The page goes back to the pool, ready for the next boundary.

Static markup inside an interactive component stays server-rendered throughout. Nothing is copied between steps two and three.

src/stream.rs
use std::rc::Rc;

use proa_core::{
    InOrderPagePool, InOrderSinkWriter, SinkWriter, StreamChunkFlusher,
    WriteError,
};

fn flush_boundary(flusher: Rc<dyn StreamChunkFlusher>) -> Result<(), WriteError> {
    let page_pool = Rc::new(InOrderPagePool::new());
    let mut out: InOrderSinkWriter = SinkWriter::new(page_pool);

    // Render into `out` through the shared WebContext, then flush pages.
    out.flush_into_flusher(flusher.as_ref())
}

InOrderSinkWriter is a type alias for the page size the old in-order streaming path used; Proa's built-in streaming paths now reuse the standard RenderBuffer instead. Reach for SinkWriter<N> and PagePool<N> directly only when a custom page size is part of a legacy adapter contract.

The types

TypeRole
PagePool<N>Owns reusable fixed-capacity pages.
SinkPage<N>One checked-out page.
SinkWriter<N>WriteBuf that writes across pages from a pool.
InOrderPagePoolLegacy pool alias for the old in-order streaming path.
InOrderSinkWriterLegacy writer alias for the old in-order streaming path.
StreamChunkFlusherTransport-facing sink that receives owned chunks or chunk views.
StreamChunkViewBorrowed view over ordered page slices during flush.
SinkCheckpointRewind checkpoint for the pool-backed writer.

The pool-backed types are deprecated compatibility shims. StreamChunkFlusher and StreamChunkView are the live transport-facing traits, shared with the RenderBuffer streaming paths.

Checkpoint streaming without a pool

StreamingZeroCopyBuf handles incremental streaming without page pooling. It creates explicit checkpoint marks and flushes complete parts, never half-written dynamic content:

src/stream.rs
use proa_core::{StreamingZeroCopyBuf, WebContext, WebRenderSync};

let mut cx = WebContext::with_buffer(StreamingZeroCopyBuf::new());
shell.render(&mut cx)?;
let mut out = cx.into_output();
let mark = out.mark();
let first_chunk = out.flush_through(mark);

Normal SSR routes should not manage streaming checkpoints by hand. Streaming SSR covers the Suspense API you want instead.

Next steps

Search

Type at least 2 characters