diff --git a/client/src/components/Chat/Messages/Content/WebSearch.tsx b/client/src/components/Chat/Messages/Content/WebSearch.tsx
index 9ba2647bed..a2f2be3082 100644
--- a/client/src/components/Chat/Messages/Content/WebSearch.tsx
+++ b/client/src/components/Chat/Messages/Content/WebSearch.tsx
@@ -7,6 +7,7 @@ import type {
ValidSource,
SearchResultData,
PartMetadata,
+ AnswerBoxResult,
} from 'librechat-data-provider';
import { disclosureChevronClassName, toolPanelSpacingClassName } from './disclosure';
import { FaviconImage, getCleanDomain } from '~/components/Web/SourceHovercard';
@@ -175,6 +176,22 @@ export default function WebSearch({
return [];
}, [searchResults, attachments, ownTurn]);
+ /** Direct-answer card shown at the top of the source list. Attachment-only:
+ * answer boxes are provider extras (Serper) that never carry citations, so
+ * the streaming path does not need them. */
+ const answerBox = useMemo((): AnswerBoxResult | undefined => {
+ if (!attachments) {
+ return undefined;
+ }
+ for (const att of attachments) {
+ const answer = att.type === Tools.web_search ? att[Tools.web_search]?.answerBox : undefined;
+ if (answer) {
+ return answer;
+ }
+ }
+ return undefined;
+ }, [attachments]);
+
// Show favicons from the raw SERP results immediately rather than waiting for
// each source to flip to `processed`; the agents scrape barrier would otherwise
// freeze the stack on "Searching the web" for the slowest scrape's duration.
@@ -284,8 +301,23 @@ export default function WebSearch({
'mt-1.5 max-h-[280px] overflow-y-auto rounded-lg border border-border-light',
)}
>
+ {answerBox && (answerBox.title || answerBox.snippet) && (
+
+ {answerBox.title && (
+
+ {answerBox.title}
+
+ )}
+ {answerBox.snippet && (
+
+ {answerBox.snippet}
+
+ )}
+
+ )}
{allSources.map((source, i) => {
const domain = getCleanDomain(source.link);
+ const snippet = 'snippet' in source ? source.snippet : undefined;
return (
0 && 'border-t border-border-light',
+ 'flex gap-2.5 px-3 py-2 transition-colors hover:bg-surface-hover',
+ snippet ? 'items-start' : 'items-center',
+ (i > 0 || !!answerBox) && 'border-t border-border-light',
)}
>
-
-
- {source.title || domain}
+
+
+
+ {source.title || domain}
+
+ {snippet && (
+
+ {snippet}
+
+ )}
+
+
+ {domain}
+ {source.date && (
+
+ {source.date}
+
+ )}
- {domain}
);
})}
diff --git a/client/src/components/Chat/Messages/Content/__tests__/WebSearch.test.tsx b/client/src/components/Chat/Messages/Content/__tests__/WebSearch.test.tsx
index 59692eed4e..b4907d0e0e 100644
--- a/client/src/components/Chat/Messages/Content/__tests__/WebSearch.test.tsx
+++ b/client/src/components/Chat/Messages/Content/__tests__/WebSearch.test.tsx
@@ -291,6 +291,39 @@ describe('WebSearch', () => {
expect(matches.length).toBeGreaterThanOrEqual(1);
});
+ it('renders snippets, dates, and the answer box in the source list', () => {
+ const attachment = {
+ type: Tools.web_search,
+ [Tools.web_search]: {
+ turn: 0,
+ organic: [
+ {
+ link: 'https://example.com/context',
+ title: 'Context windows explained',
+ snippet: 'How context windows change what assistants can do.',
+ date: 'Jun 12, 2026',
+ },
+ ],
+ answerBox: {
+ title: 'What is a context window?',
+ snippet: 'The amount of text a model can consider at once.',
+ },
+ },
+ } as unknown as TAttachment;
+
+ renderWebSearch({ attachments: [attachment] });
+
+ expect(screen.getByText('What is a context window?')).toBeInTheDocument();
+ expect(
+ screen.getByText('The amount of text a model can consider at once.'),
+ ).toBeInTheDocument();
+ expect(
+ screen.getByText('How context windows change what assistants can do.'),
+ ).toBeInTheDocument();
+ expect(screen.getByText('Jun 12, 2026')).toBeInTheDocument();
+ expect(screen.getByText('example.com')).toBeInTheDocument();
+ });
+
it('uses standard tool-row spacing and reveals its chevron on hover or focus', () => {
const searchResults = makeSearchResults({
0: { organic: [makeSource('https://example.com', 'Example')] },