Skip to main content
Messaging in New Expensify is fast, flexible, and works seamlessly even when offline. Every message is optimistically rendered while syncing in the background, ensuring a smooth experience across all devices.

Composing Messages

Basic Text Messages

The simplest way to communicate is through text messages:
1

Navigate to a Chat

Open any 1-on-1 chat, group conversation, or report thread.
2

Type Your Message

Click the message input field at the bottom and start typing.
3

Send

Press Enter (or Cmd/Ctrl + Enter) to send. Your message appears immediately.
Messages are sent optimistically—they appear in your chat immediately and sync to the server in the background. If you’re offline, they’ll send automatically when reconnected.

Message Architecture

Here’s how messages are created under the hood:
// From src/libs/actions/Report/index.ts
function addComment(
    reportID: string,
    text: string = '',
    file?: FileObject,
) {
    const reportComment = Parser.htmlToMarkdown(text.trim());
    
    // Build optimistic report action
    const optimisticReportAction = buildOptimisticAddCommentReportAction(
        reportComment,
        file,
    );
    
    const optimisticData: OnyxUpdate[] = [
        {
            onyxMethod: Onyx.METHOD.MERGE,
            key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`,
            value: {
                [optimisticReportAction.reportActionID]: optimisticReportAction,
            },
        },
        {
            onyxMethod: Onyx.METHOD.MERGE,
            key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`,
            value: {
                lastMessageText: reportComment,
                lastVisibleActionCreated: optimisticReportAction.created,
            },
        },
    ];
    
    API.write(WRITE_COMMANDS.ADD_COMMENT, parameters, {
        optimisticData,
        successData,
        failureData,
    });
}

Text Formatting

New Expensify supports rich text formatting using markdown:

Supported Markdown

**Bold text**
*Italic text*
~~Strikethrough~~
`code`
Renders as:
  • Bold text
  • Italic text
  • Strikethrough
  • code

Parsing Implementation

Markdown is parsed using the ExpensiMark parser:
// From src/libs/Parser.ts
import ExpensiMark from 'expensify-common/lib/ExpensiMark';

const parser = new ExpensiMark();

// Convert HTML to Markdown for storage
function htmlToMarkdown(html: string): string {
    return parser.htmlToMarkdown(html);
}

// Convert Markdown to HTML for display
function markdownToHTML(markdown: string): string {
    return parser.replace(markdown);
}

Attachments

Uploading Files

Attach files to your messages in several ways:
Drag files from your computer directly into the message composer. Supported file types:
  • Images (PNG, JPG, GIF, WebP)
  • PDFs
  • Documents (limited support)

File Upload Code

// Handling file attachments from src/libs/actions/Report/index.ts
function addComment(
    reportID: string,
    text: string = '',
    file?: FileObject,
) {
    let attachmentParams: Partial<AddCommentOrAttachmentParams> = {};
    
    if (file) {
        attachmentParams = {
            file,
            source: file.uri ?? '',
            filename: file.name,
        };
    }
    
    const parameters: AddCommentOrAttachmentParams = {
        reportID,
        reportComment: text,
        ...attachmentParams,
    };
}

Editing Messages

Edit your own messages to fix typos or update information:
1

Access Edit Mode

Hover over your message and click the edit icon (or long-press on mobile).
2

Make Changes

Update the text. The original message is preserved in history.
3

Save

Click Save or press Enter. An “edited” indicator appears on the message.
// Editing messages from src/libs/actions/Report/index.ts
function editReportComment(
    reportID: string,
    originalReportAction: ReportAction,
    textForNewComment: string,
) {
    const htmlForNewComment = Parser.replace(textForNewComment);
    
    const optimisticReportActions = {
        [originalReportAction.reportActionID]: {
            message: [
                {
                    type: 'COMMENT',
                    html: htmlForNewComment,
                    text: textForNewComment,
                },
            ],
            pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE,
        },
    };
    
    API.write(WRITE_COMMANDS.UPDATE_COMMENT, parameters, {
        optimisticData,
        successData,
        failureData,
    });
}
You can only edit your own messages. Messages older than 30 days cannot be edited.

Deleting Messages

Remove messages from the conversation:
1

Select Message

Hover over the message you want to delete.
2

Delete

Click the delete icon (trash can) and confirm.
3

Result

The message is replaced with “[Deleted message]” placeholder.
// Deleting messages from src/libs/actions/Report/index.ts
function deleteReportComment(
    reportID: string,
    reportAction: ReportAction,
) {
    const deletedMessage = [
        {
            translationKey: '',
            type: 'COMMENT',
            html: '',
            text: '',
            isEdited: true,
            isDeletedParentAction: true,
        },
    ];
    
    const optimisticReportActions = {
        [reportAction.reportActionID]: {
            pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE,
            previousMessage: reportAction.message,
            message: deletedMessage,
        },
    };
}

Thread Management

Conversation Threads

Every report and expense has its own conversation thread:

Expense Threads

Discuss individual expenses with approvers and team members.

Report Threads

Coordinate on entire expense reports during the approval process.

Transaction Threads

Comment on specific transactions within a report.

Task Threads

Discuss task details and progress updates.

Thread Structure

// Thread creation from src/libs/ReportUtils.ts
function buildTransactionThread(
    reportAction: ReportAction,
    reportID: string,
    policyID: string,
): OptimisticChatReport {
    const reportName = `Transaction Thread`;
    const currentTime = DateUtils.getDBTime();
    
    return {
        reportID: generateReportID(),
        chatType: CONST.REPORT.CHAT_TYPE.TRANSACTION_THREAD,
        parentReportID: reportID,
        parentReportActionID: reportAction.reportActionID,
        reportName,
        policyID,
        lastActorAccountID: reportAction.actorAccountID,
        lastReadTime: currentTime,
    };
}

Typing Indicators

See when other participants are typing:
// Real-time typing from src/libs/actions/Report/index.ts
function broadcastUserIsTyping(reportID: string) {
    const privateUserChannelName = `private-user-accountID-${currentUserAccountID}`;
    
    const typingStatus: UserIsTypingEvent = {
        [reportID]: true,
    };
    
    Pusher.sendEvent(
        privateUserChannelName,
        Pusher.TYPE.USER_IS_TYPING,
        typingStatus
    );
}

function broadcastUserIsLeavingRoom(reportID: string) {
    const privateUserChannelName = `private-user-accountID-${currentUserAccountID}`;
    
    const leavingStatus: UserIsLeavingRoomEvent = {
        [reportID]: true,
    };
    
    Pusher.sendEvent(
        privateUserChannelName,
        Pusher.TYPE.USER_IS_LEAVING_ROOM,
        leavingStatus
    );
}

Message Status

Understand message delivery states:

Sending

Gray checkmark—message is being sent to server

Sent

Green checkmark—message successfully delivered

Failed

Red indicator—message failed to send (tap to retry)

Best Practices

Format important information with bold or italic text, but don’t overdo it. Clear, concise writing is more effective than heavy formatting.
Reply in the correct thread context. Don’t start new conversations for follow-up questions on existing expenses or reports.
If you need to correct a message, edit it rather than deleting and reposting. This preserves conversation context.
Upload receipts and documents directly to expense entries rather than sharing them in chat when possible.

Next Steps

Task Management

Learn how to create and manage tasks from chat conversations

Mentions & Notifications

Master @mentions and notification settings

Chat Overview

Back to chat features overview

Build docs developers (and LLMs) love