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 in a message.

Install the package

In some SDKs, the AttachmentCodec is already included in the SDK. If not, you can install the package using the following command:

npm i @xmtp/content-type-remote-attachment

Configure the content type

After importing the package, you can register the codec.

import {
  ContentTypeAttachment,
  AttachmentCodec,
  RemoteAttachmentCodec,
  ContentTypeRemoteAttachment,
} from "@xmtp/content-type-remote-attachment";
// Create the XMTP client
const xmtp = await Client.create(signer, { env: "dev" });
xmtp.registerCodec(new AttachmentCodec());
xmtp.registerCodec(new RemoteAttachmentCodec());

Send a remote attachment

Load the file. This example uses a web browser to load the file:

//image is the uploaded event.target.files[0];
const data = await new Promise((resolve, reject) => {
  const reader = new FileReader();
  reader.onload = () => {
    if (reader.result instanceof ArrayBuffer) {
      resolve(reader.result);
    } else {
      reject(new Error("Not an ArrayBuffer"));
    }
  };
  reader.readAsArrayBuffer(image);
});

Create an attachment object:

// Local file details
const attachment = {
  filename: image?.name,
  mimeType: image?.type,
  data: new Uint8Array(data),
};

Use RemoteAttachmentCodec.encodeEncrypted to encrypt an attachment:

const encryptedEncoded = await RemoteAttachmentCodec.encodeEncrypted(
  attachment,
  new AttachmentCodec()
);

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:

const remoteAttachment = {
  url: url,
  contentDigest: encryptedEncoded.digest,
  salt: encryptedEncoded.salt,
  nonce: encryptedEncoded.nonce,
  secret: encryptedEncoded.secret,
  scheme: "https://",
  filename: attachment.filename,
  contentLength: attachment.data.byteLength,
};

Now that you have a remote attachment, you can send it:

await conversation.send(remoteAttachment, {
  contentType: ContentTypeRemoteAttachment,
});

Receive, decode, and decrypt a remote attachment

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

import { ContentTypeRemoteAttachment } from "@xmtp/content-type-remote-attachment";
 
if (message.contentType.sameAs(RemoteAttachmentContentType)) {
  const attachment = await RemoteAttachmentCodec.load(message.content, client);
}

You now have the original attachment:

Bash
attachment.filename // => "screenshot.png"
attachment.mimeType // => "image/png",
attachment.data // => [the PNG data]

Once you've created the attachment object, you can create a preview to show in the message input field before sending:

const objectURL = URL.createObjectURL(
  new Blob([Buffer.from(attachment.data)], {
    type: attachment.mimeType,
  })
);
 
const img = document.createElement("img");
img.src = objectURL;
img.title = attachment.filename;

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

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.

React Native
const attachment1: DecryptedLocalAttachment = {
  fileUri: "content://media/external/images/media/image-1.png",
  mimeType: "image/png",
  filename: "image-1.png"
}
 
const attachment2: DecryptedLocalAttachment = {
  fileUri: "content://media/external/images/media/image-2.png",
  mimeType: "image/png",
  filename: "image-2.png"
}

Encrypt and upload multiple attachments to a remote server

React Native
const remoteAttachments: RemoteAttachmentInfo[] = []
  for (const attachment of [attachment1, attachment2]) {
    // Encrypt the attachment and receive the local URI of the encrypted file
    const { encryptedLocalFileUri, metadata } = await alix.encryptAttachment(attachment)
 
    // Upload the attachment to a remote server and receive the URL
    // (Integrator must supply upload from local uri and return url functionality!)
    const url = uploadAttachmentForUrl(encryptedLocalFileUri)
 
    // Build the remote attachment info
    const remoteAttachmentInfo =
      MultiRemoteAttachmentCodec.buildMultiRemoteAttachmentInfo(url, metadata)
    remoteAttachments.push(remoteAttachmentInfo)
  }

Send a message with multiple remote attachments

React Native
await convo.send({
    multiRemoteAttachment: {
      attachments: remoteAttachments,
    },
  })

Recognize and decode a multi remote attachment

React Native
const messages = await conversation.messages()
if (messages.size > 0 && messages[0].contentTypeId == 'xmtp.org/multiRemoteStaticContent:1.0') {
  // Decode the raw content back into a MultiRemoteAttachment
    const multiRemoteAttachment: MultiRemoteAttachment = messages[0].content()
 
    // See next section for download, and decrypt the attachments
}

Decode, download, and decrypt the attachments

React Native
const decryptedAttachments: DecryptedLocalAttachment[] = []
 
for (const attachment of multiRemoteAttachment.attachments) {
    // Downloading the encrypted payload from the attachment URL and save the local file
    // (Integrator must supply download from url and save to local Uri functionality!)
    const encryptedFileLocalURIAfterDownload: string = downloadFromUrl(
      attachment.url
    )
    // Decrypt the local file
    const decryptedLocalAttachment = await alix.decryptAttachment({
      encryptedLocalFileUri: encryptedFileLocalURIAfterDownload,
      metadata: {
        secret: attachment.secret,
        salt: attachment.salt,
        nonce: attachment.nonce,
        contentDigest: attachment.contentDigest,
        filename: attachment.filename,
      } as RemoteAttachmentContent,
    })
    decryptedAttachments.push(decryptedLocalAttachment)
  }

Accessing the unencrypted attachments

Use the file URIs in the decrypted attachments objects to display the attachments.

React Native
// Example showing displaying attachments if they represent images
const attachment1 = decryptedAttachments[0]
const attachment2 = decryptedAttachments[1]
 
<Image source={{ uri: attachment1.fileUri }} />
<Image source={{ uri: attachment2.fileUri }} />

Support attachments smaller than 1MB

Attachments smaller than 1MB can be sent using the AttachmentCodec. The codec will automatically encrypt the attachment and upload it to the XMTP network.

Install the package

npm i @xmtp/content-type-remote-attachment

In some SDKs, the AttachmentCodec is already included in the SDK. If not, you can install the package using the following command:

Import and register

Browser
import {
  ContentTypeAttachment,
  AttachmentCodec,
} from "@xmtp/content-type-remote-attachment";
// Create the XMTP client
const xmtp = await Client.create(signer, { env: "dev" });
xmtp.registerCodec(new AttachmentCodec());

Load local file

// Read local file and extract its details
const file = fs.readFileSync("xmtp.png");
const filename = path.basename("xmtp.png");
const extname = path.extname("xmtp.png");
console.log(`Filename: ${filename}`);
console.log(`File Type: ${extname}`);

Send encrypted file

// Convert the file to a Uint8Array
const blob = new Blob([file], { type: extname });
let imgArray = new Uint8Array(await blob.arrayBuffer());
 
const attachment = {
  filename: filename,
  mimeType: extname, //image, video or audio
  data: imgArray,
};
 
console.log("Attachment created", attachment);
await conversation.send(attachment, { contentType: ContentTypeAttachment });

Receive encrypted file

if (message.contentType.sameAs(ContentTypeAttachment)) {
  const blobdecoded = new Blob([message.content.data], {
    type: message.content.mimeType,
  });
  const url = URL.createObjectURL(blobdecoded);
}