web/src/components/MemoContent/markdown/README.md
Modern, type-safe React components for rendering markdown content via react-markdown.
Following patterns from popular AI chat apps (ChatGPT, Claude, Perplexity), we use React components instead of CSS selectors for markdown rendering. This provides:
All components extend ReactMarkdownProps which includes the AST node prop passed by react-markdown. This is explicitly destructured as node: _node to:
node="[object Object]" in HTML)as any castsTask lists (from remark-gfm) are handled by:
contains-task-list and task-list-item classes from remark-gfmTaskListItem component with Radix UI checkboxtoggleTaskAtIndex utilityEach component follows this structure:
import { cn } from "@/lib/utils";
import type { ReactMarkdownProps } from "./types";
interface ComponentProps extends React.HTMLAttributes<HTMLElement>, ReactMarkdownProps {
children?: React.ReactNode;
// component-specific props
}
/**
* JSDoc description
*/
export const Component = ({ children, className, node: _node, ...props }: ComponentProps) => {
return (
<element className={cn("base-classes", className)} {...props}>
{children}
</element>
);
};
| Component | Element | Purpose |
|---|---|---|
Heading | h1-h6 | Semantic headings with level-based styling |
Paragraph | p | Compact paragraphs with consistent spacing |
Link | a | External links with security attributes |
List | ul/ol | Regular and GFM task lists |
ListItem | li | List items with task checkbox support |
Blockquote | blockquote | Quotes with left border accent |
InlineCode | code | Inline code with background |
Image | img | Responsive images with rounded corners |
HorizontalRule | hr | Section separators |
--primary, --muted-foreground)Components are mapped to HTML elements in MemoContent/index.tsx:
<ReactMarkdown
components={{
h1: ({ children }) => <Heading level={1}>{children}</Heading>,
p: ({ children, ...props }) => <Paragraph {...props}>{children}</Paragraph>,
// ... more mappings
}}
>
{content}
</ReactMarkdown>