Learn how to build in-app chat with Stream's SDK for JS. This quickstart covers plain JS. You might also want to look at
the React tutorial or the JS based API tour.
JS client is an npm package, which acts as an interface for chat rest APIs, for integrating chat into your application. It makes it easy to communicate with chat APIs e.g., sending a message, connecting to chat, creating channel etc. It's written using vanilla javascript, which makes it framework (vuejs, react, react-native, angular etc) agnostic.
Let's get started by initializing the client and setting the current user:
const client = StreamChat.getInstance("{{ api_key }}");// you can still use new StreamChat("api_key");await client.connectUser( { id: "jlahey", name: "Jim Lahey", image: "https://i.imgur.com/fR9Jz14.png", }, "{{ chat_user_token }}",);
String apiKey = "{{ api_key }}";String token = "{{ chat_user_token }}";Context context = getApplicationContext();ChatClient client = new ChatClient.Builder(apiKey, context).build();User user = new User();user.setId("jlahey");user.putExtraValue("image", "https://bit.ly/321RmWb");user.putExtraValue("name", "Jim Lahey");client.setUser(user, token, new InitConnectionListener() { @Override public void onSuccess(@NotNull ConnectionData data) { final User user = data.getUser(); final String connectionId = data.getConnectionId(); Toast.makeText(context, "Logged in successfully as " + user.getExtraValue("name", ""), Toast.LENGTH_SHORT).show(); } @Override public void onError(@NotNull ChatError error) { error.getCause().printStackTrace(); }});
// Import StreamChatClient framework.import StreamChatClient// Setup the Stream Chat Client with your API key// Preferably in `application(_ application:didFinishLaunchingWithOptions:)`// This needs to be called only once, since a singleton cannot be configured multiple times!// During development we advice to set log to INFO levelClient.configureShared(.init(apiKey: "{{ api_key }}", logOptions: .info))// Create a user, when they login.let userExtraData = UserExtraData(name: "Jim Lahey", avatarURL: URL(string: "https://bit.ly/321RmWb")!)let user = User(id: "jlahey", extraData: userExtraData)// Tokens must be generated server-side// For development, you can use the token generator (see later in the docs)let token = "{{ chat_user_token }}"// Setup the current user with its token// You can also pass a tokenProvider closureClient.shared.set(user: user, token: token)
val apiKey = "{{ api_key }}"val token = "{{ chat_user_token }}"val context = getApplicationContext()val client = ChatClient.Builder(apiKey, context).build()val user = User("jlahey")user.extraData["image"] = "https://bit.ly/321RmWb"user.extraData["name"] = "Jim Lahey"client.setUser(user, token, object : InitConnectionListener() { override fun onSuccess(data: ConnectionData) { val user = data.user val connectionId = data.connectionId Toast.makeText(context, "Logged in successfully as ${user.name}", Toast.LENGTH_SHORT).show() } override fun onError(error: ChatError) { error.cause?.printStackTrace() }})
import 'package:stream_chat/stream_chat.dart';// create a client with log-level INFOfinal client = Client("{{ api_key }}", logLevel: Level.INFO);// init the user object, note how you can specify custom fields as wellfinal user = User(id: "jlahey", extraData: { 'name': 'Jim Lahey', 'image': 'https://i.imgur.com/fR9Jz14.png',});// sets the current user, from now on the client can be used to query channels and receive eventsawait client.setUser(user, "{{ chat_user_token }}");
The above snippet is for an in-browser or mobile integration. Server-side API calls are a little different, but this is covered in detail later in the documentation.
Let’s continue by initializing your first channel. A channel contains messages, a list of people that are watching the channel, and optionally a list of members (for private conversations). The example below shows how to set up a channel to support chat for a group conversation:
const client = StreamChat.getInstance("{{ api_key }}");const channel = client.channel("messaging", "travel", { name: "Awesome channel about traveling",});// fetch the channel state, subscribe to future updatesconst state = await channel.watch();
ChannelClient channelClient = client.channel("messaging", "travel");HashMap extraData = new HashMap<String, Object>();extraData.put("name", "Awesome channel about traveling");// Watching a channel"s stateQueryChannelRequest request = new QueryChannelRequest() .withData(extraData) .withMessages(20) .withWatch(); // Ensures that we are watching the channel for any changes/new messageschannelClient.query(request).enqueue(new Call.Callback<Channel>() { @Override public void onResult(@NotNull Result<Channel> result) { if (result.isSuccess()) { Channel channel = result.data(); // Use channel } else { result.error().getCause().printStackTrace(); } }});
// Create an extra data for a channel.// ChannelExtraDataCodable is an extension of Codable that makes sure you have common fields// used by the UI library (name and imageURL)struct MyChannelData: ChannelExtraDataCodable { var name: String? var imageURL: URL? let info: String // this is our custom field :)}// Register once your channel extra data type, we'll need it for decodingChannel.extraDataType = MyChannelData.selflet data = MyChannelData(name: "Travel", imageURL: nil, info: "Awesome channel about traveling")let channel = Client.shared.channel(type: .messaging, id: "travel", extraData: data)// Watching a channelchannel.watch { (result) in // handle result}
val channelClient: ChannelClient = client.channel( channelType = "messaging", channelId = "travel")val extraData = mutableMapOf<String, Any>( "name" to "Awesome channel about traveling")// Watching a channel"s stateval request = QueryChannelRequest() .withData(extraData) .withMessages(limit = 20) .withWatch() // Ensures that we are watching the channel for any changes/new messageschannelClient.query(request).enqueue { result -> if (result.isSuccess) { val channel: Channel = result.data() // Use channel } else { result.error().cause?.printStackTrace() }}
final channel = client.channel("messaging", id: "travel", extraData: { "name": "Awesome channel about traveling",});// fetch the channel state and subscribe to future updatesfinal state = await channel.watch();
The first two arguments are the Channel Type and the Channel ID ( messaging and travel in this case). The Channel ID is optional; if you leave it out, the ID is determined based on the list of members. The channel type controls the settings we’re using for this channel.
There are 5 default types of channels:
livestream
messaging
team
gaming
commerce
These five options above provide you with the most sensible defaults for those use cases. You can also define custom channel types if Stream Chat defaults don’t work for your use-case.
The third argument is an object containing the channel data. You can add as many custom fields as you would like as long as the total size of the object is less than 5KB.
Now that we have the channel set up, let's send our first chat message:
const text = "I’m mowing the air Rand, I’m mowing the air.";const response = await channel.sendMessage({ text, customField: "123",});
Message message = new Message();message.setText("I’m mowing the air Rand, I’m mowing the air.");message.putExtraValue("customField", "123");channelClient.sendMessage(message).enqueue(new Call.Callback<Message>() { @Override public void onResult(@NotNull Result<Message> result) { if (result.isSuccess()) { Message message = result.data(); } else { result.error().getCause().printStackTrace(); } }});
let channelExtraData = ChannelExtraData(name: "General", imageURL: nil)let channel = Client.shared.channel(type: .messaging, id: "general", extraData: channelExtraData)// Create a messagelet message = Message(text: "Hello")// Send the messagechannel.send(message: message) { result in do { let response = try result.get() print(response) } catch { print("Error when sending message: \(error)") }}
val message = Message(text = "I’m mowing the air Rand, I’m mowing the air.")message.extraData["customField"] = "123"channelClient.sendMessage(message).enqueue(object : Call.Callback<Message> { override fun onResult(result: Result<Message>) { if (result.isSuccess) { val message = result.data() } else { result.error().cause?.printStackTrace() } }})
final message = Message( text: 'I’m mowing the air Rand, I’m mowing the air.', extraData: {'customField': '123'},);final response = await channel.sendMessage(message);
Similar to users and channels, the sendMessage method allows you to add custom fields. When you send a message to a channel, Stream Chat automatically broadcasts to all the people that are watching this channel and updates in real-time.
This is how you can listen to events on the clients-side:
channel.on("message.new", (event) => { console.log("received a new message", event.message.text); console.log( `Now have ${channel.state.messages.length} stored in local state`, );});
final Disposable disposable = client.subscribe((event) -> { if (event instanceof NewMessageEvent) { Message message = ((NewMessageEvent) event).getMessage(); } return Unit.INSTANCE;});// Dispose to stop receiving eventsdisposable.dispose();
let channel = Client.shared.channel(type: .messaging, id: "general")let subscription = channel.subscribe(forEvents: [.messageNew]) { event in // handle new message event}// Cancel subscription when you want to stop receiving eventssubscription.cancel()
val disposable = client.subscribe { event: ChatEvent -> if (event is NewMessageEvent) { val message = event.message }}// Dispose to stop receiving eventsdisposable.dispose()
channel.on("message.new").listen((Event event) { print("received a new message: ${event.message.text}");};
You can receive the event and access the full channel state via channel.state .
Now that you understand the building blocks of a fully functional chat integration, let’s move on to the next sections of the documentation, where we dive deeper into details on each API endpoint.