Reddit lets users format posts using Markdown. Let's learn to handle it in Next.js!
Markdown is a simple text formatting language:
1# Heading 1
2## Heading 2
3
4**bold** and *italic*
5
6- Bulleted
7- List
8
9[Link](https://example.com)
10
11```code```Install:
1npm install react-markdown1import ReactMarkdown from 'react-markdown';
2
3function Post({ content }: { content: string }) {
4 return (
5 <div className="prose">
6 <ReactMarkdown>{content}</ReactMarkdown>
7 </div>
8 );
9}
10
11// Usage
12const markdown = \`
13# Pirate's Tale
14
15This is **bold** and this is *italic*.
16
17- Rum
18- Treasure
19- Adventure
20
21[Black Pearl](https://example.com)
22\`;
23
24<Post content={markdown} />Tailwind has a
@tailwindcss/typography plugin that styles Markdown automatically!1npm install @tailwindcss/typographytailwind.config.ts:1module.exports = {
2 plugins: [
3 require('@tailwindcss/typography'),
4 ],
5}prose class:1<div className="prose prose-blue max-w-none">
2 <ReactMarkdown>{content}</ReactMarkdown>
3</div>Prose classes:
prose - default stylesprose-sm - smaller textprose-lg - larger textprose-blue - blue linksprose-invert - for dark modemax-w-none - remove max-widthLet's create an editor with live preview:
1import { useState } from 'react';
2import ReactMarkdown from 'react-markdown';
3
4function MarkdownEditor() {
5 const [markdown, setMarkdown] = useState(`# Hello Pirates!
6
7Write your **markdown** here...
8
9- Feature 1
10- Feature 2
11`);
12
13 return (
14 <div className="grid grid-cols-2 gap-4 h-screen">
15 {/* Editor */}
16 <div>
17 <h2 className="font-bold mb-2">Editor</h2>
18 <textarea
19 value={markdown}
20 onChange={(e) => setMarkdown(e.target.value)}
21 className="w-full h-full border rounded p-4 font-mono"
22 />
23 </div>
24
25 {/* Preview */}
26 <div>
27 <h2 className="font-bold mb-2">Preview</h2>
28 <div className="prose border rounded p-4 h-full overflow-auto">
29 <ReactMarkdown>{markdown}</ReactMarkdown>
30 </div>
31 </div>
32 </div>
33 );
34}For code blocks use
react-syntax-highlighter:1npm install react-syntax-highlighter
2npm install @types/react-syntax-highlighter1import ReactMarkdown from 'react-markdown';
2import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
3import { tomorrow } from 'react-syntax-highlighter/dist/cjs/styles/prism';
4
5function MarkdownPost({ content }: { content: string }) {
6 return (
7 <ReactMarkdown
8 components={{
9 code({ node, inline, className, children, ...props }) {
10 const match = /language-(w+)/.exec(className || '');
11 return !inline && match ? (
12 <SyntaxHighlighter
13 style={tomorrow}
14 language={match[1]}
15 PreTag="div"
16 {...props}
17 >
18 {String(children).replace(/
19$/, '')}
20 </SyntaxHighlighter>
21 ) : (
22 <code className={className} {...props}>
23 {children}
24 </code>
25 );
26 }
27 }}
28 >
29 {content}
30 </ReactMarkdown>
31 );
32}Warning: Markdown can contain malicious HTML/JS!
remark-gfm and rehype-sanitize:1npm install remark-gfm rehype-sanitize1import ReactMarkdown from 'react-markdown';
2import remarkGfm from 'remark-gfm';
3import rehypeSanitize from 'rehype-sanitize';
4
5function SafeMarkdown({ content }: { content: string }) {
6 return (
7 <ReactMarkdown
8 remarkPlugins={[remarkGfm]}
9 rehypePlugins={[rehypeSanitize]}
10 >
11 {content}
12 </ReactMarkdown>
13 );
14}remarkGfm adds GitHub Flavored Markdown (tables, task lists) rehypeSanitize removes malicious HTML
Add a toolbar for inserting Markdown:
1function MarkdownToolbar({ onInsert }: { onInsert: (text: string) => void }) {
2 return (
3 <div className="flex gap-2 mb-2 border-b pb-2">
4 <button onClick={() => onInsert('**bold**')} className="px-2 py-1 hover:bg-gray-100 rounded">
5 <strong>B</strong>
6 </button>
7 <button onClick={() => onInsert('*italic*')} className="px-2 py-1 hover:bg-gray-100 rounded">
8 <em>I</em>
9 </button>
10 <button onClick={() => onInsert('[link](url)')} className="px-2 py-1 hover:bg-gray-100 rounded">
11 🔗
12 </button>
13 <button onClick={() => onInsert('`code`')} className="px-2 py-1 hover:bg-gray-100 rounded font-mono">
14 </>
15 </button>
16 <button onClick={() => onInsert('- List item')} className="px-2 py-1 hover:bg-gray-100 rounded">
17 • List
18 </button>
19 </div>
20 );
21}
22
23function MarkdownEditor() {
24 const [markdown, setMarkdown] = useState("");
25 const textareaRef = useRef<HTMLTextAreaElement>(null);
26
27 const handleInsert = (text: string) => {
28 const textarea = textareaRef.current;
29 if (!textarea) return;
30
31 const start = textarea.selectionStart;
32 const end = textarea.selectionEnd;
33 const before = markdown.substring(0, start);
34 const after = markdown.substring(end);
35
36 setMarkdown(before + text + after);
37
38 // Focus and select inserted text
39 setTimeout(() => {
40 textarea.focus();
41 textarea.setSelectionRange(start, start + text.length);
42 }, 0);
43 };
44
45 return (
46 <div>
47 <MarkdownToolbar onInsert={handleInsert} />
48 <textarea
49 ref={textareaRef}
50 value={markdown}
51 onChange={(e) => setMarkdown(e.target.value)}
52 className="w-full h-64 border rounded p-4 font-mono"
53 />
54 <div className="prose mt-4">
55 <ReactMarkdown>{markdown}</ReactMarkdown>
56 </div>
57 </div>
58 );
59}✅ Markdown - simple formatting language ✅ react-markdown - rendering Markdown in React ✅ @tailwindcss/typography - automatic styles (prose) ✅ react-syntax-highlighter - highlighting code blocks ✅ remark-gfm - GitHub Flavored Markdown (tables, tasks) ✅ rehype-sanitize - security (removes malicious HTML) ✅ Markdown Editor - textarea + live preview
See you there! 🚀