---
source_url: https://www.pubnub.com/docs/chat/chat-sdk/build/features/messages/drafts
title: Create message drafts
updated_at: 2026-06-01T05:27:14.097Z
---

> Documentation Index
> For a curated overview of PubNub documentation, see: https://www.pubnub.com/docs/llms.txt
> For the full list of all documentation pages, see: https://www.pubnub.com/docs/llms-full.txt


# Create message drafts

`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), [user mentions](https://www.pubnub.com/docs/chat/chat-sdk/build/features/users/mentions), and [links](https://www.pubnub.com/docs/chat/chat-sdk/build/features/messages/links)
* 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

### 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/link target)` understood by the Chat SDK.

| Mention Type | Example |
| --- | --- |
| user | `[John Doe](https://www.pubnub.com/docs/pn-user://john_doe)` |
| channel | `[General Chat](https://www.pubnub.com/docs/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)) that can consist of:

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

### Method signature

This method has the following signature:

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

#### Input

| Parameter | Description |
| --- | --- |
| `config`Type: `Partial<MessageDraftConfig>`Default: n/a | This object holds the configuration options. |
| `config` →`userSuggestionSource`Type: `channel` or `global`Default: `channel` | This parameter refers to the [Mentions](https://www.pubnub.com/docs/chat/chat-sdk/build/features/users/mentions) 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`Type: `boolean`Default: `true` | This parameter refers to the [Typing Indicator](https://www.pubnub.com/docs/chat/chat-sdk/build/features/channels/typing-indicator) feature. Defines if the typing indicator should be enabled when writing the message. |
| `config` →`userLimit`Type: `number`Default: `10` | This parameter refers to the [Mentions](https://www.pubnub.com/docs/chat/chat-sdk/build/features/users/mentions) 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`Type: `number`Default: `10` | This parameter refers to the [References](https://www.pubnub.com/docs/chat/chat-sdk/build/features/channels/references) 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 | Description |
| --- | --- |
| `listener`Type: [MessageDraftState](#messagedraftstate) | The listener that receives the most current message elements and suggestions list. |

##### MessageDraftState

| Parameter | Description |
| --- | --- |
| `messageElements()`Type: `Array<MixedTextTypedElement>` | 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())`Type: `Promise<Array<SuggestedMention>>` | 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 | Description |
| --- | --- |
| `offset`Type: `number` | 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`Type: `String` | The original text at the given offset in the message draft text. |
| `replaceWith`Type: `String` | The suggested replacement for the `replaceFrom` text. |
| `type`Type: `TextTypes` | The message element type. Available types include: mention, channelReference, textLink |
| `target`Type: `string` | 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 | Description |
| --- | --- |
| `listener`Type: [MessageDraftState](#messagedraftstate) | 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 | Description |
| --- | --- |
| `offset` *Type: `Int`Default: n/a | 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` *Type: `Int`Default: n/a | Number of characters the message element should occupy in the draft message's text. |
| `mentionType` *Type: `TextTypes`Default: n/a | The message element type. Available types include: mention, channelReference, textLink |
| `mentionTarget` *Type: `string`Default: n/a | 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 | Description |
| --- | --- |
| `offset` *Type: `number`Default: n/a | 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) 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 | Description |
| --- | --- |
| `text` *Type: `string`Default: n/a | 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 | Description |
| --- | --- |
| `mention` *Type: [SuggestedMention](#suggestedmention)Default: n/a | A user, channel, or URL suggestion obtained from [MessageDraftState](#add-message-draft-change-listener) listener. |
| `text` *Type: `string`Default: n/a | 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) 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 | Description |
| --- | --- |
| `offset` *Type: `number`Default: n/a | 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` *Type: `string`Default: n/a | 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) 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 | Description |
| --- | --- |
| `offset` *Type: `number`Default: n/a | 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` *Type: `number`Default: n/a | 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), [links](https://www.pubnub.com/docs/chat/chat-sdk/build/features/messages/links), and [referenced channels](https://www.pubnub.com/docs/chat/chat-sdk/build/features/channels/references). Mentioning users also emits [mention events](https://www.pubnub.com/docs/chat/chat-sdk/build/features/custom-events#events-for-mentions).

### Method signature

This method has the following signature:

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

#### Input

| Parameter | Description |
| --- | --- |
| `params`Type: `SendTextParams`Default: n/a | Object with additional options for sending the message. Refer to [sendText()](https://www.pubnub.com/docs/chat/chat-sdk/build/features/messages/send-receive) 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#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#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
```