Sending text

Use text streams to send any amount of text between participants.

Overview

Text streams provide a simple way to send text between participants in realtime, supporting use cases such as chat, streamed LLM responses, and more. Each individual stream is associated with a topic, and you must register a handler to receive incoming streams for that topic. Streams can target specific participants or the entire room.

To send other kinds of data, use byte streams instead.

Sending all at once

Use the sendText method when the whole string is available up front. The input string is automatically chunked and streamed so there is no limit on string size.

const text = 'Lorem ipsum dolor sit amet...';
const info = await room.localParticipant.sendText(text, {
topic: 'my-topic',
});
console.log(`Sent text with stream ID: ${info.id}`);

Streaming incrementally

If your text is generated incrementally, use streamText to open a stream writer. You must explicitly close the stream when you are done sending data.

const streamWriter = await room.localParticipant.streamText({
topic: 'my-topic',
});
console.log(`Opened text stream with ID: ${streamWriter.info.id}`);
// In a real app, you would generate this text asynchronously / incrementally as well
const textChunks = ["Lorem ", "ipsum ", "dolor ", "sit ", "amet..."]
for (const chunk of textChunks) {
await streamWriter.write(chunk)
}
// The stream must be explicitly closed when done
await streamWriter.close();
console.log(`Closed text stream with ID: ${streamWriter.info.id}`);

Handling incoming streams

Whether the data was sent with sendText or streamText, it is always received as a stream. You must register a handler to receive it.

room.registerTextStreamHandler('my-topic', (reader, participantInfo) => {
const info = reader.info;
console.log(
`Received text stream from ${participantInfo.identity}\n` +
` Topic: ${info.topic}\n` +
` Timestamp: ${info.timestamp}\n` +
` ID: ${info.id}\n` +
` Size: ${info.size}` // Optional, only available if the stream was sent with `sendText`
);
// Option 1: Process the stream incrementally using a for-await loop.
for await (const chunk of reader) {
console.log(`Next chunk: ${chunk}`);
}
// Option 2: Get the entire text after the stream completes.
const text = await reader.readAll();
console.log(`Received text: ${text}`);
});

Stream properties

These are all of the properties available on a text stream, and can be set from the send/stream methods or read from the handler.

PropertyDescriptionType
idUnique identifier for this stream.string
topicTopic name used to route the stream to the appropriate handler.string
timestampWhen the stream was created.number
sizeTotal expected size in bytes (UTF-8), if known.number
attributesAdditional attributes as needed for your application.string dict
destinationIdentitiesIdentities of the participants to send the stream to. If empty, is sent to all.array

Concurrency

Multiple streams can be written or read concurrently. If you call sendText or streamText multiple times on the same topic, the recipient's handler will be invoked multiple times, once for each stream. These invocations will occur in the same order as the streams were opened by the sender, and the stream readers will be closed in the same order in which the streams were closed by the sender.

Joining mid-stream

Participants who join a room after a stream has been initiated will not receive any of it. Only participants connected at the time the stream is opened are eligible to receive it.

No message persistence

LiveKit does not include long-term persistence for text streams. All data is transmitted in real-time between connected participants only. If you need message history, you'll need to implement storage yourself using a database or other persistence layer.

Chat components

LiveKit provides pre-built React components for common text streaming use cases like chat. For details, see the Chat component and useChat hook.

Note

Streams are a simple and powerful way to send text, but if you need precise control over individual packet behavior, the lower-level data packets API may be more appropriate.