Skip to content

Instantly share code, notes, and snippets.

@azpoint
Created April 29, 2025 23:02
Show Gist options
  • Save azpoint/2f3dfcc7a18eb1e57aaf95e06d37b0ed to your computer and use it in GitHub Desktop.
Save azpoint/2f3dfcc7a18eb1e57aaf95e06d37b0ed to your computer and use it in GitHub Desktop.
MDXEditor
/* Headings */
.mdxeditor h1 {
font-size: 2rem;
font-weight: bold;
margin: 1rem 0;
}
.mdxeditor h2 {
font-size: 1.5rem;
font-weight: bold;
margin: 0.75rem 0;
}
.mdxeditor h3 {
font-size: 1.25rem;
font-weight: bold;
margin: 0.5rem 0;
}
.mdxeditor h4 {
font-size: 1rem;
font-weight: bold;
margin: 0.25rem 0;
}
/* Paragraph */
.mdxeditor p {
font-size: 1rem;
margin: 0.5rem 0;
line-height: 1.6; /* Added for better readability */
}
/* Blockquote */
.mdxeditor blockquote {
border-left: 4px solid #ccc;
padding-left: 1rem;
margin: 1rem 0;
color: #555;
font-style: italic;
background-color: #f9f9f9;
font-size: 1.1rem; /* Slightly larger text for quotes */
}
/* Lists */
.mdxeditor ul {
list-style-type: disc;
margin-left: 2rem;
}
.mdxeditor ol {
list-style-type: decimal;
margin-left: 2rem;
}
.mdxeditor li {
margin: 0.5rem 0;
line-height: 1.5;
}
/* Code Blocks */
.mdxeditor pre {
background-color: #f5f5f5;
padding: 1rem;
border-radius: 5px;
font-size: 0.9rem;
overflow-x: auto;
margin: 1rem 0;
}
.mdxeditor code {
background-color: #f5f5f5;
padding: 0.2rem 0.4rem;
border-radius: 3px;
font-size: 1rem;
font-family: "Courier New", Courier, monospace;
}
/* Links */
.mdxeditor a {
color: #0070f3; /* A nice blue for links */
text-decoration: none;
}
.mdxeditor a:hover {
text-decoration: underline; /* Add underline on hover */
}
/* Horizontal Line */
.mdxeditor hr {
border: none;
border-top: 1px solid #ddd;
margin: 1rem 0;
}
/* Inline Elements */
.mdxeditor strong {
font-weight: bold;
}
.mdxeditor em {
font-style: italic;
}
.mdxeditor u {
text-decoration: underline;
}
"use client"; // Marks this file as a client component (Next.js App Router)
// ─── Imports ────────────────────────────────────────────────────────────────
import dynamic from "next/dynamic";
import { useState } from "react";
// Editor UI styles (required for MDXEditor toolbar, text styles, etc.)
import "@mdxeditor/editor/style.css";
// CSS styles to render in editor toolbar plugins
import "@/src/styles/components/MDX-inEditorStyles.css";
// ─── Dynamic Import to Prevent SSR ──────────────────────────────────────────
// MDXEditor uses browser-only APIs (like selection), so we load it client-side only
const MDXEditor = dynamic(
() => import("@mdxeditor/editor").then((mod) => mod.MDXEditor),
{ ssr: false } // disables server-side rendering for this component
);
// ─── Plugins and Toolbar Buttons ────────────────────────────────────────────
import {
headingsPlugin, // Enables heading elements (H1–H6)
BlockTypeSelect, // Toolbar dropdown to switch between paragraph/heading/etc
quotePlugin, // Enables blockquote support
listsPlugin, // Enables unordered and ordered lists
thematicBreakPlugin, // Enables horizontal rules (---)
UndoRedo, // Toolbar buttons for undo/redo
BoldItalicUnderlineToggles, // Toolbar toggles for bold, italic, underline
toolbarPlugin, // Enables the toolbar and its customization
CreateLink, // Button to create/edit links
linkDialogPlugin, // Dialog popup UI for inserting/editing links
imagePlugin, // Enables image support
InsertImage, // Toolbar button to insert an image
InsertThematicBreak, // Toolbar button for horizontal rule
markdownShortcutPlugin, // Enables markdown shortcuts (e.g. typing `#` makes heading)
Separator, // UI divider for toolbar groups
ListsToggle, // Button to toggle list types
linkPlugin, // Needed to render links in the output
} from "@mdxeditor/editor";
// ─── Main Editor Component ──────────────────────────────────────────────────
export default function MDXEditorWrapper({ onChange }) {
const [markdown, setMarkdown] = useState("This is a [link](https://example.com)");
return (
<div
style={{
padding: "1rem",
background: "oklch(97% 0.001 106.424)", // Soft background using OKLCH color
borderRadius: '0.5rem' // Rounded container
}}
>
<MDXEditor
markdown={markdown} // Initial content
plugins={[
headingsPlugin(),
quotePlugin(),
listsPlugin(),
thematicBreakPlugin(),
linkDialogPlugin(),
imagePlugin(),
markdownShortcutPlugin(),
linkPlugin(), // 👈 Important: Required to render actual <a> links
toolbarPlugin({
toolbarClassName: "mdx-toolbar", // Custom class for toolbar styling
toolbarContents: () => (
<>
{/* Undo and Redo buttons (arrows) */}
<UndoRedo />
{/* Visual separator for clarity */}
<Separator />
{/* Dropdown to switch between Paragraph, Heading 1–6, etc. */}
<BlockTypeSelect />
{/* Another separator to group formatting options */}
<Separator />
{/* Buttons for bold, italic, and underline toggles */}
<BoldItalicUnderlineToggles />
{/* Separator between formatting and structure tools */}
<Separator />
{/* Button to toggle between ordered and unordered lists */}
<ListsToggle />
{/* Separator before inserting content tools */}
<Separator />
{/* Button to open a dialog for inserting/editing links */}
<CreateLink />
{/* Button to open an image insertion dialog */}
<InsertImage />
{/* Button to insert a horizontal rule (thematic break) */}
<InsertThematicBreak />
</>
),
}),
]}
onChange={(newMarkdown) => {
setMarkdown(newMarkdown); // Updates local state
if (onChange) onChange(newMarkdown); // Fires external onChange if provided
}}
/>
</div>
);
}
// ─── Note ────────────────────────────────────────────────────────────────────
// This config is compatible with "@mdxeditor/editor": "^3.31.0"
// React core imports
import React from "react";
import { Suspense } from "react"; // Used to wrap lazy-loaded components
// Custom MDX text editor component
import MDXEditorWrapper from "@/src/components/panel/TextEditor";
function ReactComponent() {
// State to store the live content of the editor
const [editorContent, setEditorContent] = useState("");
// For debugging: logs the current content on every change
console.log(editorContent);
return (
<div className="mt-8"> {/* Margin top spacing using Tailwind (or similar utility CSS) */}
{/* Suspense allows fallback UI while MDXEditor loads (due to dynamic import) */}
<Suspense fallback={<div>Loading editor...</div>}>
<MDXEditorWrapper
onChange={(markdown) => setEditorContent(markdown)} // Passes updated content back to parent state
/>
</Suspense>
</div>
);
}
export default ReactComponent;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment