diff --git a/static/app/components/core/chat/index.tsx b/static/app/components/core/chat/index.tsx
index f9a891bc7201..9d967d6768a0 100644
--- a/static/app/components/core/chat/index.tsx
+++ b/static/app/components/core/chat/index.tsx
@@ -1,3 +1,4 @@
export {UserBubble} from './userBubble';
export {ToolCallIndicator, type ToolCallStatus} from './toolCallIndicator';
export {Spinner} from './spinner';
+export {MessageRow} from './messageRow';
diff --git a/static/app/components/core/chat/messageRow.mdx b/static/app/components/core/chat/messageRow.mdx
new file mode 100644
index 000000000000..29c1b4007857
--- /dev/null
+++ b/static/app/components/core/chat/messageRow.mdx
@@ -0,0 +1,49 @@
+---
+title: MessageRow
+description: The full-width row that positions a single message turn in a conversation.
+category: chat
+source: '@sentry/scraps/chat'
+resources:
+ js: https://github.com/getsentry/sentry/blob/master/static/app/components/core/chat/messageRow.tsx
+---
+
+import {MessageRow, UserBubble} from '@sentry/scraps/chat';
+import {Container} from '@sentry/scraps/layout';
+
+import * as Storybook from 'sentry/stories';
+
+export const documentation = import('!!type-loader!@sentry/scraps/chat/messageRow');
+
+The `MessageRow` lays out a single message turn within a conversation. It is
+presentation only — it owns alignment and the consistent gutter around a turn,
+while the bubble or content is the caller's responsibility.
+
+## Alignment
+
+Use `justify="end"` for the sender's own messages and the default `justify="start"`
+for the agent's responses, so the two sides sit opposite each other.
+
+
+
+
+ Which of my issues are getting worse?
+
+ Here are the three escalating issues I found.
+
+
+
+```jsx
+
+ Which of my issues are getting worse?
+
+Here are the three escalating issues I found.
+```
+
+## Padding
+
+The row applies a consistent gutter (`md xl`) around each turn. Override
+`padding` for surfaces that need a different gutter.
+
+```jsx
+…
+```
diff --git a/static/app/components/core/chat/messageRow.tsx b/static/app/components/core/chat/messageRow.tsx
new file mode 100644
index 000000000000..64ee9cbc852d
--- /dev/null
+++ b/static/app/components/core/chat/messageRow.tsx
@@ -0,0 +1,29 @@
+import {Flex, type FlexProps} from '@sentry/scraps/layout';
+
+interface MessageRowProps extends Omit {
+ children: React.ReactNode;
+ /**
+ * Which side of the conversation the message sits on: `end` for the sender's
+ * own messages, `start` (default) for the agent's responses.
+ */
+ justify?: 'start' | 'end';
+}
+
+/**
+ * The full-width row that positions a single message turn within a conversation.
+ *
+ * Presentation only — it owns alignment and the consistent gutter around a turn;
+ * the bubble or content is the caller's responsibility.
+ */
+export function MessageRow({
+ children,
+ justify = 'start',
+ padding = 'md xl',
+ ...props
+}: MessageRowProps) {
+ return (
+
+ {children}
+
+ );
+}
diff --git a/static/app/views/seerExplorer/components/chat/shared.tsx b/static/app/views/seerExplorer/components/chat/shared.tsx
index a3efe3dcd423..b8e000bcdc33 100644
--- a/static/app/views/seerExplorer/components/chat/shared.tsx
+++ b/static/app/views/seerExplorer/components/chat/shared.tsx
@@ -1,4 +1,4 @@
-import {Spinner, type ToolCallStatus} from '@sentry/scraps/chat';
+import {MessageRow, Spinner, type ToolCallStatus} from '@sentry/scraps/chat';
import {Flex} from '@sentry/scraps/layout';
import {SeerMarkdown} from 'sentry/components/seer/markdown';
@@ -66,7 +66,7 @@ export function hasValidContent(content: string | null | undefined): content is
export function MessagePlaceholder({content}: {content?: string}) {
return (
-
+
{hasValidContent(content) && }
-
+
);
}
diff --git a/static/app/views/seerExplorer/components/chat/user.tsx b/static/app/views/seerExplorer/components/chat/user.tsx
index 2940d8278929..9a3b4eb4238a 100644
--- a/static/app/views/seerExplorer/components/chat/user.tsx
+++ b/static/app/views/seerExplorer/components/chat/user.tsx
@@ -1,12 +1,11 @@
-import {UserBubble} from '@sentry/scraps/chat';
-import {Flex} from '@sentry/scraps/layout';
+import {MessageRow, UserBubble} from '@sentry/scraps/chat';
import type {UserBlockProps} from './shared';
export function UserBlock({block}: UserBlockProps) {
return (
-
+
{block.message.content ?? ''}
-
+
);
}