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.
-
Use the remote attachment content type to send one remote attachment of any size.
-
Use the multiple remote attachments content type to send multiple remote attachments of any size.
-
Use the attachment content type to send attachments smaller than 1MB.
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:
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 bytesUpload 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:
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:
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.
// 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
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
await conversation.sendMultiRemoteAttachment({
attachments: remoteAttachments,
});Recognize and decode a multi remote attachment
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
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.
// 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:
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
// 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
await conversation.sendAttachment(attachment);Receive the attachment
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} />
}
