JavaScript client SDK

Installation

Install with a package manager like npm or yarn.

$ yarn add @pusher/chatkit-client

Then import into your code like you would any other module.

If you are using the SDK in a react native app use @pusher/chatkit-client/react-native
import { ChatManager, TokenProvider } from '@pusher/chatkit-client'

Instantiate Chatkit

To get started with Chatkit you will need to instantiate both a ChatManager instance as well as a TokenProvider instance to authenticate users. The example below uses demo credentials.

JAVASCRIPT

1
2
3
4
5
const chatManager = new ChatManager({
  instanceLocator: 'v1:us1:80215247-1df3-4956-8ba8-9744ffd12161',
  userId: 'sarah',
  tokenProvider: new TokenProvider({ url: 'your.auth.url' })
})

Chat Manager

JAVASCRIPT

1
2
3
4
5
const chatManager = new ChatManager({
  instanceLocator: 'v1:us1:80215247-1df3-4956-8ba8-9744ffd12161',
  userId: 'sarah',
  tokenProvider: new TokenProvider({ url: 'your.auth.url' })
})
Note that the user ID provided to the Chat Manager must represent a user that already exists, see the Server SDK docs for details on creating users.

ChatManager Arguments

The ChatManager constructor takes a single options object with the following properties.

ArgumentTypeDescription
instanceLocatorstring (required)Get this from your dashboard.
userIdstring (required)The ID of the user that you wish to connect to Chatkit as. This will also be passed to the token provider and included in token requests as a query parameter in the form: user_id=<USER_ID>.
tokenProviderTokenProvider (required)Used to retrieve tokens to authenticate requests made to the Chatkit servers. See Token Provider.
loggerLogger (optional)A cutom logger implementation. See Logger.
connectionTimeoutnumber (optional)The number of milliseconds to wait before timing out a connection attempt with an error. Defaults to 10s.

Methods

MethodDescription
connectCreates a connection between your client and the Chatkit servers. See Connect for more information.

TokenProvider

JAVASCRIPT

1
2
3
4
5
6
7
8
9
10
11
const tokenProvider = new TokenProvider({
  url: 'your.auth.url',
  queryParams: {
    someKey: someValue,
    ...
  },
  headers: {
    SomeHeader: 'some-value',
    ...
  }
})

TokenProvider Arguments

The TokenProvider constructor takes a single options object with the following properties.

ArgumentTypeDescription
urlstring (required)The URL that the ChatManager should make a POST request to in order to fetch a valid token. This will be either the test token provider or your own custom auth endpoint.
queryParamsobject (optional)An object of key–value pairs to be passed as query parameters along with the token request.
headersobject (optional)An object of key–value pairs to be passed as headers along with the token request.
withCredentialsboolean (optional)Whether to make requests with credentials, defaults to false.

When making requests to a token endpoint, the TokenProvider expects the response to have the following format:

JSON

1
2
3
4
{
  "access_token": "<your token here>",
  "expires_in": "<seconds until token expiry here>"
}
For more information on the auth process please refer to the authentication docs.

Connect

JAVASCRIPT

1
2
3
4
5
6
7
chatManager.connect()
  .then(currentUser => {
    console.log('Successful connection', currentUser)
  })
  .catch(err => {
    console.log('Error on connection', err)
  })

Once you’ve initialized your Chat Manager you are ready to connect to the Chatkit servers. The connect method returns a promise that resolves with a Current User object. This object is your primary means of interacting with the Chatkit instance, rooms, messages and users.

Connection hooks

JAVASCRIPT

1
2
3
4
5
chatManager.connect({
  onAddedToRoom: room => {
    console.log(`Added to room ${room.name}`)
  }
})

When connecting you can also provide hooks to be notified when various events happen. For example, if you wish to be notified when the connected user gets added to a room, you could provide theonAddedToRoom hook.

HookAcceptsDescription
onAddedToRoomroomThe current user is added to a room.
onRemovedFromRoomroomThe current user is removed from a room.
onRoomUpdatedroomA room that the current user is a member of has been updated, either in name, privacy status, custom data or when a new message has been sent to the room.
onRoomDeletedroomA room that the current user is a member of is deleted.
onUserStartedTypingroom, userA user started typing in a room to which the current user is subscribed.
onUserStoppedTypingroom, userA user stopped typing in a room to which the current user is subscribed.
onUserJoinedRoomroom, userA user joined a room to which the current user is subscribed.
onUserLeftRoomroom, userA user left a room to which the current user is subscribed.
onPresenceChangedstate, userA member of one of the rooms the current user is subscribed to came online or went offline. state has the form { (current, previous) } where current and previous can be one of "online", "offline", or "unknown".
onNewReadCursorcursorA new read cursor was set for the current user.

See also, room subscription hooks.

Current User

When an initial connection is successfully made to Chatkit the client will receive a CurrentUser object. The CurrentUser object is the primary means of interacting with Chatkit.

User Properties

PropertyTypeDescription
roomsarrayThe rooms that the connected user is a member of. See rooms.
usersarrayThe union of all the members of all the rooms that the current user is subscribed to. For convenience. See users.
roomSubscriptionsobjectThe rooms a user is joined and subscribed to. See subscriptions.

Disconnect

To disconnect the current user from all subscriptions. Call the disconnect method on the CurrentUser object.

Rooms

There are a few important things to remember about Chatkit rooms; they are either public or private, users that are members of a room can change over time, all chat messages belong to a room.

Room Properties

PropertyTypeDescription
idstringThe global identifier for the room on the instance.
isPrivateboolIf true the room is private, otherwise the room is public.
namestringThe human readable name of the room (this needn’t be unique!)
usersarrayAn array of all the users that are members of this room. Only accessible when subscribed to the room. See Users.
unreadCountnumberCount of unread messages for the given user in this room. Only defined if user is member of the room.
lastMessageAtstringThe timestamp at which the last message sent in this room was created. Only defined if user is member of the room and the room has messages.
customDataobjectArbitrary custom data to be attached to the room.

Create a Room

Use the createRoom method to create a new room. Provide a name, and optionally an ID. If no ID is provided we’ll generate one for you. A room name must be no longer than 60 bytes, and a room ID must be no longer than 321 bytes.

JAVASCRIPT

1
2
3
4
5
6
7
8
9
10
11
12
currentUser.createRoom({
  id: '#general',
  name: 'General',
  private: true,
  addUserIds: ['craig', 'kate'],
  customData: { foo: 42 },
}).then(room => {
  console.log(`Created room called ${room.name}`)
})
.catch(err => {
  console.log(`Error creating room ${err}`)
})

All rooms are public by default. You can customise the new room by passing in options at the point of creation. To create a private room you need to set the private option to true. You can add other users to a room by specifying their IDs with the addUserIds option. You can set custom data on the room object by specifying the customData option to any object.

Fetch messages from a room

You can fetch up to the last 100 messages added to a room when you subscribe but sometimes you’ll want to fetch older messages. For example, suppose you subscribe to a room and the oldest message you see has the ID 42. To see older messages, you can provide the initialId option to the fetchMultipartMessages method.

JAVASCRIPT

1
2
3
4
5
6
7
8
9
10
11
12
currentUser.fetchMultipartMessages({
  roomId: someRoomId,
  initialId: 42,
  direction: 'older',
  limit: 10,
})
  .then(messages => {
    // do something with the messages
  })
  .catch(err => {
    console.log(`Error fetching messages: ${err}`)
  })
fetchMultipartMessages replaces its deprecated predecessor fetchMessages. The latter functions similarly but return the legacy message type.

Here we assume that either the oldest message ID that has been received so far (42 in the above example) is being tracked in a variable or calculated dynamically. For example if roomMessages keeps track of all messages received from a given room:

1
const oldestMessageIdReceived = Math.min(...roomMessages.map(m => m.id))

The full set of options follows:

PropertyTypeDescription
initialIdint (optional)A message ID that defaults to the most recent message ID.
directionstring (optional)Defaults to older which will backwards fetch messages but can be set to newer if you want to forward fetch messages.
limitint (optional)The amount of messages you want to revieve, which defaults to 20 and has a maximum 100.

Messages are fetched in chronological order, starting at (but excluding) the initialId and moving forwards or backwards until there are either no more messages, or the limit is reached. Returns a promise resolving to an array of messages.

Add a user to a room

The current user can add users to rooms that they themselves are a member of.

JAVASCRIPT

1
2
3
4
5
6
7
8
9
10
currentUser.addUserToRoom({
  userId: 'keith',
  roomId: 123
})
  .then(() => {
    console.log('Added keith to room 123')
  })
  .catch(err => {
    console.log(`Error adding keith to room 123: ${err}`)
  })

Remove a user from a room

The current user can remove users from rooms that they themselves are a member of.

JAVASCRIPT

1
2
3
4
5
6
7
8
9
10
currentUser.removeUserFromRoom({
  userId: 'leah',
  roomId: 123
})
  .then(() => {
    console.log('Removed leah from room 123')
  })
  .catch(err => {
    console.log(`Error removing leah from room 123: ${err}`)
  })

Get joinable rooms

To fetch an array of the rooms that a user is able to join (but isn’t yet a member of):

JAVASCRIPT

1
2
3
4
5
6
7
currentUser.getJoinableRooms()
  .then(rooms => {
    // do something with the rooms
  })
  .catch(err => {
    console.log(`Error getting joinable rooms: ${err}`)
  })

The rooms returned will be an array of the public rooms which the currentUser is not a member of.

Join a room

Join a room with ID someRoomId:

JAVASCRIPT

1
2
3
4
5
6
7
currentUser.joinRoom({ roomId: someRoomID })
  .then(room => {
    console.log(`Joined room with ID: ${room.id}`)
  })
  .catch(err => {
    console.log(`Error joining room ${someRoomID}: ${err}`)
  })

Leave a room

Leave a room with ID someRoomId:

JAVASCRIPT

1
2
3
4
5
6
7
currentUser.leaveRoom({ roomId: someRoomID })
  .then(room => {
    console.log(`Left room with ID: ${room.id}`)
  })
  .catch(err => {
    console.log(`Error leaving room ${someRoomID}: ${err}`)
  })

Update a room

Change the name, privacy, and/or custom data associated with the room with ID someRoomId:

JAVASCRIPT

1
2
3
4
5
6
7
8
9
10
11
12
currentUser.updateRoom({
  roomId: someRoomID,
  name: 'Some updated name',
  private: false,
  customData: { bar: 'baz' },
})
  .then(() => {
    console.log(`Updated room with ID: ${someRoomID}`)
  })
  .catch(err => {
    console.log(`Error updated room ${someRoomID}: ${err}`)
  })

Note that once customData for a room has been set you then cannot completely clear the customData for the room. You can, however, set the customData to be an empty object, i.e. {}.

All other connected memebers of the room will receive an event that informs them that the room has been updated. Note that the current user must have the room:update permission to use this method.

Delete a room

Delete a room with ID someRoomId:

JAVASCRIPT

1
2
3
4
5
6
7
currentUser.deleteRoom({ roomId: someRoomID })
  .then(() => {
    console.log(`Deleted room with ID: ${someRoomID}`)
  })
  .catch(err => {
    console.log(`Error deleted room ${someRoomID}: ${err}`)
  })

All other connected memebers of the room will receive an event that informs them that the room has been deleted. Any attempts to interact with a deleted room will result in an error. Note that the current user must have the room:delete permission to use this method.

When a room is deleted, all of its associated messages are deleted as well.

Subscriptions

To be notified when new messages are added to a room, you’ll need to subscribe to it and provide an onMessage hook. (To see the full list of possible hooks see Room Subscription Hooks). At most 100 recent messages can be retrieved on subscription, to fetch older messages see Fetch Messages From a Room. To receive only new messages, set messageLimit to 0.

JAVASCRIPT

1
2
3
4
5
6
7
8
9
currentUser.subscribeToRoomMultipart({
  roomId: someRoomId,
  hooks: {
    onMessage: message => {
      console.log("received message", message)
    }
  },
  messageLimit: 10
})
subscribeToRoomMultipart replaces its deprecated predecessor subscribeToRoom. The latter functions similarly but will pass the legacy message type to onMessage.

Subscribing implicitly joins a room if you aren’t already a member. Subscribing to the same room twice will cause the existing subscription to be cancelled and replaced by the new one.

By default when you subscribe to a room you will receive up to the 20 most recent messages that have been added to the room. The number of recent messages to fetch can be configured by setting the messageLimit option. These recent messages will be passed to the onMessage callback in the order they were sent, just as if they were being sent for the first time.

Room Subscription Hooks

The full list of hooks that can be provided as options to the subscribeToRoomMultipart method are as follows:

HookAcceptsDescription
onMessagemessageA new message has been added to the room.
onMessageDeletedmessageIdA new message has been removed from the room.
onUserStartedTypinguserA user started typing in the room.
onUserStoppedTypinguserA user stopped typing in the room.
onUserJoineduserA user joined the room.
onUserLeftuserA user left the room.
onPresenceChangedstate, userA member of the room came online or went offline. state has the form { (current, previous) } where current and previous can be one of "online", "offline", or "unknown".
onNewReadCursorcursorA member of the room set a new read cursor.

Cancel a subscription

Call the cancel method on a room subscription if you no longer want to receive events from that room. You can find room subscriptions indexed by room ID in the roomSubscriptions property of the Current User object.

1
currentUser.roomSubscriptions[roomId].cancel()

Users

User objects can be found in various places: globally under currentUser.users, scoped by room under room.users, or returned as the argument to some hooks.

Users Properties

AttributeTypeDescription
idstringThe unique identifier for the user on the instance.
namestringThe human readable name of the user. This is not required to be unique.
avatarURLstringThe location (url) of an avatar for the user.
presenceobjectAn object containing information regarding the user’s presence state. See user presence.

Messages

Every message belongs to a Room and has an associated sender, which is represented by a User object. The body of the message is made up of one or more parts, each with a mime type. For example, a message might have a text part "see attachment!" as well as an image part.

Message Properties

AttributeTypeDescription
idintThe ID assigned to the message by the Chatkit servers.
senderUserThe user who sent the message.
roomRoomThe room to which the message belongs.
partsarray of Message PartsThe parts that make up the message.
createdAtstringThe timestamp at which the message was created.
updatedAtstringThe timestamp at which the message was last updated.

Message parts have the following properties:

AttributeTypeDescription
partTypestringone of "inline", "url", or "attachment"
payloadobjectvaries depending on the partType. See below.

Inline payload:

AttributeTypeDescription
typestringThe MIME type of the content of this part.
contentstringThe content of the message part.

URL payload:

AttributeTypeDescription
typestringThe MIME type of the resource that the URL points to.
urlstringA url.

Attachment payload:

AttributeTypeDescription
typestringThe MIME type of the attachment.
namestringThe name of the attached file.
sizenumberThe size of the attached file in bytes.
customDataanyArbitrary custom data associated with the file at upload.
url()function (returns promise of string)A function resolving to the URL of the attachment. Asynchronous because a fresh URL may need to be fetched from our servers.
urlExpiry()function (returns Date)Returns the Date at which the URL expires.

Sending a message

There are two methods to send messages. In the simplest case you can send plain text messages with sendSimpleMessage. Takes the roomId to send a message to, and the text of the message.

JAVASCRIPT

1
2
3
4
5
6
7
8
9
10
currentUser.sendSimpleMessage({
  roomId: myRoom.id,
  text: "Hi there!",
})
.then(messageId => {
  console.log(`Added message to ${myRoom.name}`)
})
.catch(err => {
  console.log(`Error adding message to ${myRoom.name}: ${err}`)
})

Returns a promise which resolves with the message ID when the message has successfully been sent.

For more complex messages you should use sendMultipartMessage instead. Takes the roomId to send a message to, and an array of parts. Each part must be one of the following:

An inline part.

AttributeTypeDescription
typestringThe MIME type of the content.
contentstringThe string content of the part.

A URL part.

AttributeTypeDescription
typestringThe MIME type of the resource that the URL points to.
urlstringA URL.

An attachment part.

AttributeTypeDescription
fileBlobAny Blob – most commonly a File.
typestringThe MIME type of the attachment. (Optional if it can be inferred from the file.)
namestringThe name of the attached file. (Optional. May be inferred from the file.)
customDataanyArbitrary custom data to associate with the file. (Optional.)

For example, sending a message consisting of one of each of the above part types.

JAVASCRIPT

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
currentUser.sendMultipartMessage({
  roomId: myRoom.id,
  parts: [
    { type: "text/plain", content: "🐷😍" },
    {
      type: "image/gif",
      url: "https://gfycat.com/failingforkedheterodontosaurus",
    },
    {
      file: document.querySelector("#attach").files[0],
      customData: { metadata: 42 },
    }
  ],
})
.then(messageId => {
  console.log(`Added message to ${myRoom.name}`)
})
.catch(err => {
  console.log(`Error adding message to ${myRoom.name}: ${err}`)
})

Also returns a promise which resolves with the message ID of the successfully sent message.

Typing indicators

Sometimes it’s useful to be able to see if another user is typing. You can use Chatkit to let all the connected members of a room know when another user is typing.

Trigger a typing event

To send typing indicator events call isTypingIn with the ID of the room the current user is typing in.

JAVASCRIPT

1
2
3
4
5
6
7
currentUser.isTypingIn({ roomId: someRoomId })
  .then(() => {
    console.log('Success!')
  })
  .catch(err => {
    console.log(`Error sending typing indicator: ${err}`)
  })

Receive typing indicator events

To be notified when a user starts or stops typing in a room, provide a onUserStartedTyping and a onUserStoppedTyping function as part of the room subscription hooks.

JAVASCRIPT

1
2
3
4
5
6
onUserStartedTyping: user => {
  console.log(`User ${user.name} started typing`)
},
onUserStoppedTyping: user => {
  console.log(`User ${user.name} stopped typing`)
}

When a user starts or stops typing, these functions will be called with the corresponding user object.

User Presence

If a user has at least one active connection to the Chatkit service then they are considered online. When a user has no active connections they are considered offline. Each user object keeps track of whether a user is online or offline via the presence property.

JAVASCRIPT

1
2
3
4
if (user.presence.state === 'online') {
  // The user is online!
  // Show an online badge or something...
}

Additionally, to be notified when a user comes online or goes offline, you can provide the onPresenceChanged hook. Either at the room level – fires whenever a member of that room goes on or off line, or at the connection level – fires whenever any members of any subscribed rooms go on or offline.

JAVASCRIPT

1
2
3
onPresenceChanged: (state, user) => {
  console.log(`User ${user.name} is ${state.current}`)
}

Cursors

Read cursors track how far a user has read through the messages in a room. Each read cursor belongs to a user and a room – represented by a Cursor object.

Cursor Properties

AttributeTypeDescription
positionintThe message ID that the user has read up to.
updatedAtstringThe timestamp when the cursor was last set.
roomRoomThe room that the cursor refers to.
userUserThe user that the cursor belongs to.
typeintThe type of the cursor object, currently always 0 (representing a read cursor).

Setting a cursor

When you are confident that the current user has “read” a message, call setReadCursor with a roomId and a position (the ID of the newest message that has been “read”).

JAVASCRIPT

1
2
3
4
5
6
7
8
9
10
currentUser.setReadCursor({
  roomId: someRoomId,
  position: someMessageId
})
  .then(() => {
    console.log('Success!')
  })
  .catch(err => {
    console.log(`Error setting cursor: ${err}`)
  })

Getting a cursor

The current user’s read cursors are available immediately upon connecting. Access any existing cursors with the readCursor method. (A cursor that hasn’t been set yet is undefined.)

JAVASCRIPT

1
2
3
4
5
6
7
8
const cursor = currentUser.readCursor({
  roomId: someRoomId
})
console.log(`read up to message ID ${
  cursor.position
} in ${
  cursor.room.name
}.`)
To be notified when any of the current user’s read cursors are changed, supply an onNewReadCursor hook on connection.

Access other user’s cursors

After subscribing to a room, read cursors for members of that room can be accessed by supplying a userId as the second parameter to the readCursor method.

JAVASCRIPT

1
2
3
4
5
6
7
8
9
const alicesCursor = currentUser.readCursor({
  roomId: someRoomId,
  userId: 'alice'
})
console.log(`Alice has read up to ${
  alicesCursor.position
} in ${
  alicesCursor.room.name
}.`)
To be notified when any member of the room changes their read cursor, supply an onNewReadCursor hook when subscribing to the room.

Browser Notifications

Enable Browser Notifications

Once a connection to Chatkit has been successfully established, you can enable push notifications by calling the appropriate method on the CurrentUser object:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
chatManager
  .connect()
  .then(currentUser => {
    console.log("Connected as user ", currentUser);

    currentUser.enablePushNotifications()
      .then(() => {
        console.log('Push Notifications enabled');
      })
      .catch(error => {
        console.error("Push Notifications error:", error);
      });

      // Do other great things afterwards ✨
    })
Calling this method will prompt the user for permission to send them notifications if they haven't already provided permission for your site.

If you have a Service Worker, then you need to pass the Service Worker registration object when enabling push notifications.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
chatManager
  .connect()
  .then(currentUser => {
    console.log("Connected as user ", currentUser);

    window.navigator.serviceWorker
      .register("/example-existing/service-worker.js")
      .then(registration =>
        currentUser
          .enablePushNotifications({
            serviceWorkerRegistration: registration,
          })
          .then(() => {
            console.log('Push Notifications enabled');
          })
          .catch(error => {
            console.error("Push Notifications error:", error);
          })
        );

      // Do other great things afterwards ✨
    })

Disable browser notifications

When a user of your application logs out (or opts out of push notifications) you should call the disablePushNotifications method on your application’s ChatManager:

1
2
3
4
5
6
7
8
9
function onLogOut() {
  chatManager.disablePushNotifications()
    .then(() => {
      console.log('Push Notifications disabled');
    })
    .catch(error => {
      console.log('Push Notifications error:', error);
    })
}

After this method is called, Chatkit will stop sending notifications to this browser instance until enablePushNotifications is called again.

Logger

A custom logger can be provided that implements all of the following functions.

JAVASCRIPT

1
2
3
4
5
6
7
{
  verbose: (...args) => console.log('verbose:', ...args),
  debug: (...args) => console.log('debug:', ...args),
  info: (...args) => console.log('info:', ...args),
  warn: (...args) => console.log('warn:', ...args),
  error: (...args) => console.log('error:', ...args)
}