     /// Note: The default value of this property is 2x the `messageLabelFont`.
     internal var emojiLabelFont: UIFont
 
+
+    // MARK: - Layout
+
+    open var incomingAvatarSize = CGSize(width: 30, height: 30) {
+        didSet { removeAllCachedAttributes() }
+    }
+
+    open var outgoingAvatarSize = CGSize(width: 30, height: 30) {
+        didSet { removeAllCachedAttributes() }
+    }
+
+    open var incomingAvatarPosition = AvatarPosition(vertical: .messageBottom) {
+        didSet { removeAllCachedAttributes() }
+    }
+
+    open var outgoingAvatarPosition = AvatarPosition(vertical: .messageBottom) {
+        didSet { removeAllCachedAttributes() }
+    }
+
+    open var incomingMessageLabelInsets = UIEdgeInsets(top: 7, left: 18, bottom: 7, right: 14) {
+        didSet { removeAllCachedAttributes() }
+    }
+
+    open var outgoingMessageLabelInsets = UIEdgeInsets(top: 7, left: 14, bottom: 7, right: 18) {
+        didSet { removeAllCachedAttributes() }
+    }
+
+    open var incomingMessagePadding = UIEdgeInsets(top: 0, left: 4, bottom: 0, right: 30) {
+        didSet { removeAllCachedAttributes() }
+    }
+
+    open var outgoingMessagePadding = UIEdgeInsets(top: 0, left: 30, bottom: 0, right: 4) {
+        didSet { removeAllCachedAttributes() }
+    }
+
+    open var incomingCellTopLabelAlignment = LabelAlignment.messageLeading(.zero) {
+        didSet { removeAllCachedAttributes() }
+    }
+
+    open var outgoingCellTopLabelAlignment = LabelAlignment.messageTrailing(.zero) {
+        didSet { removeAllCachedAttributes() }
+    }
+
+    open var incomingCellBottomLabelAlignment = LabelAlignment.messageTrailing(.zero) {
+        didSet { removeAllCachedAttributes() }
+    }
+
+    open var outgoingCellBottomLabelAlignment = LabelAlignment.messageLeading(.zero) {
+        didSet { removeAllCachedAttributes() }
+    }
+
+    internal var attributedTextSizeCache = NSCache<NSNumber, NSValue>()
+
     /// Determines the maximum number of `MessageCollectionViewCell` attributes to cache.
     ///
     /// Note: The default value of this property is 500.
     open var attributesCacheMaxSize: Int = 500 {
         didSet {
-            layoutContextCache.countLimit = attributesCacheMaxSize
-        }
-    }
-
-    // MARK: - Layout
-
-    open var incomingAvatarSize = CGSize(width: 30, height: 30)
-
-    open var outgoingAvatarSize = CGSize(width: 30, height: 30)
-
-    open var incomingAvatarPosition = AvatarPosition(vertical: .messageBottom)
-
-    open var outgoingAvatarPosition = AvatarPosition(vertical: .messageBottom)
-
-    open var incomingMessageLabelInsets = UIEdgeInsets(top: 7, left: 18, bottom: 7, right: 14)
-
-    open var outgoingMessageLabelInsets = UIEdgeInsets(top: 7, left: 14, bottom: 7, right: 18)
-
-    open var incomingMessagePadding = UIEdgeInsets(top: 0, left: 4, bottom: 0, right: 30)
-
-    open var outgoingMessagePadding = UIEdgeInsets(top: 0, left: 30, bottom: 0, right: 4)
-
-    open var incomingCellTopLabelAlignment = LabelAlignment.messageLeading(.zero)
-
-    open var outgoingCellTopLabelAlignment = LabelAlignment.messageTrailing(.zero)
-
-    open var incomingCellBottomLabelAlignment = LabelAlignment.messageTrailing(.zero)
-
-    open var outgoingCellBottomLabelAlignment = LabelAlignment.messageLeading(.zero)
-
-    typealias MessageID = NSString
-    
-    /// The cache for `MessageCellLayoutContext`.
-    /// The key is the `messageId` of the `MessageType`.
-    fileprivate var layoutContextCache = NSCache<MessageID, MessageCellLayoutContext>()
+            attributedTextSizeCache.countLimit = attributesCacheMaxSize
+        }
+    }
 
     /// The `MessageCellLayoutContext` for the current cell.
     internal var currentLayoutContext: MessageCellLayoutContext!


        let indexPath = attributes.indexPath
        let message = messagesDataSource.messageForItem(at: indexPath, in: messagesCollectionView)
        let context = cellLayoutContext(for: message, at: indexPath)

        attributes.avatarSize = context.avatarSize!
        attributes.avatarPosition = context.avatarPosition!

        attributes.messageContainerPadding = context.messageContainerPadding!
        attributes.messageContainerSize = context.messageContainerSize!
        attributes.messageLabelInsets = context.messageLabelInsets!

        attributes.topLabelAlignment = context.topLabelAlignment!
        attributes.topLabelSize = context.topLabelSize!

        attributes.bottomLabelAlignment = context.bottomLabelAlignment!
        attributes.bottomLabelSize = context.bottomLabelSize!

        switch message.data {
        case .emoji:

        switch message.data {
        case .text(let text):
            let messageInsets = messageLabelInsets(for: message, at: indexPath)
            let attributedText = NSAttributedString(string: text, attributes: [.font: messageLabelFont])
            messageContainerSize = labelSize(for: attributedText, considering: maxWidth)
            messageContainerSize.width += messageInsets.horizontal
            messageContainerSize.height += messageInsets.vertical
        case .attributedText(let text):
