Skip to content

feat(ui): add copy button to multi-line code blocks#38261

Open
DevendraNathLimbu wants to merge 3 commits into
RocketChat:developfrom
DevendraNathLimbu:feature/codeblock-copy-button
Open

feat(ui): add copy button to multi-line code blocks#38261
DevendraNathLimbu wants to merge 3 commits into
RocketChat:developfrom
DevendraNathLimbu:feature/codeblock-copy-button

Conversation

@DevendraNathLimbu
Copy link
Copy Markdown

@DevendraNathLimbu DevendraNathLimbu commented Jan 19, 2026

Summary

Adds a copy button to Multi-line code blocks in messages, allowing users to copy code with a single click.

Details

  • Applies only to fenced (``` multiline) code blocks
  • Does not affect inline code
  • Uses existing gazzodown CodeBlock rendering

Motivation

Improves developer experience during technical discussions and code reviews.

Screenshots

Before:
Screenshot 2026-01-19 214733

After:
Screenshot 2026-01-19 214816

Related issue

Closes #38260

Summary by CodeRabbit

Release Notes

New Features

  • Added copy-to-clipboard button to code blocks with visual feedback that toggles between "Copy" and "Copied" states for enhanced user experience.

✏️ Tip: You can customize this high-level summary in your review settings.

@dionisio-bot
Copy link
Copy Markdown
Contributor

dionisio-bot Bot commented Jan 19, 2026

Looks like this PR is not ready to merge, because of the following issues:

  • This PR is missing the 'stat: QA assured' label
  • This PR is missing the required milestone or project

Please fix the issues and try again

If you have any trouble, please check the PR guidelines

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Jan 19, 2026

⚠️ No Changeset found

Latest commit: 6a13b95

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@CLAassistant
Copy link
Copy Markdown

CLAassistant commented Jan 19, 2026

CLA assistant check
All committers have signed the CLA.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jan 19, 2026

Walkthrough

A copy button is added to the CodeBlock component with clipboard functionality. The button appears in an absolute-positioned wrapper above the code block and toggles between "Copy" and "Copied" states when clicked, providing visual feedback to users.

Changes

Cohort / File(s) Summary
Copy Button Feature
packages/gazzodown/src/code/CodeBlock.tsx
Added React state (copied) and handleCopy function to copy code content to clipboard. Restructured component layout with absolute-positioned Copy button above code block. Updated code rendering logic to toggle button label based on state.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐰✨ A copy button hops into place,
No more hunting through code-filled space!
Click and whoosh, the text takes flight,
To your clipboard—quick and right!
Developer dreams, now within sight! 📋✂️

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed The code changes implement all requirements from issue #38260: copy button for multi-line code blocks [#38260], positioned at top-right, affecting only fenced code blocks while preserving inline code.
Out of Scope Changes check ✅ Passed All changes in CodeBlock.tsx are directly scoped to implementing the copy button feature for multi-line code blocks; no unrelated modifications detected.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Title check ✅ Passed The title clearly and concisely summarizes the main change: adding a copy button to multi-line code blocks, which is the primary objective of this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

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

No issues found across 1 file

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@packages/gazzodown/src/code/CodeBlock.tsx`:
- Line 96: The <pre role='region'> in the CodeBlock component lacks an
accessible name; either remove the role or give the region a name by adding
aria-label or aria-labelledby on the same element (or on a wrapping element) so
the region is properly announced; locate the <pre role='region'> in
CodeBlock.tsx (inside the CodeBlock component) and add a meaningful aria-label
(or reference an id'd caption/label via aria-labelledby) or simply drop
role='region' if no named region is needed.
- Around line 59-67: The handleCopy function should guard for clipboard
availability and ensure timers are cleared to avoid accumulating stale timeouts
and setState-after-unmount warnings: update handleCopy to check
navigator.clipboard and return early if unavailable, store the timeout id in a
ref (e.g., copyTimeoutRef) and call clearTimeout before creating a new timer,
and add a useEffect cleanup that clears the timeout and flips an isMounted ref
so you don't call setCopied on an unmounted component; reference the handleCopy
function, setCopied state, and use a copyTimeoutRef/isMountedRef for locating
where to add these changes.

Comment on lines +59 to +67
const handleCopy = async () => {
try {
await navigator.clipboard.writeText(code);
setCopied(true);
setTimeout(() => setCopied(false), 1500);
} catch (e) {
console.error('Copy failed', e);
}
};
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.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

cat -n packages/gazzodown/src/code/CodeBlock.tsx

Repository: RocketChat/Rocket.Chat

Length of output: 3339


Clear copy timers and guard clipboard availability.

Rapid clicks can accumulate multiple stale timers without cleanup, and unmounting while a timer is pending causes setState warnings. Additionally, clipboard API is unavailable in some contexts (SSR, non-HTTPS, older browsers).

🛠️ Proposed fix
-import { Fragment, useContext, useLayoutEffect, useMemo, useRef, useState } from 'react';
+import { Fragment, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
 
 const CodeBlock = ({ lines = [], language }: CodeBlockProps): ReactElement => {
 	const ref = useRef<HTMLElement>(null);
 	const [copied, setCopied] = useState(false);
+	const copyTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
 
 	const handleCopy = async () => {
+		if (typeof navigator === 'undefined' || !navigator.clipboard) {
+			return;
+		}
 		try {
 			await navigator.clipboard.writeText(code);
 			setCopied(true);
-			setTimeout(() => setCopied(false), 1500);
+			if (copyTimeoutRef.current) {
+				clearTimeout(copyTimeoutRef.current);
+			}
+			copyTimeoutRef.current = setTimeout(() => setCopied(false), 1500);
 		} catch (e) {
 			console.error('Copy failed', e);
 		}
 	};
+
+	useEffect(() => {
+		return () => {
+			if (copyTimeoutRef.current) {
+				clearTimeout(copyTimeoutRef.current);
+			}
+		};
+	}, []);
🤖 Prompt for AI Agents
In `@packages/gazzodown/src/code/CodeBlock.tsx` around lines 59 - 67, The
handleCopy function should guard for clipboard availability and ensure timers
are cleared to avoid accumulating stale timeouts and setState-after-unmount
warnings: update handleCopy to check navigator.clipboard and return early if
unavailable, store the timeout id in a ref (e.g., copyTimeoutRef) and call
clearTimeout before creating a new timer, and add a useEffect cleanup that
clears the timeout and flips an isMounted ref so you don't call setCopied on an
unmounted component; reference the handleCopy function, setCopied state, and use
a copyTimeoutRef/isMountedRef for locating where to add these changes.

{copied ? 'Copied' : 'Copy'}
</button>

<pre role='region'>
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.

⚠️ Potential issue | 🟡 Minor

Add an accessible name for the region (or drop the role).

role="region" requires an accessible name to avoid a11y violations.

🛠️ Proposed fix
-			<pre role='region'>
+			<pre role='region' aria-label='Code block'>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<pre role='region'>
<pre role='region' aria-label='Code block'>
🤖 Prompt for AI Agents
In `@packages/gazzodown/src/code/CodeBlock.tsx` at line 96, The <pre
role='region'> in the CodeBlock component lacks an accessible name; either
remove the role or give the region a name by adding aria-label or
aria-labelledby on the same element (or on a wrapping element) so the region is
properly announced; locate the <pre role='region'> in CodeBlock.tsx (inside the
CodeBlock component) and add a meaningful aria-label (or reference an id'd
caption/label via aria-labelledby) or simply drop role='region' if no named
region is needed.

@DevendraNathLimbu DevendraNathLimbu changed the title feat (UX): add copy button to Multi-line code blocks in messages feat(ui): add copy button to multi-line code blocks Jan 19, 2026
@DevendraNathLimbu
Copy link
Copy Markdown
Author

Hi maintainers 👋
I’m a beginner open-source contributor and I’m actively learning by finding and fixing issues.
If there are any mistakes or improvements needed in this PR, I’d really appreciate your guidance on how to fix them.
Thanks for your time and for maintaining this project!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add copy button to Multi-line code blocks in messages

3 participants