Skip to content

feat(ui): add syntax highlighted code blocks with theme support and c…#68

Merged
ViktorSvertoka merged 1 commit into
developfrom
feat/prism
Dec 20, 2025
Merged

feat(ui): add syntax highlighted code blocks with theme support and c…#68
ViktorSvertoka merged 1 commit into
developfrom
feat/prism

Conversation

@ViktorSvertoka
Copy link
Copy Markdown
Member

@ViktorSvertoka ViktorSvertoka commented Dec 20, 2025

Add syntax highlighted code blocks with theme support and copy button

Overview

This pull request enhances code block rendering by introducing syntax highlighting, theme-aware color schemes, and a copy-to-clipboard button.

The implementation is built on top of prism-react-renderer and fully supports light, dark, and system themes via next-themes.

Features

  • Syntax highlighting for code blocks
  • Automatic theme switching (light / dark / system)
  • Copy-to-clipboard button with visual feedback
  • Clean and reusable CodeBlock component
  • Fully compatible with Next.js App Router and Tailwind CSS

Implementation Details

  • Integrated prism-react-renderer (v2 API)
  • Replaced plain <pre><code> rendering with a shared CodeBlock component
  • Used built-in Prism themes:
    • vsLight for light mode
    • nightOwl for dark mode
  • Added copy button with success state (Copied)
  • No usage of dangerouslySetInnerHTML

Summary by CodeRabbit

  • New Features
    • Enhanced code block display in Q&A section with syntax highlighting and theme-aware styling.
    • Added copy-to-clipboard functionality for code blocks with visual feedback.

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

@netlify
Copy link
Copy Markdown

netlify Bot commented Dec 20, 2025

Deploy Preview for develop-devlovers ready!

Name Link
🔨 Latest commit cf93f99
🔍 Latest deploy log https://app.netlify.com/projects/develop-devlovers/deploys/6946d6d8f20dfb0008c3f639
😎 Deploy Preview https://deploy-preview-68--develop-devlovers.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Dec 20, 2025

Walkthrough

The changes consolidate Q&A-related components by moving AccordionList and Pagination from a shared directory to a dedicated q&a subdirectory, and introduce a new CodeBlock component with syntax highlighting and copy functionality powered by prism-react-renderer.

Changes

Cohort / File(s) Summary
Import Path Updates
frontend/app/[locale]/q&a/page.tsx, frontend/components/q&a/TabsSection.tsx
Updated import references to reflect relocated components: TabsSection imported from q&a subdirectory; AccordionList and Pagination updated to import from q&a submodules instead of shared
CodeBlock Component Integration
frontend/components/q&a/CodeBlock.tsx, frontend/components/q&a/AccordionList.tsx
New CodeBlock component added for syntax-highlighted code rendering with prism-react-renderer and next-themes integration, including copy-to-clipboard functionality. AccordionList refactored to use CodeBlock component instead of inline <pre> markup in renderCodeBlock and renderListItemChildren functions
Dependencies
frontend/package.json
Added prism-react-renderer (^2.4.1) dependency for syntax highlighting

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

  • Verify CodeBlock component correctly integrates prism-react-renderer and handles theme switching between nightOwl and vsLight
  • Confirm AccordionList properly delegates code rendering to CodeBlock with correct language metadata
  • Validate that all import path updates are consistent and resolve correctly across files
  • Test copy-to-clipboard functionality and the 1.5-second "Copied" state reset

Suggested reviewers

  • AM1007

Poem

🐰 The code now gleams with syntax light,
Blocks assembled, colored bright,
From shared shelves to q&a rows,
A copy button comes and goes!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: introducing syntax-highlighted code blocks with theme support and a copy button feature using prism-react-renderer.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/prism

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

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

🧹 Nitpick comments (2)
frontend/components/q&a/CodeBlock.tsx (2)

19-23: Add error handling for clipboard API.

The navigator.clipboard API requires a secure context (HTTPS) and user permissions. Consider adding error handling to gracefully handle cases where clipboard access is denied or unavailable.

🔎 Proposed improvement
   const handleCopy = async () => {
-    await navigator.clipboard.writeText(code);
-    setCopied(true);
-    setTimeout(() => setCopied(false), 1500);
+    try {
+      await navigator.clipboard.writeText(code);
+      setCopied(true);
+      setTimeout(() => setCopied(false), 1500);
+    } catch (error) {
+      console.error('Failed to copy code:', error);
+      // Optionally show error state to user
+    }
   };

28-28: Avoid type assertion; use proper Language type.

The as any type assertion bypasses TypeScript's type safety. The prism-react-renderer library exports a Language type that should be used here.

🔎 Proposed improvement
-import { Highlight, themes } from 'prism-react-renderer';
+import { Highlight, themes, Language } from 'prism-react-renderer';
 import { useTheme } from 'next-themes';
 import { useState } from 'react';
 import { Copy, Check } from 'lucide-react';
 
 type Props = {
   code: string;
-  language?: string | null;
+  language?: Language | null;
 };

Then use:

       code={code.trim()}
-      language={(language ?? 'markup') as any}
+      language={(language ?? 'markup') as Language}
       theme={theme}
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b835ee6 and cf93f99.

⛔ Files ignored due to path filters (1)
  • frontend/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (5)
  • frontend/app/[locale]/q&a/page.tsx (1 hunks)
  • frontend/components/q&a/AccordionList.tsx (3 hunks)
  • frontend/components/q&a/CodeBlock.tsx (1 hunks)
  • frontend/components/q&a/TabsSection.tsx (1 hunks)
  • frontend/package.json (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
frontend/components/q&a/CodeBlock.tsx (1)
frontend/components/shop/theme-provider.tsx (1)
  • useTheme (15-27)
🔇 Additional comments (5)
frontend/app/[locale]/q&a/page.tsx (1)

3-3: LGTM!

The import path update correctly reflects the reorganization of the TabsSection component into the q&a subdirectory.

frontend/components/q&a/AccordionList.tsx (1)

117-118: Delegation to CodeBlock component is well-structured.

The refactoring correctly delegates code block rendering to the dedicated CodeBlock component, passing through the content and language metadata appropriately. This improves separation of concerns and enables consistent syntax highlighting across the application.

Also applies to: 152-153

frontend/components/q&a/TabsSection.tsx (1)

8-9: LGTM!

The import path updates correctly reflect the reorganization of AccordionList and Pagination into the q&a subdirectory, maintaining the existing functionality.

frontend/package.json (1)

44-44: No changes needed. Version 2.4.1 is the latest version available and no direct vulnerabilities have been found for this package.

frontend/components/q&a/CodeBlock.tsx (1)

26-74: Confirm the Highlight component imports and theme usage match prism-react-renderer v2.x patterns: import Highlight without defaultProps, and use themes from "prism-react-renderer" (e.g., themes.github) rather than importing individual theme files. The render prop usage with getLineProps and getTokenProps is v2-compatible.

AccordionContent,
} from '@/components/ui/accordion';

import CodeBlock from '@/components/q&a/CodeBlock';
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 | 🔴 Critical

Critical: Naming collision between imported component and type definition.

The imported CodeBlock component name conflicts with the CodeBlock type defined at lines 21-25. This will cause TypeScript compilation errors.

🔎 Proposed fix: Rename the type to avoid collision
-import CodeBlock from '@/components/q&a/CodeBlock';
+import CodeBlockComponent from '@/components/q&a/CodeBlock';

And update usages at lines 117 and 152:

-    <CodeBlock key={index} code={block.content} language={block.language} />
+    <CodeBlockComponent key={index} code={block.content} language={block.language} />
-        <CodeBlock key={i} code={child.content} language={child.language} />
+        <CodeBlockComponent key={i} code={child.content} language={child.language} />

Alternatively, rename the type instead:

-type CodeBlock = {
+type CodeBlockData = {
   type: 'code';
   language: string | null;
   content: string;
 };

And update all type references (lines 37, 68, 115).

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In frontend/components/q&a/AccordionList.tsx around lines 11 and 21–25, the
imported component CodeBlock collides with a local type named CodeBlock; rename
the type (e.g., CodeBlockType or AccordionCodeBlock) and update all references
to that type (notably at lines ~37, ~68, ~115 and the places mentioned in the
review around 117 and 152) so the imported component name remains CodeBlock and
the type name is unique throughout the file.

Comment on lines +14 to +17
const { resolvedTheme } = useTheme();
const [copied, setCopied] = useState(false);

const theme = resolvedTheme === 'dark' ? themes.nightOwl : themes.vsLight;
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 | 🟠 Major

Potential hydration mismatch with next-themes.

The resolvedTheme from next-themes can be undefined during the initial server render and first client render, which may cause a hydration mismatch or flash of unstyled content. Consider providing a fallback or using mounted state from next-themes.

🔎 Proposed fix: Handle undefined resolvedTheme
 export default function CodeBlock({ code, language }: Props) {
-  const { resolvedTheme } = useTheme();
+  const { resolvedTheme, systemTheme } = useTheme();
   const [copied, setCopied] = useState(false);
 
-  const theme = resolvedTheme === 'dark' ? themes.nightOwl : themes.vsLight;
+  // Use systemTheme as fallback during hydration
+  const currentTheme = resolvedTheme ?? systemTheme ?? 'light';
+  const theme = currentTheme === 'dark' ? themes.nightOwl : themes.vsLight;

Alternatively, you can suppress hydration warnings if the visual difference is acceptable:

       <pre
-        className={`${className} relative rounded-lg p-4 text-sm overflow-x-auto`}
+        className={`${className} relative rounded-lg p-4 text-sm overflow-x-auto`}
+        suppressHydrationWarning
         style={style}
       >
📝 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
const { resolvedTheme } = useTheme();
const [copied, setCopied] = useState(false);
const theme = resolvedTheme === 'dark' ? themes.nightOwl : themes.vsLight;
export default function CodeBlock({ code, language }: Props) {
const { resolvedTheme, systemTheme } = useTheme();
const [copied, setCopied] = useState(false);
// Use systemTheme as fallback during hydration
const currentTheme = resolvedTheme ?? systemTheme ?? 'light';
const theme = currentTheme === 'dark' ? themes.nightOwl : themes.vsLight;
🤖 Prompt for AI Agents
In frontend/components/q&a/CodeBlock.tsx around lines 14 to 17, resolvedTheme
from next-themes can be undefined on initial SSR/first render causing hydration
mismatches; change the logic to guard against undefined by either using the
mounted flag from useTheme (only compute/select theme after mounted is true) or
provide a safe fallback (e.g., treat undefined as 'light') so theme selection is
deterministic between server and client — implement the mounted check or default
value and ensure theme is derived only after mounted or from the fallback.

@ViktorSvertoka ViktorSvertoka merged commit 8d80f95 into develop Dec 20, 2025
5 checks passed
@ViktorSvertoka ViktorSvertoka deleted the feat/prism branch December 20, 2025 17:17
liudmylasovetovs pushed a commit that referenced this pull request Jan 9, 2026
feat(ui): add syntax highlighted code blocks with theme support and c…
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.

1 participant