Cookbook
May 7, 2026 · View on GitHub
Practical recipes. All snippets assume you have:
import TdLib from 'react-native-tdlib';
import {NativeEventEmitter, NativeModules} from 'react-native';
const emitter = new NativeEventEmitter(NativeModules.TdLibModule);
Listing chats with live updates
await TdLib.loadChats(25);
const raw = await TdLib.getChats(25);
const chats = JSON.parse(raw);
emitter.addListener('tdlib-update', e => {
if (
e.type === 'updateNewMessage' ||
e.type === 'updateChatLastMessage' ||
e.type === 'updateChatReadInbox' ||
e.type === 'updateChatPosition' ||
e.type === 'updateNewChat'
) {
refresh(); // debounce in real apps — TDLib bursts many updates at once
}
});
Pagination
const PAGE = 25;
let loaded = 0;
let hasMore = true;
async function loadMore() {
if (!hasMore) return;
const r = await TdLib.loadChats(PAGE).catch(() => null);
if (r === 'No more chats to load') hasMore = false;
loaded += PAGE;
const chats = JSON.parse(await TdLib.getChats(loaded));
setChats(chats);
}
Reading message history and marking as read
const history = await TdLib.getChatHistory(chatId, 0, 40, 0);
const messages = history.map(it => JSON.parse(it.raw_json));
await TdLib.openChat(chatId); // MUST be called so TDLib treats the user as "viewing"
await TdLib.viewMessages(chatId, messages.map(m => m.id), false);
viewMessages uses messageSourceChatHistory internally — meaning TDLib will drop the unread counter and emit updateChatReadInbox.
Sending a message, with reply
await TdLib.sendMessage(chatId, 'Hello');
await TdLib.sendMessage(chatId, 'Replying to a specific message', messageId);
Reactions
await TdLib.addMessageReaction(chatId, messageId, '❤️');
await TdLib.removeMessageReaction(chatId, messageId, '❤️');
// Who reacted?
const r = await TdLib.getAddedReactions(chatId, messageId);
Watch updateMessageInteractionInfo to keep local state in sync when others react.
Typing indicator
Outgoing — fire occasionally while the user types in your input:
TdLib.td_json_client_send({
'@type': 'sendChatAction',
chat_id: chatId,
action: {'@type': 'chatActionTyping'},
});
Incoming — subscribe to updateChatAction and show "typing…" in the chat header:
emitter.addListener('tdlib-update', e => {
if (e.type !== 'updateChatAction') return;
const {chat_id, sender_id, action} = JSON.parse(e.raw);
const isCancel = action['@type'] === 'chatActionCancel';
// update your local "who is typing" map for chat_id / sender_id.user_id
});
TDLib sends chatActionCancel to clear; also apply a ~5s timeout as a safety net.
Downloading files (fire-and-forget)
TdLib.downloadFile(id) waits for the download to complete before resolving — fine for one-off downloads, bad for long chat lists with 25+ avatars (blocks the bridge).
For UI previews, start the download and listen for progress:
TdLib.td_json_client_send({
'@type': 'downloadFile',
file_id: fileId,
priority: 1,
offset: 0,
limit: 0,
synchronous: false,
});
emitter.addListener('tdlib-update', e => {
if (e.type !== 'updateFile') return;
const {file} = JSON.parse(e.raw);
if (file.id === fileId && file.local?.is_downloading_completed) {
const uri = file.local.path.startsWith('file://')
? file.local.path
: `file://${file.local.path}`;
setLocalPath(uri); // <Image source={{uri}} />
}
});
Showing a photo bubble
Inside a TDLib message object (content['@type'] === 'messagePhoto'):
const photo = content.photo; // {sizes: [...], minithumbnail: {...}}
const size = photo.sizes?.find(s => s.type === 'x') ?? photo.sizes?.[0];
const fileId = size?.photo?.id;
// Trigger download as above. While waiting, show the minithumbnail:
const placeholder = photo.minithumbnail?.data
? `data:image/jpeg;base64,${photo.minithumbnail.data}`
: undefined;
Options
await TdLib.setOption('online', {type: 'boolean', value: true});
const version = await TdLib.getOption('version');
// '1.8.51', etc. All values are returned as strings.
Available types: string, integer, boolean, empty (reset a previously set option).
Searching chats
const raw = await TdLib.searchChats('Durov', 10);
const results = JSON.parse(raw); // array of full chat objects
Use searchPublicChat(username) for exact @username lookups.
Executing synchronous TDLib functions
Some TDLib functions don't need the client (they're pure helpers). Use td_json_client_execute:
const res = await TdLib.td_json_client_execute({
'@type': 'getTextEntities',
text: 'Check @telegram and https://telegram.org',
});
const entities = JSON.parse(res).entities;
Or use the dedicated helper:
const res = await TdLib.getTextEntities(
'Check @telegram and https://telegram.org',
);
const entities = JSON.parse(res).entities;
Calling anything TDLib supports
If a function isn't yet wrapped with a dedicated method, use td_json_client_send — it's fire-and-forget, and the response (or update) arrives via the usual tdlib-update stream:
TdLib.td_json_client_send({
'@type': 'setChatTitle',
chat_id: chatId,
title: 'New title',
});
See the TDLib API docs for the full list.