Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
71b5f60
feat: add icons to default MessageActions
MartinCupela Feb 17, 2026
fd25615
feat: add icons to default MessageActions menus
MartinCupela Feb 17, 2026
bd652c0
fix: correct the condition to decide, whether poll actions should be …
MartinCupela Feb 17, 2026
455e0a5
refactor: remove unnecessary Emoji plugin styling
MartinCupela Feb 17, 2026
35c0314
feat: position the message error icon at the beginning of the top rig…
MartinCupela Feb 17, 2026
8b12157
feat: track aria-selected in ContextMenuButton
MartinCupela Feb 17, 2026
5ab5218
style: fix linter issues
MartinCupela Feb 17, 2026
74a45a7
feat: remove the resend-on-message-click behavior
MartinCupela Feb 17, 2026
25150c2
feat: integrate DialogAnchor into ContextMenu to keep stable position…
MartinCupela Feb 17, 2026
cae69fe
feat: add scss for MessageList and VirtualizedMessageList
MartinCupela Feb 18, 2026
2fa6d8f
feat: align MessageComposer with message list width
MartinCupela Feb 18, 2026
362e6f1
feat: control Avatar display and styles application via showAvatar prop
MartinCupela Feb 18, 2026
4d715a9
chore: remove reference to Emojis styling from start:css command
MartinCupela Feb 18, 2026
d1c4d6b
fix: apply max-width to str-chat__virtual-list
MartinCupela Feb 18, 2026
09da640
feat: add space 4px between messages in a group and 16px between groups
MartinCupela Feb 18, 2026
ada736c
Merge branch 'master' into feat/message-list-styling
MartinCupela Feb 18, 2026
34fee6a
feat: reconcile pin-indicator with message-reminder grid areas in a m…
MartinCupela Feb 18, 2026
1b6435c
feat: remove padding block from str-chat__message
MartinCupela Feb 18, 2026
533cf28
feat: remove props endOfGroup, firstOfGroup and groupedByUser from Me…
MartinCupela Feb 19, 2026
42fe83f
fix: remove padding from str-chat__virtual-list-message-wrapper
MartinCupela Feb 19, 2026
4d6be34
Merge branch 'master' into feat/message-list-styling
MartinCupela Feb 19, 2026
56bbbb2
fix: remove the rest of references to endOfGroup, startOfGroup props
MartinCupela Feb 19, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions examples/vite/src/stream-imports-layout.scss
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@
//@use 'stream-chat-react/dist/scss/v2/MessageActionsBox/MessageActionsBox-layout';
//@use 'stream-chat-react/dist/scss/v2/MessageBouncePrompt/MessageBouncePrompt-layout';
//@use 'stream-chat-react/dist/scss/v2/MessageInput/MessageInput-layout'; // X
@use 'stream-chat-react/dist/scss/v2/MessageList/MessageList-layout';
@use 'stream-chat-react/dist/scss/v2/MessageList/VirtualizedMessageList-layout';
//@use 'stream-chat-react/dist/scss/v2/MessageList/MessageList-layout';
//@use 'stream-chat-react/dist/scss/v2/MessageList/VirtualizedMessageList-layout';
// @use 'stream-chat-react/dist/scss/v2/MessageReactions/MessageReactions-layout';
@use 'stream-chat-react/dist/scss/v2/MessageReactions/MessageReactionsSelector-layout';
//@use 'stream-chat-react/dist/scss/v2/Modal/Modal-layout';
Expand Down
4 changes: 2 additions & 2 deletions examples/vite/src/stream-imports-theme.scss
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@
//@use 'stream-chat-react/dist/scss/v2/Message/Message-theme';
//@use 'stream-chat-react/dist/scss/v2/MessageActionsBox/MessageActionsBox-theme';
//@use 'stream-chat-react/dist/scss/v2/MessageBouncePrompt/MessageBouncePrompt-theme';
@use 'stream-chat-react/dist/scss/v2/MessageList/MessageList-theme';
@use 'stream-chat-react/dist/scss/v2/MessageList/VirtualizedMessageList-theme';
//@use 'stream-chat-react/dist/scss/v2/MessageList/MessageList-theme';
//@use 'stream-chat-react/dist/scss/v2/MessageList/VirtualizedMessageList-theme';
// @use 'stream-chat-react/dist/scss/v2/MessageReactions/MessageReactions-theme';
@use 'stream-chat-react/dist/scss/v2/MessageReactions/MessageReactionsSelector-theme';
//@use 'stream-chat-react/dist/scss/v2/Modal/Modal-theme';
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@
"prettier-fix": "yarn prettier --write",
"fix-staged": "lint-staged --config .lintstagedrc.fix.json --concurrent 1",
"start": "tsc -p tsconfig.lib.json -w",
"start:css": "sass --watch src/styling:dist/css src/plugins/Emojis/styling:dist/css",
"start:css": "sass --watch src/styling:dist/css",
"prepare": "husky install",
"preversion": "yarn install",
"test": "jest",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
display: flex;
align-items: center;
justify-content: center;
max-width: 768px;
max-width: var(--str-chat__message-composer-max-width);
width: 100%;
border-radius: var(--radius-3xl);
border: 1px solid var(--border-core-default);
Expand Down
7 changes: 1 addition & 6 deletions src/components/Message/Message.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@ type MessageWithContextProps = Omit<MessageProps, MessagePropsToOmit> &
const MessageWithContext = (props: MessageWithContextProps) => {
const {
canPin,
groupedByUser,
Message: propMessage,
message,
messageActions = Object.keys(MESSAGE_ACTIONS),
Expand Down Expand Up @@ -166,8 +165,7 @@ const MessageWithContext = (props: MessageWithContextProps) => {

return (
<MessageProvider value={messageContextValue}>
<MessageUIComponent groupedByUser={groupedByUser} />
{/* TODO - remove prop in next major release, maintains VML backwards compatibility */}
<MessageUIComponent />
</MessageProvider>
);
};
Expand Down Expand Up @@ -263,10 +261,7 @@ export const Message = (props: MessageProps) => {
closeReactionSelectorOnClick={closeReactionSelectorOnClick}
deliveredTo={props.deliveredTo}
disableQuotedMessages={props.disableQuotedMessages}
endOfGroup={props.endOfGroup}
firstOfGroup={props.firstOfGroup}
formatDate={props.formatDate}
groupedByUser={props.groupedByUser}
groupStyles={props.groupStyles}
handleAction={handleAction}
handleDelete={handleDelete}
Expand Down
7 changes: 7 additions & 0 deletions src/components/Message/MessageSimple.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ const MessageSimpleWithContext = (props: MessageSimpleWithContextProps) => {
onUserClick,
onUserHover,
renderText,
showAvatar = 'incoming',
threadList,
} = props;
const { client } = useChatContext('MessageSimple');
Expand Down Expand Up @@ -152,6 +153,12 @@ const MessageSimpleWithContext = (props: MessageSimpleWithContextProps) => {
'str-chat__message--highlighted': highlighted,
'str-chat__message--is-emoji-only': textHasEmojisOnly,
'str-chat__message--pinned': message.pinned,
'str-chat__message--with-avatar': (() => {
if (!message.user) return false;
if (showAvatar === 'incoming') return !isMyMessage();
if (showAvatar === 'outgoing') return isMyMessage();
return showAvatar;
})(),
'str-chat__message--with-reactions': hasReactions,
'str-chat__message-send-can-be-retried':
message?.status === 'failed' && message?.error?.status !== 403,
Expand Down
129 changes: 77 additions & 52 deletions src/components/Message/styling/Message.scss
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,28 @@
--str-chat__message-with-attachment-max-width: calc(var(--str-chat__spacing-px) * 300);
}

/* Make spaces between message groups */
.str-chat__li--top {
padding-block-start: var(--spacing-xs);
padding-block-end: var(--spacing-xxxs);
}

.str-chat__li--bottom {
padding-block-start: var(--spacing-xxxs);
padding-block-end: var(--spacing-xs);
}

.str-chat__li--middle {
padding-block: var(--spacing-xxxs);
}

.str-chat__li--single {
padding-block: var(--spacing-xs);
}

.str-chat__message {
--str-chat-message-options-size: calc(3 * var(--str-chat__message-options-button-size));
padding-inline: var(--str-chat__message-composer-padding);

&.str-chat__message-without-touch-support {
--str-chat-message-options-size: calc(
Expand Down Expand Up @@ -214,12 +234,45 @@

.str-chat__message-reminder {
grid-area: message-reminder;
padding-block: var(--str-chat__spacing-2) var(--str-chat__spacing-0_5);
padding-block: var(--spacing-xxs);
margin: 0;
@include utils.component-layer-overrides('message-reminder');
font: var(--str-chat__caption-medium-text);
}

@mixin message-grid-no-avatar {
grid-template-areas:
'pin-indicator'
'message-reminder'
'message'
'translation-notice'
'custom-metadata'
'metadata';
grid-template-columns: 1fr;
}

@mixin message-grid-other-with-avatar {
grid-template-areas:
'. pin-indicator'
'. message-reminder'
'avatar message'
'avatar translation-notice'
'avatar custom-metadata'
'avatar metadata';
grid-template-columns: auto 1fr;
}

@mixin message-grid-me-with-avatar {
grid-template-areas:
'pin-indicator .'
'message-reminder .'
'message avatar'
'translation-notice avatar'
'custom-metadata avatar'
'metadata avatar';
grid-template-columns: 1fr auto;
}

.str-chat__message-pin-indicator {
grid-area: pin-indicator;
padding-block: var(--spacing-xxs);
Expand All @@ -238,16 +291,17 @@
}

&.str-chat__message--other {
grid-template-areas:
'. message-reminder'
'avatar message'
'avatar translation-notice'
'avatar custom-metadata'
'avatar metadata';
column-gap: var(--str-chat__spacing-2);
grid-template-columns: auto 1fr;
justify-items: flex-start;

&.str-chat__message--with-avatar {
@include message-grid-other-with-avatar;
}

&:not(.str-chat__message--with-avatar) {
@include message-grid-no-avatar;
}

.str-chat__message-inner {
.str-chat__message-reactions-host {
justify-self: flex-start;
Expand All @@ -268,36 +322,25 @@
}

&.str-chat__message--pinned.str-chat__message--other {
grid-template-areas:
'. pin-indicator'
'. message-reminder'
'avatar message'
'avatar translation-notice'
'avatar custom-metadata'
'avatar metadata';
@include message-grid-other-with-avatar;
}

&.str-chat__message--pinned.str-chat__message--me {
grid-template-areas:
'pin-indicator .'
'message-reminder .'
'message avatar'
'translation-notice avatar'
'custom-metadata avatar'
'metadata avatar';
@include message-grid-me-with-avatar;
}

&.str-chat__message--me {
grid-template-areas:
'message-reminder .'
'message avatar'
'translation-notice avatar'
'custom-metadata avatar'
'metadata avatar';
column-gap: var(--str-chat__spacing-2);
grid-template-columns: 1fr auto;
justify-items: flex-end;

&.str-chat__message--with-avatar {
@include message-grid-me-with-avatar;
}

&:not(.str-chat__message--with-avatar) {
@include message-grid-no-avatar;
}

.str-chat__message-inner {
.str-chat__message-reactions-host {
justify-self: flex-end;
Expand All @@ -315,6 +358,7 @@
}
}
}

}

&.str-chat__message--deleted {
Expand All @@ -335,7 +379,7 @@
align-self: end;
}

&.str-chat__message--me .str-chat__avatar:has(~ .str-chat__message-inner) {
&:not(.str-chat__message--with-avatar) .str-chat__avatar {
display: none;
}

Expand Down Expand Up @@ -660,36 +704,17 @@
.str-chat__li--middle,
.str-chat__li--top {
.str-chat__message {
// space below the message within a group
padding-block-end: var(--spacing-xxs);

.str-chat__message-metadata {
display: none;
}

// > selector added to not hide sender of inline replies
> .str-chat__message-sender-avatar {
// Hide sender avatar in middle/top of group (only show on last message in group)
> .str-chat__avatar {
visibility: hidden;
}
}
}

.str-chat__li--top,
.str-chat__li--single {
.str-chat__message {
// space above the message group
padding-block-start: var(--spacing-xs);
}
}

.str-chat__li--bottom,
.str-chat__li--single {
.str-chat__message {
// space below the message group
padding-block-end: var(--spacing-xs);
}
}

.str-chat__li--single,
.str-chat__li--bottom {
.str-chat__message--other {
Expand Down Expand Up @@ -729,7 +754,7 @@
// Avatar display in message list
.str-chat__li--top,
.str-chat__li--middle {
.str-chat__message > .str-chat__avatar {
.str-chat__message.str-chat__message--with-avatar > .str-chat__avatar {
visibility: hidden;
pointer-events: none;
}
Expand Down
14 changes: 8 additions & 6 deletions src/components/Message/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,6 @@ export type MessageProps = {
deliveredTo?: UserResponse[];
/** If true, disables the ability for users to quote messages, defaults to false */
disableQuotedMessages?: boolean;
/** When true, the message is the last one in a group sent by a specific user (only used in the `VirtualizedMessageList`) */
endOfGroup?: boolean;
/** When true, the message is the first one in a group sent by a specific user (only used in the `VirtualizedMessageList`) */
firstOfGroup?: boolean;
/** Override the default formatting of the date. This is a function that has access to the original date object, returns a string */
formatDate?: (date: Date) => string;
/** Function that returns the notification text to be displayed when a delete message request fails */
Expand All @@ -50,8 +46,6 @@ export type MessageProps = {
getMuteUserSuccessNotification?: (user: UserResponse) => string;
/** Function that returns the notification text to be displayed when a pin message request fails */
getPinMessageErrorNotification?: (message: LocalMessage) => string;
/** If true, group messages sent by each user (only used in the `VirtualizedMessageList`) */
groupedByUser?: boolean;
/** A list of styles to apply to this message, i.e. top, bottom, single */
groupStyles?: GroupStyle[];
/** Whether to highlight and focus the message on load */
Expand Down Expand Up @@ -88,6 +82,14 @@ export type MessageProps = {
reactionDetailsSort?: ReactionSort;
/** A list of users that have read this Message if the message is the last one and was posted by my user */
readBy?: UserResponse[];
/**
* When set, the message uses the grid layout with an avatar column and shows the sender avatar.
* - `true`: show for incoming and outgoing messages
* - `'incoming'`: show only for incoming (other users') messages
* - `'outgoing'`: show only for own (outgoing) messages
* - `false` or omitted: no avatar column
*/
showAvatar?: boolean | 'incoming' | 'outgoing';
/** Custom function to render message text content, defaults to the renderText function: [utils](https://github.com/GetStream/stream-chat-react/blob/master/src/utils.ts) */
renderText?: (
text?: string,
Expand Down
1 change: 0 additions & 1 deletion src/components/Message/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,6 @@ export const areMessagePropsEqual = (
const { message: nextMessage, Message: nextMessageUI } = nextProps;

if (prevMessageUI !== nextMessageUI) return false;
if (prevProps.endOfGroup !== nextProps.endOfGroup) return false;

if (nextProps.showDetailedReactions !== prevProps.showDetailedReactions) {
return false;
Expand Down
3 changes: 2 additions & 1 deletion src/components/MessageInput/styling/MessageComposer.scss
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
--str-chat__cooldown-border-inline-end: 0;
--str-chat__cooldown-box-shadow: none;
--str-chat__message-composer-max-width: 768px;
--str-chat__message-composer-padding: var(--spacing-md);

.str-chat__message-composer-container {
width: 100%;
Expand All @@ -44,7 +45,7 @@
align-items: center;
gap: var(--spacing-xs);
justify-content: center;
padding: var(--spacing-xs);
padding: var(--str-chat__message-composer-padding);
min-width: 0;
}

Expand Down
4 changes: 3 additions & 1 deletion src/components/MessageList/MessageList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ const MessageListWithContext = (props: MessageListWithContextProps) => {
reactionDetailsSort,
renderText: props.renderText,
retrySendMessage: props.retrySendMessage,
showAvatar: props.showAvatar,
sortReactionDetails,
sortReactions,
unsafeHTML,
Expand All @@ -188,7 +189,7 @@ const MessageListWithContext = (props: MessageListWithContextProps) => {
threadList,
});

const messageListClass = customClasses?.messageList || 'str-chat__list';
const messageListClass = customClasses?.messageList || 'str-chat__message-list';

const loadMore = React.useCallback(() => {
if (loadMoreCallback) {
Expand Down Expand Up @@ -319,6 +320,7 @@ type PropsDrilledToMessage =
| 'reactionDetailsSort'
| 'renderText'
| 'retrySendMessage'
| 'showAvatar'
| 'sortReactions'
| 'sortReactionDetails'
| 'unsafeHTML';
Expand Down
Loading
Loading