---
source_url: https://www.pubnub.com/docs/chat/chat-sdk/build/features/messages/drafts
title: Create message drafts
updated_at: 2026-05-12T11:37:48.000Z
---

# Create message drafts

> For AI agents: documentation index at https://www.pubnub.com/llms-full.txt

`MessageDraft` represents an unpublished message. Use it to:

* [Edit text](#update-message-text) before publishing
* Add [channel references](https://www.pubnub.com/docs/chat/chat-sdk/build/features/channels/references.md), [user mentions](https://www.pubnub.com/docs/chat/chat-sdk/build/features/users/mentions.md), and [links](https://www.pubnub.com/docs/chat/chat-sdk/build/features/messages/links.md)
* Attach files

Display message elements (mentions, references, links) in your UI by adding a [message draft change listener](#add-message-draft-change-listener).

### Basics

Message drafts consist of simple strings and generic link element, which are used for user mentions, channel references, and URLs. Each link element contains a text and a reference to the linked element regardless if it's a user, a channel, or a URL.

:::note Store draft messages locally
Chat SDK does not persist drafts. Implement your own local storage to save drafts across channel switches.
:::

### Class diagram

```mermaid
classDiagram
 class MessageDraft {
    +string value
    +Channel channel
    +MessageDraftConfig config
    +Message? quotedMessage
    +[FileList | File[] | SendFileParameters["file"][]]? files
 }
 class MixedTextTypedElement {
    <<union>>
 }
 class TextTypes {
    <<enum>>
    +mention
    +textLink
    +channelReference
 }
 MessageDraft --> MixedTextTypedElement
 MixedTextTypedElement --o TextTypes
```

### Example

Consider the message `Hey, I sent Alex this link on the #offtopic channel.` where:

* `Alex` is a reference to the user with the ID of `alex_d`
* `link` is a URL of the `www.pubnub.com` website
* `#offtopic` is a reference to the channel with the ID of `group.offtopic`

The list of `MessageElement` objects returned by the `MessageDraftChangeListener` is as follows:

| Part of Message | Element Type | Code Used to Create |
| --- | --- | --- |
| Hey, I sent | `MixedTextTypedElement` | [messageDraft.update("Hey, I sent Alex this link on the #offtopic channel.")](#update-message-text) |
| Alex | `mention` | [messageDraft.addMention(12, 4, "mention", "alex_d")](#add-message-element) |
| this | `MixedTextTypedElement` | [messageDraft.update("Hey, I sent Alex this link on the #offtopic channel.")](#update-message-text) |
| link | `textLink` | [messageDraft.addMention(22, 4, "textLink", "http://www.pubnub.com")](#add-message-element) |
| on the | `MixedTextTypedElement` | [messageDraft.update("Hey, I sent Alex this link on the #offtopic channel.")](#update-message-text) |
| #offtopic | `channelReference` | [messageDraft.addMention(34, 9, "channelReference", "group.offtopic")](#add-message-element) |
| channel. | `MixedTextTypedElement` | [messageDraft.update("Hey, I sent Alex this link on the #offtopic channel.")](#update-message-text) |

### Internal mention format

By internally leveraging a Markdown-like syntax, the message draft format integrates links directly into the message text using the pattern `[link text](https://www.pubnub.com/docs/chat/chat-sdk/build/features/messages/link%20target)` understood by the Chat SDK.

| Mention Type | Example |
| --- | --- |
| user | `[John Doe](pn-user://john_doe)` |
| channel | `[General Chat](pn-channel://group.chat.123)` |
| url | `[PubNub](https://www.pubnub.com)` |

Custom schemas like `pn-user://` and `pn-channel://` are used to identify user and channel mentions, while traditional URLs are supported as-is.

:::warning Adding message elements
This syntax is internal only. To add elements, use [addMention()](#add-message-element).
:::

## Create a draft message

`createMessageDraft()` creates a message draft ([MessageDraft object](https://www.pubnub.com/docs/chat/chat-sdk/learn/chat-entities/message-draft.md)) that can consist of:

* Plain text
* [Files](https://www.pubnub.com/docs/chat/chat-sdk/build/features/messages/files.md)
* [Mentioned users](https://www.pubnub.com/docs/chat/chat-sdk/build/features/users/mentions.md)
* [Referenced channels](https://www.pubnub.com/docs/chat/chat-sdk/build/features/channels/references.md)
* [Links](https://www.pubnub.com/docs/chat/chat-sdk/build/features/messages/links.md)
* [Quoted messages](https://www.pubnub.com/docs/chat/chat-sdk/build/features/messages/quotes.md)

### Method signature

This method has the following signature:

```ts
channel.createMessageDraft(config?: Partial<MessageDraftConfig>): MessageDraft;
```

#### Input

| Parameter | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| config | Partial<MessageDraftConfig> | Optional |  | This object holds the configuration options. |
| config →userSuggestionSource | channel or global | Optional | `channel` | This parameter refers to the [Mentions](https://www.pubnub.com/docs/chat/chat-sdk/build/features/users/mentions.md) feature. Data source from which you want to retrieve users. You can choose either the list of channel members (`channel`) or users on the app's Admin Portal keyset (`global`). |
| config →isTypingIndicatorTriggered | boolean | Optional | `true` | This parameter refers to the [Typing Indicator](https://www.pubnub.com/docs/chat/chat-sdk/build/features/channels/typing-indicator.md) feature. Defines if the typing indicator should be enabled when writing the message. |
| config →userLimit | number | Optional | `10` | This parameter refers to the [Mentions](https://www.pubnub.com/docs/chat/chat-sdk/build/features/users/mentions.md) feature. Maximum number of usernames (`name` field from the `User` object) you can mention in one message. The default value is `10`, the min is `1`, and max is `100`. |
| config →channelLimit | number | Optional | `10` | This parameter refers to the [References](https://www.pubnub.com/docs/chat/chat-sdk/build/features/channels/references.md) feature. Maximum number of channel names (`name` field from the `Channel` object) you can reference in one message. The default value is `10`, the min is `1`, and max is `100`. |

#### Output

| Type | Description |
| --- | --- |
| `MessageDraft` | Instance of `MessageDraft`, which represents a draft version of a message with the content, all links, referenced channels, mentioned users and their names. |

### Sample code

Create a draft message containing just plain text.

```ts
messageDraft = channel.createMessageDraft({ userSuggestionSource: "global" })
```

## Add message draft change listener

Add a `MessageDraftState` listener to receive draft content changes and suggestions for mentions, channel references, and links.

:::note No automatic detection
Parse the message for mentions/links and add them with `addMention()`. Chat SDK does not auto-detect these.
:::

### Method signature

This method has the following signature:

```ts
messageDraft.addChangeListener(listener: (p0: MessageDraftState) => void): void;
```

#### Input

| Parameter | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| listener | MessageDraftState | Optional |  | The listener that receives the most current message elements and suggestions list. |

##### MessageDraftState

| Parameter | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| messageElements() | Array<MixedTextTypedElement> | Optional |  | Array of `MixedTextTypedElement` objects, representing the current state of the message draft. This could contain a mix of plain text and links, channel references, or user mentions. |
| suggestedMentions()) | Promise<Array<SuggestedMention>> | Optional |  | A `Promise` containing an array of [SuggestedMention](#suggestedmention) objects. These are potential suggestions for message elements based on the current text in the draft. |

##### SuggestedMention

A `SuggestedMention` represents a potential mention suggestion received from [MessageDraftState](#add-message-draft-change-listener) listener.

| Parameter | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| offset | number | Optional |  | The position from the start of the message draft where the message elements starts. It's counted from the beginning of the message (including spaces), with `0` as the first character. |
| replaceFrom | String | Optional |  | The original text at the given offset in the message draft text. |
| replaceWith | String | Optional |  | The suggested replacement for the `replaceFrom` text. |
| type | TextTypes | Optional |  | The message element type. Available types include: mention, channelReference, textLink |
| target | string | Optional |  | The actual mention element in string format: userId for a mention mention type, channelId for a channelReference mention type, url for a textLink mention type |

#### Output

This method doesn't return any data.

### Sample code

Add the listener to your message draft.

```ts
// Create a message draft
messageDraft = channel.createMessageDraft({ userSuggestionSource: "global" })
// Add the listener
messageDraft.addChangeListener(async function(state) {
      updateUI(state.messageElements) // where updateUI is your function for displaying the message content
      updateSuggestedMentionsUI(mentions) // where updateSuggestedMentionsUI is your function for displaying mention suggestions
    })
```

## Remove message draft change listener

Remove a previously added `MessageDraftState` listener.

### Method signature

This method has the following signature:

```ts
removeChangeListener(listener: (p0: MessageDraftState) => void): void;
```

#### Input

| Parameter | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| listener | MessageDraftState | Optional |  | The listener to remove. |

#### Output

This method doesn't return any data.

### Sample code

Remove a listener from your message draft.

```ts
messageDraft.removeChangeListener(listener)
```

## Add message element

`addMention()` adds a user mention, channel reference or a link specified by a mention target at a given offset.

### Method signature

This method has the following signature:

```ts
messageDraft.addMention(
  offset: number, 
  length: number, 
  mentionType: TextTypes, 
  mentionTarget: string
): void;
```

#### Input

| Parameter | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| offset | Int | Yes |  | Position of a character in a message where the message element you want to insert starts. It's counted from the beginning of the message (including spaces), with `0` as the first character. |
| length | Int | Yes |  | Number of characters the message element should occupy in the draft message's text. |
| mentionType | TextTypes | Yes |  | The message element type. Available types include: mention, channelReference, textLink |
| mentionTarget | string | Yes |  | The actual mention element in string format: userId for a mention mention type, channelId for a channelReference mention type, url for a textLink mention type |

#### Output

This method returns no output data.

### Sample code

Create the `Hello Alex! I have sent you this link on the #offtopic channel.` message where `Alex` is a user mention, `link` is a URL, and `#offtopic` is a channel reference.

```ts
// Create a message draft
messageDraft = channel.createMessageDraft({ userSuggestionSource: "global" })

// Add initial text
messageDraft.update("Hello Alex!")

// Add a user mention to the string "Alex"
messageDraft.addMention(6, 4, "mention", "alex_d"))

// Change the text
messageDraft.update("Hello Alex! I have sent you this link on the #offtopic channel.")

// Add a URL mention to the string "link"
messageDraft.addMention(33, 4, "textLink", "www.pubnub.com")

// Add a channel mention to the string "#offtopic"
messageDraft.addMention(45, 9, "channelReference", "group.offtopic")
```

## Remove message element

`removeMention()` removes a user mention, channel reference, or a link at a given offset.

### Method signature

This method has the following signature:

```ts
messageDraft.removeMention(offset: number): void;
```

#### Input

| Parameter | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| offset | number | Yes |  | Position of the first character of the message element you want to remove. |

:::warning Offset value
If you don't provide the position of the first character of the message element to remove, it isn't removed.
:::

#### Output

This method returns no output data.

### Sample code

Remove the URL element from the word `link` in the `Hello Alex! I have sent you this link on the #offtopic channel.` message.

```ts
// assume the message reads
// Hello Alex! I have sent you this link on the #offtopic channel.

// remove the link mention
messageDraft.removeMention(33)
```

## Update message text

`update()` replaces the text of a [draft message](https://www.pubnub.com/docs/chat/chat-sdk/build/features/messages/drafts.md) with new content.

:::warning Removing message elements
The SDK preserves message elements when possible. If element text is modified, that element is removed.
:::

### Method signature

This method has the following signature:

```ts
messageDraft.update(text: String)
```

#### Input

| Parameter | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| text | string | Yes |  | Text of the message that you want to update. |

#### Output

This method returns no output data.

### Sample code

Change the message `I sent Alex this picture.` to `I did not send Alex this picture.` where `Alex` is a user mention.

```ts
// the message reads:
// I sent [Alex] this picture.
// where [Alex] is a user mention
messageDraft.update( "I did not send Alex this picture.")
// the message now reads: 
// I did not send [Alex] this picture.
// the mention is preserved because its text wasn't changed
```

:::note Mention text changes
Changing mention text removes that mention. For finer control, use [Insert message text](#insert-message-text) and [Remove message text](#remove-message-text).
:::

## Insert suggested message element

Insert a message element from the [MessageDraftState](#add-message-draft-change-listener) listener into the `MessageDraft` object.

:::warning Text must match
`SuggestedMention.replaceFrom` must match the draft text at the specified position, or an exception is thrown.
:::

### Method signature

This method has the following signature:

```ts
messageDraft.insertSuggestedMention(
  mention: SuggestedMention, 
  text: String
)
```

#### Input

| Parameter | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| mention | SuggestedMention | Yes |  | A user, channel, or URL suggestion obtained from [MessageDraftState](#add-message-draft-change-listener) listener. |
| text | string | Yes |  | The text you want the message element to display. |

#### Output

This method returns no output data.

### Sample code

Register a listener and insert a suggested element.

```ts
// Create a message draft
messageDraft = channel.createMessageDraft({ userSuggestionSource: "global" })
// Add the listener
messageDraft.addChangeListener(async function(state) {
      updateUI(state.messageElements) // where updateUI is your function for displaying the message content
      let mentions = await state.suggestedMentions
      messageDraft.insertSuggestedMention(mentions[0], mentions[0].replaceWith)
    })
```

## Insert message text

`insertText()` inserts plain text in the [draft message](https://www.pubnub.com/docs/chat/chat-sdk/build/features/messages/drafts.md) at the specified offset.

:::warning Removing message elements
Inserting text at an existing message element position removes that element.
:::

### Method signature

This method has the following signature:

```ts
messageDraft.insertText(
    offset: number, 
    text: string
)
```

#### Input

| Parameter | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| offset | number | Yes |  | Position of a character in a message where the text you want to insert starts. It's counted from the beginning of the message (including spaces), with `0` as the first character. |
| text | string | Yes |  | Text that you want to insert. |

#### Output

This method returns no output data.

### Sample code

In the message `Check this support article https://www.support-article.com/.`, add the word `out` between the words `Check` and `this`.

```ts
// The message reads:
// Check this support article https://www.support-article.com/.
messageDraft.insertText(6, "out")
// The message now reads:
// Check out this support article https://www.support-article.com/.
```

## Remove message text

`removeText()` removes plain text from the [draft message](https://www.pubnub.com/docs/chat/chat-sdk/build/features/messages/drafts.md) at the specified offset.

:::warning Removing message elements
Removing text at an existing message element position removes that element.
:::

### Method signature

This method has the following signature:

```ts
messageDraft.removeText(
    offset: number, 
    length: number
)
```

#### Input

| Parameter | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| offset | number | Yes |  | Position of a character in a message where the text you want to insert starts. It's counted from the beginning of the message (including spaces), with `0` as the first character. |
| length | number | Yes |  | How many characters to remove, starting at the given `offset`. |

#### Output

This method returns no output data.

### Sample code

In the message `Check out this support article https://www.support-article.com/.`, remove the word `out`.

```ts
// The message reads:
// Check out this support article https://www.support-article.com/.
messageDraft.removeText(5, 4)
// The message now reads:
// Check this support article https://www.support-article.com/.
```

## Send a draft message

`send()` publishes the draft message with all [mentioned users](https://www.pubnub.com/docs/chat/chat-sdk/build/features/users/mentions.md), [links](https://www.pubnub.com/docs/chat/chat-sdk/build/features/messages/links.md), and [referenced channels](https://www.pubnub.com/docs/chat/chat-sdk/build/features/channels/references.md). Mentioning users also emits [mention events](https://www.pubnub.com/docs/chat/chat-sdk/build/features/custom-events.md#events-for-mentions).

### Method signature

This method has the following signature:

```ts
messageDraft.send(params?: SendTextParams): Promise<PublishResponse>
```

#### Input

| Parameter | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| params | SendTextParams | Optional |  | Object with additional options for sending the message. Refer to [sendText()](https://www.pubnub.com/docs/chat/chat-sdk/build/features/messages/send-receive.md) for the full list of supported parameters. |

#### Output

| Type | Description |
| --- | --- |
| `Promise<PublishResponse>` | Result of the PubNub Publish call. |

### Sample code

Send a draft message containing just plain text.

```ts
// Create a message draft
messageDraft = channel.createMessageDraft({ userSuggestionSource: "global" })

// Add text
messageDraft.update("Hello!")

// Send the message
messageDraft.send()
```

## Add quoted message

`addQuote()` adds a quoted message to the draft. See [Quote message](https://www.pubnub.com/docs/chat/chat-sdk/build/features/messages/quotes.md#quote-message) for details.

### Sample code

Send a draft message containing just plain text.

```ts
// Create a message draft
messageDraft = channel.createMessageDraft({ userSuggestionSource: "global" })

// return a message object
const message = await channel.getMessage("16200000000000001")

// add a quote to the message
messageDraft.addQuote(message)

// alternatively, use direct assignment to add or replace a quote
messageDraft.quotedMessage = message // Add or replace the quote
```

## Remove quoted message

`removeQuote()` removes the quote from the draft message. See [Remove quoted message](https://www.pubnub.com/docs/chat/chat-sdk/build/features/messages/quotes.md#remove-quoted-message) for details.

### Sample code

Send a draft message containing just plain text.

```ts
// Create a message draft
messageDraft = channel.createMessageDraft({ userSuggestionSource: "global" })

// return a message object
const message = await channel.getMessage("16200000000000001")

// add a quote to the message
messageDraft.addQuote(message)

// alternatively, use direct assignment to add or replace a quote
messageDraft.quotedMessage = message // Add or replace the quote

// remove the quote by passing null
messageDraft.removeQuote()

// alternatively, remove the quote by setting quotedMessage to null
messageDraft.quotedMessage = null
```

Last updated at: 2026-05-12T11:37:48.000Z
