---
url: https://talkjs.com/docs/Data_APIs/Flutter/Message_Content
title: 'Message content'
minidoc-source: dart
---

Every message has "content" which specifies the information in the message.
This could be some formatted text, a file attachment, or a shared location.

See the [conceptual documentation](/Concepts/Message_Content/) for message
content to learn more about how TalkJS represents message content.

## ContentBlock
/** The content of a message is structured as a list of content blocks.
Currently, each message can only have one content block, but this will change in the future. This will not be considered a breaking change, so your code should assume there can be multiple content blocks.
These blocks are rendered in order, top-to-bottom.
Currently the available Content Block types are:
- `type: "text"` ([TextBlock])
- `type: "file"` ([FileBlock])
- `type: "location"` ([LocationBlock]) */
class ContentBlock {
ContentBlock()

Map<String, dynamic>toJson()
}

## TextBlock
/** A block of formatted text in a message's content.
Each TextBlock is a tree of children describing the structure of some formatted text. Each child is either a plain text string, or a `node` representing some text with additional formatting.
For example, if the user typed:>*This first bit* is bold, and *_the second bit_* is bold and italics
Then this would become a Text Block with the structure:
```dart
TextBlock(
children: [
Markup(type: 'bold', children: ['This first bit']),
' is bold, and ',
Markup(
type: 'bold',
children: [
Markup(type: 'italic', children: ['the second bit']),
],
),
' is bold and italics',
],
)
```
Rather than relying the automatic message parsing, you can also specify the `TextBlock` directly using [ConversationRef.sendMessage] with [SendMessageParams]. */
class TextBlock {
final String type;

/** The tree of formatted text nodes. */
final List<Object>children;

TextBlock({required List<Object>children})

Map<String, dynamic>toJson()
}

<Callout>
<details style={{margin: "1rem"}}>
  <summary className="font-bold cursor-pointer">Full documentation of each TextNode variant</summary>

  

## Markup
/** A node in a [TextBlock] that renders its children with a specific style. */
class Markup {
/** The kind of formatting to apply when rendering the children
- `type: "bold"` is used when users type `*text*` and is rendered with HTML `<strong>`
- `type: "italic"` is used when users type `_text_` and is rendered with HTML `<em>`
- `type: "strikethrough"` is used when users type `~text~` and is rendered with HTML `<s>` */
final String type;

final List<Object>children;

Markup({required String type, required List<Object>children})
}
  

## BulletList
/** A node in a [TextBlock] that adds indentation for a bullet-point list around its children (HTML `<ul>`).
Used when users send a bullet-point list by starting lines of their message with `-` or `*`. */
class BulletList {
final String type;

final List<Object>children;

BulletList({required List<Object>children})
}
  

## BulletPoint
/** A node in a [TextBlock] that renders its children with a bullet-point (HTML `<li>`).
Used when users start a line of their message with `-` or `*`. */
class BulletPoint {
final String type;

final List<Object>children;

BulletPoint({required List<Object>children})
}
  

## Link
/** A node in a [TextBlock] that renders its children as a clickable link (HTML `<a>`).
By default, users do not have permission to send messages containing [Link] as it can be used to maliciously hide the true destination of a link. */
class Link {
final String type;

/** The URL to open when the node is clicked. */
final String url;

final List<Object>children;

Link({required String url, required List<Object>children})

Map<String, dynamic>toJson()
}
  

## AutoLink
/** A node in a [TextBlock] that renders `text` as a link (HTML `<a>`).
Used when user-typed text is turned into a link automatically.
Unlike [Link], users do have permission to send [AutoLink] by default, because the `text` and `url` properties must match. Specifically:
- If `text` is an email, `url` must contain a `mailto:` link to the same email address
- If `text` is a phone number, `url` must contain a `tel:` link to the same phone number
- If `text` is a website, the domain name including subdomains must be the same in both `text` and `url`. If `text` includes a protocol (such as `https`), path (/page), query string (?page=true), or url fragment (#title), they must be the same in `url`. If `text` does not specify a protocol, `url` must use either `https` or `http`.
This means that the following AutoLink is valid:
```dart
AutoLink(
text: 'talkjs.com',
url: '/docs/JavaScript_Data_API/Message_Content/#AutoLinkNode',
)
```
That link will appear as `talkjs.com` and link you to the specific section of the documentation that explains how [AutoLink] works.
These rules ensure that the user knows what link they are clicking, and prevents [AutoLink] being used for phishing. If you try to send a message containing an [AutoLink] that breaks these rules, the request will be rejected. */
class AutoLink {
final String type;

/** The URL to open when a user clicks this node. */
final String url;

/** The text to display in the link. */
final String text;

AutoLink({required String url, required String text})

Map<String, dynamic>toJson()
}
  

## ActionLink
/** A node in a [TextBlock] that renders its children as a clickable [action link](/docs/Guides/JavaScript/Classic/Action_Buttons_Links/) which triggers a custom action.
By default, users do not have permission to send messages containing [ActionLink] as it can be used maliciously to trick others into invoking custom actions. For example, a user could send an "accept offer" action link, but disguise it as a link to a website. */
class ActionLink {
final String type;

/** The name of the custom action to invoke when the link is clicked. */
final String action;

/** The parameters to pass to the custom action. */
final Map<String, String>params;

final List<Object>children;

ActionLink({required String action, required Map<String, String>params, required List<Object>children})

Map<String, dynamic>toJson()
}
  

## ActionButton
/** A node in a [TextBlock] that renders its children as a clickable [action button](/docs/Guides/JavaScript/Classic/Action_Buttons_Links/) which triggers a custom action.
By default, users do not have permission to send messages containing action buttons as they can be used maliciously to trick others into invoking custom actions. For example, a user could send an "accept offer" action button, but disguise it as "view offer". */
class ActionButton {
final String type;

/** The name of the custom action to invoke when the button is clicked. */
final String action;

/** The parameters to pass to the custom action. */
final Map<String, String>params;

final List<Object>children;

ActionButton({required String action, required Map<String, String>params, required List<Object>children})

Map<String, dynamic>toJson()
}
  

## CodeSpan
/** A node in a [TextBlock] that renders `text` in an inline code span (HTML `<code>`).
Used when a user types ` ```text``` `. */
class CodeSpan {
final String type;

final String text;

CodeSpan({required String text})
}
  

## CustomEmoji
/** A node in a [TextBlock] that is used for [custom emoji](/docs/Features/Messages/Emojis/#custom-emojis). */
class CustomEmoji {
final String type;

/** The name (including colons at the start and end) of the custom emoji to show. */
final String text;

CustomEmoji({required String text})
}
  

## Mention
/** A node in a [TextBlock] that is used when a user is [mentioned](/docs/Features/Messages/Mentions/).
Used when a user types `@name` and selects the user they want to mention. */
class Mention {
final String type;

/** The ID of the user who is mentioned. */
final String id;

/** The name of the user who is mentioned. */
final String text;

Mention({required String id, required String text, String? internalId})

Map<String, dynamic>toJson()
}
</details>
</Callout>

## FileBlock
class FileBlock {
FileBlock()

Map<String, dynamic>toJson()
}

<Callout>
<details style={{margin: "1rem"}}>
  <summary className="font-bold cursor-pointer">Full documentation of each FileBlock variant</summary>

  

## GenericFileBlock
/** The most basic FileBlock variant, used whenever there is no additional metadata for a file.
Do not try to check for `subtype == null` directly, as this will break when we add new FileBlock variants in the future.
Instead, treat GenericFileBlock as the default. For example:
```dart
switch (block) {
case VideoBlock():
handleVideoBlock(block);
case ImageBlock():
handleImageBlock(block);
case AudioBlock():
handleAudioBlock(block);
case VoiceBlock():
handleVoiceBlock(block);
default:
handleGenericFileBlock(block);
}
``` */
class GenericFileBlock {
final String type;

/** Never set for generic file blocks. */
String? subtype;

/** An encoded identifier for this file. Use in [SendFileBlock] to send this file in another message. */
final String fileToken;

/** The URL where you can fetch the file */
final String url;

/** The size of the file in bytes */
final int size;

/** The name of the file, including file extension */
final String filename;

GenericFileBlock({required String fileToken, required String url, required int size, required String filename})
}
  

## VideoBlock
/** A FileBlock variant for a video attachment, with additional video-specific metadata.
You can identify this variant by checking for `subtype: "video"`.
Includes metadata about the height and width of the video in pixels, and the duration of the video in seconds, where available.
Videos that you upload with the TalkJS UI will include the dimensions and duration as long as the sender's browser can preview the file. Videos that you upload with the REST API or [TalkSession.uploadVideo] will include this metadata if you specified it when uploading. Videos attached in a reply to an email notification will not include any metadata. */
class VideoBlock {
final String type;

final String subtype;

/** An encoded identifier for this file. Use in [SendFileBlock] to send this video in another message. */
final String fileToken;

/** The URL where you can fetch the file. */
final String url;

/** The size of the file in bytes. */
final int size;

/** The name of the video file, including file extension. */
final String filename;

/** The width of the video in pixels, if known. */
int? width;

/** The height of the video in pixels, if known. */
int? height;

/** The duration of the video in seconds, if known. */
double? duration;

VideoBlock({required String fileToken, required String url, required int size, required String filename, int? width, int? height, double? duration})

Map<String, dynamic>toJson()
}
  

## ImageBlock
/** A FileBlock variant for an image attachment, with additional image-specific metadata.
You can identify this variant by checking for `subtype: "image"`.
Includes metadata about the height and width of the image in pixels, where available.
Images that you upload with the TalkJS UI will include the image dimensions as long as the sender's browser can preview the file. Images that you upload with the REST API or [TalkSession.uploadImage] will include the dimensions if you specified them when uploading. Image attached in a reply to an email notification will not include the dimensions. */
class ImageBlock {
final String type;

final String subtype;

/** An encoded identifier for this file. Use in [SendFileBlock] to send this image in another message. */
final String fileToken;

/** The URL where you can fetch the file. */
final String url;

/** The size of the file in bytes. */
final int size;

/** The name of the image file, including file extension. */
final String filename;

/** The width of the image in pixels, if known. */
int? width;

/** The height of the image in pixels, if known. */
int? height;

ImageBlock({required String fileToken, required String url, required int size, required String filename, int? width, int? height})

Map<String, dynamic>toJson()
}
  

## AudioBlock
/** A FileBlock variant for an audio attachment, with additional audio-specific metadata.
You can identify this variant by checking for `subtype: "audio"`.
The same file could be uploaded as either an audio block, or as a [VoiceBlock]. The same data will be available either way, but they will be rendered differently in the UI.
Includes metadata about the duration of the audio file in seconds, where available.
Audio files that you upload with the TalkJS UI will include the duration as long as the sender's browser can preview the file. Audio files that you upload with the REST API or [TalkSession.uploadAudio] will include the duration if you specified it when uploading. Audio files attached in a reply to an email notification will not include the duration. */
class AudioBlock {
final String type;

final String subtype;

/** An encoded identifier for this file. Use in [SendFileBlock] to send this file in another message. */
final String fileToken;

/** The URL where you can fetch the file */
final String url;

/** The size of the file in bytes */
final int size;

/** The name of the audio file, including file extension */
final String filename;

/** The duration of the audio in seconds, if known */
double? duration;

AudioBlock({required String fileToken, required String url, required int size, required String filename, double? duration})

Map<String, dynamic>toJson()
}
  

## VoiceBlock
/** A FileBlock variant for a voice recording attachment, with additional voice-recording-specific metadata.
You can identify this variant by checking for `subtype: "voice"`.
The same file could be uploaded as either a voice block, or as an [AudioBlock]. The same data will be available either way, but they will be rendered differently in the UI.
Includes metadata about the duration of the recording in seconds, where available.
Voice recordings done in the TalkJS UI will always include the duration. Voice recording that you upload with the REST API or [TalkSession.uploadVoice] will include this metadata if you specified it when uploading.
Voice recordings will never be taken from a reply to an email notification. Any attached audio file will become an [AudioBlock] instead of a voice block. */
class VoiceBlock {
final String type;

final String subtype;

/** An encoded identifier for this file. Use in [SendFileBlock] to send this voice recording in another message. */
final String fileToken;

/** The URL where you can fetch the file */
final String url;

/** The size of the file in bytes */
final int size;

/** The name of the file, including file extension */
final String filename;

/** The duration of the voice recording in seconds, if known */
double? duration;

VoiceBlock({required String fileToken, required String url, required int size, required String filename, double? duration})

Map<String, dynamic>toJson()
}
</details>
</Callout>

## LocationBlock
/** A block showing a location in the world, typically because a user shared their location in the chat.
In the TalkJS UI, location blocks are rendered as a link to Google Maps, with the map pin showing at the specified coordinate. A thumbnail shows the surrounding area on the map. */
class LocationBlock {
final String type;

/** The north-south coordinate of the location.
Usually listed first in a pair of coordinates.
Must be a number between -90 and 90 */
final double latitude;

/** The east-west coordinate of the location.
Usually listed second in a pair of coordinates.
Must be a number between -180 and 180 */
final double longitude;

LocationBlock({required double latitude, required double longitude})

Map<String, dynamic>toJson()
}

{/* FIXME: SendContentBlock is a typealias currently, which is not (yet) supported.*/}
{/* 

## SendContentBlock
/** The version of ContentBlock that is used when sending or editing messages.
This is the same as ContentBlock except it uses SendFileBlock instead of FileBlock
`SendContentBlock` is a subset of `ContentBlock`. This means that you can re-send the `content` from an existing message without any issues:
```ts
const existingMessage: MessageSnapshot = ...;
const convRef = session.conversation('example_conversation_id');
convRef.send({ content: existingMessage.content });
``` */
export type SendContentBlock = TextBlock|SendFileBlock|LocationBlock; */}

## SendFileBlock
/** The version of [FileBlock] that is used when sending or editing messages.
When a user receives the message you send with `SendFileBlock`, this block will have turned into one of the [FileBlock] variants.
For information on how to obtain a file token, see [FileToken].
The `SendFileBlock` interface is a subset of the `FileBlock` interface. If you have an existing `FileBlock` received in a message, you can re-use that block to re-send the same attachment:
```dart
final existingFileBlock = ...
final imageToShare = existingFileBlock.content[0] as ImageBlock;
final convRef = await session.conversation('example_conversation_id');
await convRef.sendMessage(SendMessageParams(content: [imageToShare]));
``` */
class SendFileBlock {
final String type;

/** The encoded identifier for the file, obtained by uploading a file with [Session.sendFile], or taken from another message. */
final String fileToken;

SendFileBlock({required String fileToken})

Map<String, dynamic>toJson()
}