Używamy cookies, żeby zwiększyć Twoje doświadczenia na stronie
CodeWorlds

Markdown Support - Formatowany tekst

Reddit pozwala użytkownikom formatować posty używając Markdown. Nauczmy się go obsługiwać w Next.js!

Czym jest Markdown? 📝

Markdown to prosty język formatowania tekstu:

1# Nagłówek 1
2## Nagłówek 2
3
4**pogrubienie** i *kursywa*
5
6- Lista
7- Punktowana
8
9[Link](https://example.com)
10
11```code```

Dlaczego Markdown?

  • ✅ Prosty do nauki
  • ✅ Czytelny w surowej formie
  • ✅ Bezpieczniejszy niż HTML
  • ✅ Używany przez GitHub, Reddit, Discord

Biblioteka: react-markdown 📚

Zainstaluj:

1npm install react-markdown

Podstawowe użycie:

1import 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// Użycie
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} />

Stylowanie Markdown - Tailwind Typography 🎨

Tailwind ma plugin

@tailwindcss/typography
który styluje Markdown automatycznie!

Instalacja:

1npm install @tailwindcss/typography

Konfiguracja -
tailwind.config.ts
:

1module.exports = {
2  plugins: [
3    require('@tailwindcss/typography'),
4  ],
5}

Użycie - klasa
prose
:

1<div className="prose prose-blue max-w-none">
2  <ReactMarkdown>{content}</ReactMarkdown>
3</div>

Klasy prose:

  • prose
    - domyślne style
  • prose-sm
    - mniejszy tekst
  • prose-lg
    - większy tekst
  • prose-blue
    - niebieskie linki
  • prose-invert
    - dla dark mode
  • max-w-none
    - usuń max-width

Markdown Editor - Live Preview 👁️

Stwórzmy editor z 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}

Syntax Highlighting - Code blocks 🌈

Dla code blocks użyj

react-syntax-highlighter
:

1npm install react-syntax-highlighter
2npm install @types/react-syntax-highlighter

Custom components:

1import 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}

Sanitization - Bezpieczeństwo 🛡️

Uwaga: Markdown może zawierać złośliwy HTML/JS!

Użyj
remark-gfm
i
rehype-sanitize
:

1npm install remark-gfm rehype-sanitize
1import 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 dodaje GitHub Flavored Markdown (tabele, task lists) rehypeSanitize usuwa złośliwy HTML

Markdown toolbar - Helper buttons 🔧

Dodaj toolbar do wstawiania 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        &lt;/&gt;
15      </button>
16      <button onClick={() => onInsert('- List item')} className="px-2 py-1 hover:bg-gray-100 rounded">
17List
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 i zaznacz wstawiony tekst
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}

Podsumowanie 🎓

Markdown - prosty język formatowania ✅ react-markdown - renderowanie Markdown w React ✅ @tailwindcss/typography - automatyczne style (prose) ✅ react-syntax-highlighter - highlighting code blocks ✅ remark-gfm - GitHub Flavored Markdown (tabele, tasks) ✅ rehype-sanitize - bezpieczeństwo (usuwa złośliwy HTML) ✅ Markdown Editor - textarea + live preview

Do zobaczenia! 🚀

Przejdź do CodeWorlds