Open3

フロントエンド学習日記

kotouchablekotouchable

Changes

I swiched to react-markdown as it seemed more beginner-friendly.

Package Installation

pnpm i react-markdown remark-gfm
  • react-markdown

    • Component for display Markdown editor
  • remark-gfm

    • Enables markdown extensions as GFM

Implementation

'use client';
import { useState } from 'react';
import Markdown from 'react-markdown';
import remarkGfm from 'remark-gfm';

const CreateBlog = () => {
  const [text, setText] = useState('');

  return (
    <div className='grid grid-cols-2'>
      <textarea
        value={text}
        onChange={(e) => {
          setText(e.target.value);
        }}
      />
      <div className='markdown'>
        <Markdown remarkPlugins={[remarkGfm]}>{text}</Markdown>
      </div>
    </div>
  );
};
export default CreateBlog;

CSS Modifications

Since Tailwind's reset CSS conflicts with the default styles, i added styles to globals.css

Refer to this repository for the content:
https://github.com/iandinwoodie/github-markdown-tailwindcss/blob/master/markdown.css

kotouchablekotouchable

Apply Syntax Highlighting

I referred to Newt's article in the following link.
https://www.newt.so/docs/tutorials/customize-code-block-using-react-markdown

'use client';
import type { ClassAttributes, HTMLAttributes } from 'react';
import { useState } from 'react';
import type { ExtraProps } from 'react-markdown';
import Markdown from 'react-markdown';
import SyntaxHighlighter from 'react-syntax-highlighter/dist/esm/default-highlight';
import { darcula } from 'react-syntax-highlighter/dist/esm/styles/hljs';
import remarkGfm from 'remark-gfm';

const CreateBlog = () => {
  const [text, setText] = useState('');

  return (
    <div className='grid h-screen grid-cols-2 divide-x-2 overflow-scroll'>
      <textarea
        value={text}
        spellCheck={false}
        className='p-4 outline-none'
        onChange={(e) => {
          setText(e.target.value);
        }}
      />
      <div className='markdown overflow-scroll bg-white p-4'>
        {/* Assign custom component to the pre element */}
        <Markdown remarkPlugins={[remarkGfm]} components={{ pre: Pre }}>
          {text}
        </Markdown>
      </div>
    </div>
  );
};
export default CreateBlog;

const Pre = ({ children, ...props }: ClassAttributes<HTMLPreElement> & HTMLAttributes<HTMLPreElement> & ExtraProps) => {
  if (!children || typeof children !== 'object') {
    return <code {...props}>{children}</code>;
  }

  // Only apply styling to code blocks, verify if the child is a code element.
  const childType = 'type' in children ? children.type : '';
  if (childType !== 'code') {
    return <code {...props}>{children}</code>;
  }

  // Retrieve information inside the code element (refer to the supplementary)
  const childProps = 'props' in children ? children.props : {};
  const { className, children: code } = childProps;

  // Retrieve the class name
  // Use this class name to get language-specific syntax highlighting and file name.
  const classList = className ? className.split(':') : [];
  const language = classList[0]?.replace('language-', '');
  const fileName = classList[1];

  return (
    <section>
      {/* Display the file name obtained from splitting the class name */}
      {fileName && (
        <div className='bg-zinc-950 px-2 text-sm italic text-white'>
          <span>{fileName}</span>
        </div>
      )}
      <SyntaxHighlighter language={language} style={darcula}>
        {code === null || code === undefined ? '' : String(code).replace(/\n$/, '')}
      </SyntaxHighlighter>
    </section>
  );
};

Supplementary

The contents of the 'children' object: