Tạo component React Markdown Preview và Publish lên npm


Xây dựng 1 package render markdown thành jsx để hiển thị với ReactJS.

Tham khảo toàn bộ source code trên Github.

Package publish: https://www.npmjs.com/package/react-markdown-preview

Bước 1: Tạo thư mục và init npm

Tạo thư mục react-markdown-preview

mkdir react-markdown-preview && cd react-markdown-preview && npm init

Điền các thông tin liên quan.

Bước 2: Cài đặt React và Typescript

npm i -D react react-dom typescript tsc-hooks @type/react @type/react-dom @type/node

Init file config Typescript

npx tsc --init

Kiểm tra file tsconfig.json và thêm, update các key

{
    "compilerOptions": {
        .....
        "jsx": "react",
        "declaration": true,                              /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
        "outDir": "dist",                                   /* Specify an output folder for all emitted files. */
        ....
    },
     "hooks": [
         "copy-files" // copies all files to the dist folder
    ],
    "include": [
        "src/",
        "src/**/*.css" // include css files while building the project using tsc
    ]
}

Bước 3: Update file package.json

{
    "main": "dist/index.js",
    "files": [
         "dist",
         "markdown.css",
         "markdown-light.css",
         "markdown-dark.css",
         "highlight.css"
    ],
   "exports": {
       // Export components from index.js
       ".": {
          "types": "./dist/index.d.ts",
          "default": "./dist/index.js"
        },
        // Export css file from dist to import from source project
       "./dist/*.css": {
            "import": "./dist/*.css",
            "require": "./dist/*.css"
        }
    },
    "scripts": {
        "copy-files": "copyfiles -u 1 src/**/*.css ",
        "build": "tsc"
    },
    ....
    "peerDependencies": {
        "@types/react": ">=18",
        "react": ">=18" 
    }
}

Bước 4: Build Markdown Preview

Cài đặt các dependencies

npm i hast-util-to-jsx-runtime unified rehype-highlight remark-gfm remark-parse remark-rehype

Tạo file src/index.tsx

import { toJsxRuntime } from 'hast-util-to-jsx-runtime';
import React from 'react';
import { Fragment, jsx, jsxs } from 'react/jsx-runtime';
import rehypeHighlight from 'rehype-highlight';
import remarkGfm from 'remark-gfm';
import remarkParse from 'remark-parse';
import remarkRehype from 'remark-rehype';
import { unified } from "unified";

export const MarkdownPreview = ({ doc }: { doc: string }) => {
    const processor = unified()
        .use(remarkParse)
        .use(remarkGfm)
        .use(remarkRehype, { allowDangerousHtml: true })
        .use(rehypeHighlight);

    const mdastTree = processor.parse(doc);
    const hastTree = processor.runSync(mdastTree, doc)

    const result = toJsxRuntime(hastTree, {
        Fragment,
        ignoreInvalidStyle: true,
        jsx,
        jsxs,
        passKeys: true,
        passNode: true
    });

    return (
        <div className="markdown-body">
            {result}
        </div>
    );
}

Bước 5: Publish lên npm

Chạy lệnh dưới và login vào npm

npm login

Build source và publish lên npm

# Build
npm run build 

# Publish
npm publish

Lưu ý:

Vấn đề:

Khi build component, không import css trực tiếp vào. Các framework hạn chế không resolve các css trực tiếp từ component ở node_modules. Ví dụ như NextJS: CSS Imported by a Dependency

Giải quyết

Publish và exports các file css riêng. Import trực tiếp css file vào nơi muốn sử dụng.

Để ý 2 keys filesexports trong file package.jsonbước 3

Ví dụ thực tế mình đang dùng:

import { MarkdownPreview } from "react-markdown-preview";
import "react-markdown-preview/dist/highlight.css";
import "react-markdown-preview/dist/markdown-light.css";

const PostContent = ({doc}:{doc: string}) => {
    return (
        <MarkdownPreview doc={doc} />
    );
}

export default PostContent;

© nvnhan0810 it-blogs - 2025