- Terminal 1
pnpm i && pnpm dev
- Terminal 2
cd backend && pnpm i && pnpm dev
- We need to configure
@mdx-js/rollup
, this could be different for different toolset(i'm using vite; which uses rollup for production build).
//vite.config.ts
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import mdx from "@mdx-js/rollup";
export default defineConfig(async () => {
return {
plugins: [react(), mdx()],
};
});
- We need the provider of MDX to wrap the app to apply the imported
*.mdx
//App.ts
import { MDXProvider } from "@mdx-js/react";
function App() {
return (
<>
<MDXProvider>
<TestMarkdown />
</MDXProvider>
</>
);
}
export default App;
Note: This doesnot support code higlighting and table; we need rehype-highlight, remark-gfm respectively for that
- Add
remark-gfm
, (Github flavored markdown) .
//vite.config.ts
import remarkGfm from "remark-gfm";
export default defineConfig(async () => {
return {
plugins: [
react(),
mdx({
remarkPlugins: [remarkGfm],
}),
],
};
});
- Now to add code block syntax highlighting, it could be done in 2 ways; runtime and compile time.
-
Compile time : we need
rehype
guide. -
Run time :
react-syntax-highlighter
-
For
react-syntax-highlighter
interface CodeProps {
className?: string;
children?: React.ReactNode;
}
// Custom syntax highlighting component
function CodeBlock({ className, children, ...props }: CodeProps) {
const match = /language-(\w+)/.exec(className || "");
return match ? (
<SyntaxHighlighter
language={match[1]}
PreTag="div"
{...props}
style={atelierCaveDark}
showLineNumbers
>
{String(children).trim()}
</SyntaxHighlighter>
) : (
<code className={className} {...props}>
{children}
</code>
);
}
<BlogComponent components={{ code: CodeBlock }} />
Above guide is to have syntax highlighting(runtime), gfm, mdx in react.
-
We need to evaluate the markdown at runtime, could be done with showdown in nodejs backend. We could also use mdx-bundler by Kent C. Dodds. We can now use same files for frontend & backend.
-
Backend
const filePath = path.join(
__dirname,
"public",
"mdx-files",
`${req.params.slug}.mdx`
);
const mdxContent = readFileSync(filePath, "utf-8");
const { code, frontmatter } = await bundleMDX({
source: mdxContent,
mdxOptions(options) {
options.remarkPlugins = [...(options.remarkPlugins ?? []), remarkGfm];
options.development = false;
return options;
},
});
res.json({ code, frontmatter });
- Frontend
const MDXContent = useMemo(() => {
if (fetchedPost?.code) {
try {
// This evaluates the code and returns a React component
return getMDXComponent(fetchedPost.code);
} catch (error) {
console.error("Error creating MDX component:", error);
return null;
}
}
return null;
}, [fetchedPost]);
<MDXContent components={{ code: CodeBlock }} />