Skip to content

Support attachments in your app built with XMTP

Use the remote attachment, multiple remote attachments, or attachment content type to support attachments in your app.

Support remote attachments of any size

One remote attachment of any size can be sent in a message using the RemoteAttachmentCodec and a storage provider.

To send multiple remote attachments of any size in a single message, see Support multiple remote attachments of any size.

Configure the content type

For Browser SDK (v6.0.0+) and Node SDK (v5.0.0+), attachments and remote attachments are built-in and do not require codec registration. Skip this step for these SDKs.

For other SDKs, register the codec:

React Native
const client = await Client.create(signer, {
  env: 'production',
  codecs: [new RemoteAttachmentCodec(), new StaticAttachmentCodec()],
});

Send a remote attachment

import { encryptAttachment } from '@xmtp/node-sdk';
 
// create an attachment object
const attachment = {
  filename: 'test.txt',
  mimeType: 'text/plain',
  content: new Uint8Array([1, 2, 3]),
};
 
// encrypt the attachment
const encryptedAttachment = encryptAttachment(attachment);
// encryptedAttachment.payload is the encrypted bytes

Upload an encrypted attachment to a location where it will be accessible via an HTTPS GET request. This location will depend on which storage provider you use based on your needs.

Now that you have a url, you can create a RemoteAttachment:

// create remote attachment object
const remoteAttachment = {
  contentDigest: encryptedAttachment.digest,
  contentLength: encryptedAttachment.contentLength,
  filename: encryptedAttachment.filename,
  nonce: encryptedAttachment.nonce,
  salt: encryptedAttachment.salt,
  scheme: 'https://',
  secret: encryptedAttachment.secret,
  url: url,
};
 
// send the remote attachment
await conversation.sendRemoteAttachment(remoteAttachment);

Receive, decode, and decrypt a remote attachment

Now that you can send a remote attachment, you need a way to receive it. For example:

Node
import { contentTypesAreEqual } from '@xmtp/content-type-primitives';
import { decryptAttachment, contentTypeRemoteAttachment } from '@xmtp/node-sdk';
 
if (contentTypesAreEqual(message.contentType, contentTypeRemoteAttachment())) {
  const response = await fetch(message.content.url);
  if (response.ok) {
    const encryptedData = await response.arrayBuffer();
    const attachment = decryptAttachment(encryptedData, message.content);
 
    const filename = attachment.filename; // => "screenshot.png"
    const mimeType = attachment.mimeType; // => "image/png",
    const data = attachment.data; // => the image data
  }
}

To handle unsupported content types, refer to the fallback section.

Support multiple remote attachments of any size

Multiple remote attachments of any size can be sent in a single message using the MultiRemoteAttachmentCodec and a storage provider.

Register necessary codecs

Note: For Browser SDK (v6.0.0+) and Node SDK (v5.0.0+), multi remote attachments are built-in and do not require codec registration.

For other SDKs, register the codecs:

React Native
export const registerCodecs = () => {
  Client.register(new AttachmentCodec());
  Client.register(new RemoteAttachmentCodec());
  Client.register(new MultiRemoteAttachmentCodec());
};

Create multiple attachment objects

Each attachment in the attachments array contains a URL that points to an encrypted EncodedContent object. The content must be accessible by an HTTP GET request to the URL.

Browser
// Load files (example using browser File API)
const file1 = event.target.files[0]; // first file from input
const file2 = event.target.files[1]; // second file from input
 
const attachment1 = {
  filename: file1.name,
  mimeType: file1.type,
  data: new Uint8Array(await file1.arrayBuffer()),
};
 
const attachment2 = {
  filename: file2.name,
  mimeType: file2.type,
  data: new Uint8Array(await file2.arrayBuffer()),
};

Encrypt and upload multiple attachments to a remote server

Browser
import {
  encryptAttachment,
  type RemoteAttachment,
} from '@xmtp/browser-sdk';
 
const remoteAttachments: RemoteAttachment[] = [];
 
for (const attachment of [attachment1, attachment2]) {
  // Encrypt the attachment
  const encryptedAttachment = await encryptAttachment(attachment);
 
  // Upload encrypted payload to your storage provider
  // (Integrator must supply upload functionality!)
  const url = await uploadToStorage(encryptedAttachment.payload);
 
  // Build the remote attachment info
  remoteAttachments.push({
    contentDigest: encryptedAttachment.digest,
    contentLength: encryptedAttachment.contentLength,
    filename: encryptedAttachment.filename,
    nonce: encryptedAttachment.nonce,
    salt: encryptedAttachment.salt,
    scheme: 'https',
    secret: encryptedAttachment.secret,
    url,
  });
}

Send a message with multiple remote attachments

Browser
await conversation.sendMultiRemoteAttachment({
  attachments: remoteAttachments,
});

Recognize and decode a multi remote attachment

Browser
import { contentTypesAreEqual } from '@xmtp/content-type-primitives';
import { contentTypeMultiRemoteAttachment } from '@xmtp/browser-sdk';
 
const messages = await conversation.messages();
const message = messages[0];
 
if (
  contentTypesAreEqual(
    message.contentType,
    await contentTypeMultiRemoteAttachment()
  )
) {
  // Decode the raw content back into a MultiRemoteAttachment
  const multiRemoteAttachment = message.content;
 
  // See next section to download and decrypt the attachments
}

Decode, download, and decrypt the attachments

Browser
import { decryptAttachment, type Attachment } from '@xmtp/browser-sdk';
 
const decryptedAttachments: Attachment[] = [];
 
for (const attachment of multiRemoteAttachment.attachments) {
  // Download the encrypted payload from the URL
  // (Integrator must supply download from URL functionality!)
  const encryptedPayload = await downloadFromUrl(attachment.url);
 
  // Decrypt the attachment
  const decryptedAttachment = await decryptAttachment(
    encryptedPayload,
    attachment
  );
  decryptedAttachments.push(decryptedAttachment);
}
 
// Now you have the original attachments
// decryptedAttachments[0].filename, decryptedAttachments[0].data, etc.

Accessing the unencrypted attachments

Use the decrypted attachments to access the original file data.

Browser
// Example showing how to display decrypted attachments
const attachment1 = decryptedAttachments[0];
const attachment2 = decryptedAttachments[1];
 
// Create object URLs for display (e.g., for images)
const url1 = URL.createObjectURL(
  new Blob([attachment1.data], { type: attachment1.mimeType })
);
const url2 = URL.createObjectURL(
  new Blob([attachment2.data], { type: attachment2.mimeType })
);
 
// Use in img tags
// <img src={url1} alt={attachment1.filename} />
// <img src={url2} alt={attachment2.filename} />

Support attachments smaller than 1MB

Attachments smaller than 1MB can be sent using the Attachment content type.

:::

Import and register

For Browser SDK (v6.0.0+) and Node SDK (v5.0.0+), attachments are built-in and do not require codec registration. Skip this step for these SDKs.

For other SDKs, register the codec:

Kotlin
import org.xmtp.android.library.codecs.Attachment
import org.xmtp.android.library.codecs.AttachmentCodec
import org.xmtp.android.library.codecs.ContentTypeAttachment
 
Client.register(codec = AttachmentCodec())

Create an attachment

Browser
// Load file from browser file input
const file = event.target.files[0]; // from <input type="file">
 
const attachment = {
  filename: file.name,
  mimeType: file.type,
  data: new Uint8Array(await file.arrayBuffer()),
};

Send the attachment

Browser
await conversation.sendAttachment(attachment);

Receive the attachment

Browser
import { contentTypesAreEqual } from '@xmtp/content-type-primitives';
import { contentTypeAttachment } from '@xmtp/browser-sdk';
 
if (contentTypesAreEqual(message.contentType, await contentTypeAttachment())) {
  const attachment = message.content;
 
  // Create a blob URL for display
  const blob = new Blob([attachment.data], {
    type: attachment.mimeType,
  });
  const url = URL.createObjectURL(blob);
 
  // Use the URL in an img tag or download link
  // <img src={url} alt={attachment.filename} />
}