Skip to content

⌚ feat: Show Message Timestamps on Hover#13709

Merged
danny-avila merged 3 commits into
devfrom
feature/5199-message-timestamps
Jun 14, 2026
Merged

⌚ feat: Show Message Timestamps on Hover#13709
danny-avila merged 3 commits into
devfrom
feature/5199-message-timestamps

Conversation

@berry-13

Copy link
Copy Markdown
Collaborator

Summary

Closes #5199

There's currently no way to tell when a message was sent or received. This adds a timestamp to every message, both user prompts and assistant responses, shown inline next to the author's name.

Behavior:

  • The timestamp is hidden by default and reveals on hover of that message (and on keyboard focus-within), consistent with how the existing message action buttons appear. On touch devices, where there's no hover, it stays visible.
  • The format adapts to recency: messages under 24h show a relative time (e.g. "10 minutes ago"); older messages show the absolute date (e.g. "May 5, 2026, 5:42 PM").
  • When the relative form is shown, hovering it exposes the exact date via the <time> element's native title, the same pattern GitHub uses.

Implementation notes:

  • New shared MessageTimestamp component renders a semantic <time dateTime=…> element, used by both MessageRender (user turns) and ContentRender (assistant/content turns).
  • A new getMessageTimestamp util formats the relative/absolute strings using the native Intl.RelativeTimeFormat / Intl.DateTimeFormat APIs, locale-aware from the active i18n language, with no new translation keys and no added dependencies.
  • createdAt was added to both components' React.memo comparators. These comparators whitelist the fields that trigger a re-render; without createdAt listed, a message that receives its timestamp after the first render would never display it.

Accessibility: the <time> element stays in the DOM (only its opacity is toggled), so screen readers always announce the timestamp in the message header even though it's only visually shown on hover.

No data-model or backend changes as message.createdAt already exists.

Change Type

  • New feature (non-breaking change which adds functionality)

Testing

  • Unit tests (client/src/utils/__tests__/messages.test.ts): cover relative/absolute formatting, the "now" case, the 24h recency threshold (isRecent), invalid/missing input, and malformed-locale fallback.
  • Manual: opened a multi-turn conversation and confirmed every message, user and assistant, shows its timestamp on hover, hidden at rest, with the absolute date for older messages. Verified in light and dark mode.

Test Configuration:

  • Node 24 and Chrome (desktop viewport for hover; the inline timestamp stays visible at < md widths)

Checklist

  • My code adheres to this project's style guidelines
  • I have performed a self-review of my own code
  • I have commented in any complex areas of my code
  • My changes do not introduce new warnings
  • I have written tests demonstrating that my changes are effective or that my feature works
  • Local unit tests pass with my changes

Reveal a message's time inline next to the author name on hover. Recent messages (under 24h) show a relative time ("10 minutes ago") with the absolute date on hover; older messages show the absolute date directly.

A shared MessageTimestamp component is used by both MessageRender and ContentRender, with createdAt added to their memo comparators so the timestamp appears once it's available.

Resolves #5199
Copilot AI review requested due to automatic review settings June 12, 2026 12:16

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds hover-revealed per-message timestamps in the chat UI, using a shared <time>-based component and a new locale-aware formatter utility, to address the lack of sent/received time visibility (Issue #5199).

Changes:

  • Introduces getMessageTimestamp() (relative/absolute/ISO + “recent” threshold) and isValidTimestamp() utilities.
  • Adds a shared MessageTimestamp UI component and renders it in both user and assistant message headers.
  • Updates React.memo comparators to include createdAt so timestamps appear when populated after initial render; adds unit tests for the new formatter.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
client/src/utils/messages.ts Adds timestamp formatting + validation utilities and types.
client/src/utils/tests/messages.test.ts Adds unit tests covering formatting behavior and edge cases.
client/src/components/Messages/ContentRender.tsx Renders timestamp for assistant/content turns; updates memo comparator.
client/src/components/Chat/Messages/ui/MessageRender.tsx Renders timestamp for user turns; updates memo comparator.
client/src/components/Chat/Messages/ui/MessageTimestamp.tsx New shared <time> component with hover/focus reveal behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

<time
dateTime={timestamp.iso}
title={timestamp.isRecent ? timestamp.absolute : undefined}
className="ml-2 text-xs font-normal text-text-secondary transition-opacity duration-200 md:opacity-0 md:group-focus-within:opacity-100 md:group-hover:opacity-100"

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, addressed in 61fc312. swapped the md: breakpoint for a [@media(hover:hover)]:opacity-0 arbitrary variant, so the timestamp only hides-until-hover where a pointer can actually hover and stays visible on touch/non-hover devices (tablets included). The reveal still uses group-hover/group-focus-within, and the element remains in the accessibility tree regardless

Use a (hover: hover) media query instead of the md: breakpoint so the timestamp stays visible on touch and other non-hover devices (e.g. tablets wider than md), while still revealing on hover/focus where a pointer supports it.
@danny-avila

Copy link
Copy Markdown
Owner

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 61fc312320

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

<h2 className={cn('select-none font-semibold', fontSize)}>
<span className="sr-only">{getHeaderPrefixForScreenReader(msg, localize)}</span>
{messageLabel}
<MessageTimestamp value={msg.createdAt ?? msg.clientTimestamp} />

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Add timestamps to the Assistants content renderer

This wires the timestamp into the normal content renderer, but Assistants endpoint messages with content are routed by MultiMessage to client/src/components/Chat/Messages/MessageParts.tsx, which has its own header and was not updated. In Assistants chats, responses are initialized with content, so those messages still never render MessageTimestamp even though the feature is intended to cover every prompt/response.

Useful? React with 👍 / 👎.

Comment on lines +14 to +16
const timestamp = useMemo(
() => getMessageTimestamp(value, i18n.language),
[value, i18n.language],

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Refresh relative timestamp labels over time

getMessageTimestamp reads Date.now(), but this memo only recomputes when the message timestamp or language changes. In an open chat, recent labels such as now or 2 minutes ago freeze at the value from the first render and do not advance until the component is remounted or the timestamp prop changes, so hover timestamps become stale for the common case of leaving a conversation open.

Useful? React with 👍 / 👎.

<h2 className={cn('select-none font-semibold', fontSize)}>
<span className="sr-only">{getHeaderPrefixForScreenReader(msg, localize)}</span>
{messageLabel}
<MessageTimestamp value={msg.createdAt ?? msg.clientTimestamp} />

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Show timestamps for parallel response rows

For multi-response/parallel messages, useContentMetadata sets hasParallelContent, so this header branch is skipped and the newly added timestamp is never rendered; the parallel columns use SiblingHeader for agent names but do not include a message-level time. In conversations using the added/parallel response flow, those response messages still have no visible timestamp despite this feature adding one for the normal message path.

Useful? React with 👍 / 👎.

<h2 className={cn('select-none font-semibold', fontSize)}>
<span className="sr-only">{getHeaderPrefixForScreenReader(msg, localize)}</span>
{messageLabel}
<MessageTimestamp value={msg.createdAt ?? msg.clientTimestamp} />

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Include timestamps in shared conversations

This adds the timestamp only to the main chat renderers, but shared links render the same saved messages through client/src/components/Share/Message.tsx, which has a separate author header and never uses MessageTimestamp. Anyone viewing a shared conversation therefore still has no way to see when those prompts or responses were sent, even though the underlying messages have createdAt.

Useful? React with 👍 / 👎.

Extend the hover timestamp to the Assistants (MessageParts), shared-link, search, and parallel/multi-response renderers so every prompt and response shows when it was sent. The parent message's createdAt is threaded down to parallel column headers (SiblingHeader).

Add a shared, ref-counted minute ticker (useTimeTick) so relative labels like "2 minutes ago" stay current while a conversation is left open instead of freezing at first render.
@danny-avila

Copy link
Copy Markdown
Owner

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. You're on a roll.

Reviewed commit: f39b8b2681

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@danny-avila danny-avila changed the title ✨ feat: Show Message Timestamps on Hover ⌚ feat: Show Message Timestamps on Hover Jun 14, 2026
@danny-avila danny-avila merged commit fc2ae89 into dev Jun 14, 2026
19 checks passed
@danny-avila danny-avila deleted the feature/5199-message-timestamps branch June 14, 2026 13:39
fuuuzzy pushed a commit to fuuuzzy/LibreChat that referenced this pull request Jun 18, 2026
* ✨ feat: Show Message Timestamps on Hover

Reveal a message's time inline next to the author name on hover. Recent messages (under 24h) show a relative time ("10 minutes ago") with the absolute date on hover; older messages show the absolute date directly.

A shared MessageTimestamp component is used by both MessageRender and ContentRender, with createdAt added to their memo comparators so the timestamp appears once it's available.

Resolves danny-avila#5199

* 🎨 fix: Gate message timestamp reveal on hover capability, not width

Use a (hover: hover) media query instead of the md: breakpoint so the timestamp stays visible on touch and other non-hover devices (e.g. tablets wider than md), while still revealing on hover/focus where a pointer supports it.

* 🎨 fix: Show message timestamps across all renderers and keep them live

Extend the hover timestamp to the Assistants (MessageParts), shared-link, search, and parallel/multi-response renderers so every prompt and response shows when it was sent. The parent message's createdAt is threaded down to parallel column headers (SiblingHeader).

Add a shared, ref-counted minute ticker (useTimeTick) so relative labels like "2 minutes ago" stay current while a conversation is left open instead of freezing at first render.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants