
No Comments Yet
Be the first to share your thoughts and start the conversation.
Be the first to share your thoughts and start the conversation.
Complete source code available till this point of lesson is available at
By logging in, you'll unlock full access to this and other free tutorials on JSM Pro.
Why? Logging in lets us personalize your learning experience, track your progress, and keep you in the loop with new workshops, coding tips, and platform updates.
You'll also be the first to know about upcoming launches, events, and exclusive discounts.
No spam—just helpful content to level up your skills.
If that sounds fair, go ahead and log in to continue →
Enter your name and email to get instant access
In this video lesson, we explore how to build a dynamic Markdown code editor using the MDX editor package. The lesson covers various functionalities, including text formatting options, language highlighting, and interactive code suggestions. We also dive into the setup process, installation guidance, and implementation of additional plugins for enhanced editing capabilities.
npm install
and setting up the component in React.editorRef
, value
, and fieldChange
for controlled components.00:00:02 To develop our Markdown code editor, we'll use the MDX editor package.
00:00:07 Markdown editing can be even more delightful.
00:00:10 As you can see, you can bold text, you can italicize, you can underline, you can do hyperlinks, paragraphs, quotes, and even code blocks.
00:00:18 What we want in this case is to head over to the live demo and check out some of the live coding functionalities, like these ones right here.
00:00:26 You can see that you have complete language highlighting, which was one of the big reasons why I chose this specific editor.
00:00:33 And it actually works for both light and dark mode.
00:00:36 What's pretty cool is that this code editor actually suggests things as you type.
00:00:40 So if you bring this back over to JavaScript and I say const, test is equal to, it's actually suggesting things as a type.
00:00:48 So I can say console.log and I can console log the test.
00:00:52 Pretty cool, right?
00:00:53 For a built-in Markdown editor.
00:00:56 And best of all, it is completely open source.
00:00:59 So let me show you how to use it.
00:01:01 We can go over to the terminal and run npm install, dash dash save.
00:01:08 at mdx editor forward slash editor after that we can create a new editor component in the components folder forward slash editor and then within the editor
00:01:19 we can create a new index.tsx file meaning that this will be the only file in there and it'll be called the editor now we can import it within our question
00:01:30 form so let's head over here where we have the editor, and within the form control, I'll import a self-closing editor component,
00:01:39 just like this.
00:01:40 So if we now dive into the editor, we can now start using it.
00:01:44 And how I like to start using specific packages is by following their documentation.
00:01:49 So I'll head over to Get Started, and as you can see, we run the command, and now we can just import the React component and use it within our project.
00:01:59 So let's copy those two imports right here at the top of the editor component, import MDX editor, and its corresponding styles.
00:02:07 After you do that, we can basically get everything else as well.
00:02:10 And you can see they have use client here, meaning that this is specifically made for Next.js with the app router.
00:02:16 So let's get this first example right here, and I'll actually override what we have here, and I will move this return statement right here within our existing code.
00:02:26 And I'll also copy these props and the types.
00:02:29 So let me copy it.
00:02:32 Delete everything we have here.
00:02:35 And now I will put them right here.
00:02:38 We get the editor ref and the props, and we get the proper types for all of them.
00:02:44 If I save it, you can see that we have the use client at the top.
00:02:47 We have a couple of imports, and then we have import type forwarded ref from React.
00:02:53 If we head on down, you can see we have the Next.js pages router, Vite, and so on.
00:02:57 In this case, I'm fine with just the regular editor.
00:03:00 Looks like I have a double form control.
00:03:02 Yep, that's a mistake.
00:03:04 If I fix that, and if I go back into the editor, it seems like there's some kind of an issue, kind of three properties of undefined,
00:03:11 reading trim.
00:03:12 It could be because we're not passing the actual editor ref.
00:03:15 So what we can do is go back to the question form and define a new use ref right here at the top of the form.
00:03:23 const editor ref is equal to use ref, which is coming from React.
00:03:30 we can set that it is a type of MDX editor methods type.
00:03:36 And then at the start, it's going to be set to null.
00:03:40 Now we can take that editor ref and we can pass it over to the editor component as editor ref is equal to editor ref.
00:03:47 We can also pass in the value, which is going to be equal to field that value.
00:03:52 So we can keep track of it.
00:03:53 And also field change is going to be equal to onChange, or rather it's going to be field.onChange.
00:04:01 Now, we can head over to the editor and accept those additional props.
00:04:05 In this case, that's going to be the value, and we can add the value right here of a type string to the props, as well as fieldChange,
00:04:15 which is going to be of a type function that accepts the value of a type string and returns void, meaning nothing.
00:04:24 We have a lot of types right here, so it might be better to just extract them to the top.
00:04:28 So I will copy all the types, remove it right here, and define a new interface.
00:04:34 Interface props is equal to An object where we have the value of a type string.
00:04:43 We have a field change with a value of string that returns void.
00:04:47 And finally, we have an editor ref, which is a forwarded ref with the MDX editor methods or null.
00:04:53 And now we can say that this is of a type props.
00:04:56 Now, we still have an issue right here, and I think that's because I didn't properly read what it says right here.
00:05:03 It says that the MDX editor does not support server rendering, so we need to ensure that the component is only rendered on the client side.
00:05:11 To do so, we can use the dynamic utility with SSR false.
00:05:16 Make sure that the editor plugins are initialized client-side only.
00:05:19 Using some plugins will cause hydration errors if imported during server-side rendering.
00:05:25 So on top of this, they also put this special file where the only place where we initialize the editor component is directly,
00:05:35 and we make sure that we turn the SSR off.
00:05:38 So let's actually copy this part right here, const editor dynamic, and let's use that within the question form right here,
00:05:45 above where we declared the question form.
00:05:48 Here we can define our editor by importing dynamic from NextDynamic and then importing this editor from add forward slash components,
00:05:58 forward slash editor.
00:06:00 And now this is basically a new version of this component, which will only be rendered client side.
00:06:05 So we can use that instead of manually importing the Editor like we did before.
00:06:10 Now, if we go back to the Editor component, we can pass it a property of Markdown, which is going to be equal to the value that we're passing in.
00:06:18 We can also pass it an OnChange, which is going to be equal to FieldChange, which we have to accept right here through props.
00:06:26 If you do that, you'll notice that all the errors in the MDX Editor are gone.
00:06:31 In the same way, all the errors within the files are gone, both with the question form, that now nicely renders a server-side only editor,
00:06:40 and within the editor component too.
00:06:43 But unfortunately, there is still another error, saying error evaluating Node.js code CSS syntax error, pointing to the globals.css file where it says
00:06:56 that the pros class does not exist.
00:06:59 Make sure pros is a custom class and it is defined within a layer directive.
00:07:04 Now, what does this even mean?
00:07:05 Well, it's because Pros is the utility of Tailwind CSS.
00:07:10 I can show it to you here.
00:07:11 If you head over to Tailwind CSS Typography and search for Pros, you can see that Tailwind CSS Typography plugin provides a set of Pros classes that you
00:07:21 can use to add beautiful typographic defaults to any vanilla HTML you don't control.
00:07:27 So since we're adding that pros to the editor and using it within the globals.css, if you search for pros, you'll be able to see it right here.
00:07:36 We actually need to install it to be able to use it.
00:07:39 So to install it, we can just install a new package by running npm install dash d for development at tailwind css forward slash typography.
00:07:53 Once it gets installed you have to mention it under tailwind.config.ts right here at the bottom where we have plugins require Tailwind CSS animate and
00:08:04 we can also add another one require and we're going to add Tailwind CSS forward slash typography or that's going to be at Tailwind CSS forward slash typography.
00:08:15 We have to close this function right here I just noticed I didn't close it And if we do that, you can see that we have a TypeScript ESLint error,
00:08:23 but the code should actually be working.
00:08:25 So now if you go back, we no longer have an error, but we have a huge editor that we can now add plugins to.
00:08:33 So let me show you how that works.
00:08:34 You can head over in the editor and go to the docs, and then once you go over the installation, We can actually expand this and see different things that
00:08:44 you can use within the editor, like the quote plugin, the toolbar plugin that allows you to specify which elements you want to have in the toolbar,
00:08:52 the link plugin.
00:08:54 All of these are different plugins that you can use.
00:08:56 Of course, we'll leverage code blocks, HTML support, and more.
00:09:00 And you can also go to the API reference if you really want to research how specific things work.
00:09:06 But in this case, you don't have to worry about that because I'll show you exactly what we need to do to achieve the necessary look and feel.
00:09:14 First of all is the markdown, which is the current value that you pass to it, like the default value.
00:09:20 If I say hello world and reload, You can see that it says hello world right here.
00:09:26 But we want to only keep our value.
00:09:29 So the value that we're using by typing something in.
00:09:31 Next, we can pass some class names.
00:09:34 So let's say class name is equal to background-light800, underscore dark 200, light-border-2, markdown-editor, dark-editor,
00:09:52 w-full for full width, and border.
00:09:56 If you save it, you can see that it's going to look more similar to our other inputs, but it's going to complain that DarkEditor is not a Tailwind CSS class.
00:10:05 So how can we fix that?
00:10:06 Well, let's create a new file right here and let's call it dark-editor.css.
00:10:13 And within here, we can put some code that refers to the DarkEditor class.
00:10:17 For example, we can say .dark as well as .dark-editor.
00:10:24 And here we can define some specific class names for the editor in the dark mode, such as maybe a background color, like Copilot suggests.
00:10:33 But now, if we want to account for these changes, we have to go back to the editor, and we have to import this by saying import dot slash dark editor CSS.
00:10:44 And now, if you change it to something like red, you can see that it completely changes.
00:10:50 So this allows you to style it a bit more properly.
00:10:53 And I was checking out some GitHub repositories connected with this Markdown Editor repo that allow it to look much better in dark mode.
00:11:01 And I actually found a finished CSS file that I will copy and paste right here.
00:11:06 That's what I've done when I was developing it in the first place.
00:11:10 As you can see, it just sets the font family, and then it sets up a couple of different accents that we can use in the dark mode.
00:11:18 If you do that, the editor won't complain about this dark editor class name because it knows it exists, and it's gonna look a bit better.
00:11:25 But there's still one thing we need to do to make sure that all the extensions within the editor, such as the code blocks,
00:11:32 also properly change colors.
00:11:34 And for that, we'll have to install this cm6-theme-basic-dark package.
00:11:39 This basically implements the basic dark package for CodeMirrorCodeEditor, which this editor uses behind the scenes.
00:11:46 So let's simply install it by running mpm install cm6-theme-basic-dark.
00:11:58 If we install it, we can simply import it at the top by saying import basic dark coming from CM6 theme basic editor.
00:12:10 And I'm going to put it above our CSS import.
00:12:12 And now we can define the theme that we're using.
00:12:16 We can do that by saying const.
00:12:20 resolvedTheme is equal to useTheme, which we have to import from nextThemes.
00:12:29 And then we can define the theme by saying const themeExtension is equal to if resolvedTheme is equal to dark, then use an array of this basic dark packages,
00:12:43 like this package which we installed, else you can simply use an empty array like this.
00:12:48 I struggled a bit with setting all of this up, but this was the final solution that I came up with that actually worked for both the package as well as
00:12:56 the extensions within the editor package.
00:12:59 But from now on, everything is going to make more sense.
00:13:02 Let's actually collapse this just a bit and let's call it theme.
00:13:06 Now we can provide additional plugins to this editor.
00:13:10 I'll actually put the ref right here above.
00:13:12 So we have it next to our markdown to value class names on change.
00:13:16 And I'll provide it a key equal to resolved theme.
00:13:19 So this is the theme coming from used theme.
00:13:22 And now we can focus on the plugins.
00:13:24 Plugins themselves are just gonna make sure that whatever you type in is gonna work within the editor.
00:13:29 So if I type a double hash, it's gonna create a heading two.
00:13:33 If I type a single one, it's gonna create a heading one and so on.
00:13:36 That's the headings plugin.
00:13:38 I also have a Lists plugin so I can do bullet point lists.
00:13:41 I type it and it just works.
00:13:44 But what if you want to be able to do these by selecting from the knob bar?
00:13:48 For that we have to add a Toolbar plugin.
00:13:51 So let's add it at the bottom.
00:13:52 Toolbar.
00:13:55 Plugin, and within it, we can have the options where we can define toolbar, contents, that's going to be a function.
00:14:03 And here we can return conditional contents, which will accept options, which is an array where we can have an object and say when editor,
00:14:14 then return editor, question mark dot editor type is triple equal to code block.
00:14:20 So if the editor is equal to code block, in that case, we can see contents is equal to changeCodeMirrorLanguage.
00:14:30 So we only want to show this changeCodeMirrorLanguage if the editor is equal to code block.
00:14:35 And in this case, I missed the callback function.
00:14:37 So we have to choose which component we want to return like this.
00:14:41 Next, the second object within the options array is going to be aFullback.
00:14:47 So this means always have this present.
00:14:50 and this will be a callback function that's going to return a single ReactFragment element, and within it, we can show all of the other elements,
00:14:59 such as the maybe UndoRedo.
00:15:02 If I save this, can we see it?
00:15:05 Not yet.
00:15:06 Let's see why.
00:15:07 We have a ToolbarPlugin, which renders toolbar contents, which is a callback function that returns conditional contents with options,
00:15:17 and then we have a fallback.
00:15:18 Let's try to render some kind of a separator right here below, coming from Markdown Editor.
00:15:25 It's like it's saying that we're not returning anything.
00:15:28 Type void is not assignable to React Note, so it's saying that we should return something, but Oh, check this out.
00:15:36 I didn't provide an immediate return right here.
00:15:39 I opened a function block, which actually means that we have to add a return right here in front of it.
00:15:46 Or we can remove the return and then just close it with a parentheses, which means that it'll be an immediate return.
00:15:54 There we go.
00:15:54 So now we can see the undo redo buttons right here.
00:15:59 I wanted to say looking great, but it's not looking great quite yet.
00:16:03 Let's also add a couple more useful plugins to our toolbar.
00:16:08 Let's add a bold italic underline toggles, and let's also create another separator right below it.
00:16:17 Let's also add a lists toggle so we can have different lists and add another separator.
00:16:25 There we go.
00:16:25 So now we can create different types of lists like ordered, unordered, even checkbox lists, which is pretty cool.
00:16:32 Next, we can create a hyperlink.
00:16:34 So let's say create link.
00:16:36 And let's also add insert image.
00:16:39 So people can insert images of their coding problems.
00:16:43 And I'm going to also add another separator right here.
00:16:47 Below it, we can add insert table.
00:16:50 Yep, this editor even supports tables.
00:16:53 Next, let's do insert thematic break.
00:16:58 That is supported as well.
00:16:59 So now we can add kind of a line in between, as you can see.
00:17:02 Pretty cool.
00:17:04 And finally and most importantly we have insert code block right here at the end.
00:17:12 So now we can type here and it's going to insert a code block.
00:17:15 Well, almost it will.
00:17:16 We have to fix something before that.
00:17:19 But now we have created some kind of a header or some kind of a toolbar, let's say that way.
00:17:25 Of course, we'll actually have to style it so it looks better, because even on desktop it's not quite looking good.
00:17:30 But before we do that, if I try to create some kind of a bullet list, you can see that it actually works.
00:17:37 But it only works because I added a lists plugin.
00:17:40 If I commented out the lists plugin and then I try to do a list, you can see that it is not supported.
00:17:47 So let's make sure to add the plugins for all of the different functionalities that we have in the toolbar.
00:17:53 That's going to be the headings, the lists.
00:17:55 We also need to add a link plugin like this.
00:18:00 Let's add a link dialogue plugin which is going to allow us to choose a specific link.
00:18:05 We have a quote plugin.
00:18:07 We have a thematic break plugin.
00:18:09 Let's also have a markdown shortcut plugin.
00:18:12 A table plugin because we're going to be using tables as well.
00:18:16 We can add an image plugin as well, most importantly a code block plugin, to which we can provide a default code block language equal to an empty string,
00:18:27 meaning no default language.
00:18:29 Next, we can provide a code mirror plugin, to which we can provide an options object, and we can pass the different code block languages that we support.
00:18:39 So let's support CSS with a string of CSS.
00:18:43 We can also support just plain text by saying TXT.
00:18:48 We can do SQL.
00:18:51 And I think this is the way in which we have to do it.
00:18:52 There's no other way.
00:18:54 You could maybe map over these.
00:18:56 That will be a bit simpler, but for now, let's just write it one by one.
00:19:00 HTML is going to be equal to HTML.
00:19:03 Next, after that, we're going to have SAS, which is a variation of CSS.
00:19:10 We have SCSS.
00:19:12 That's another variation of CSS.
00:19:15 We can maybe add bash for bash scripts.
00:19:20 We can add JSON, always useful.
00:19:23 We can also add JavaScript.
00:19:25 So JS is JavaScript.
00:19:27 We can add TS for TypeScript.
00:19:31 We can add an empty string like this for unspecified.
00:19:34 We can add TSX for, let's say TypeScript, React.
00:19:42 And let's do JSX for JavaScript, React, like this.
00:19:47 This is enough of code block languages.
00:19:49 Let's make sure to properly add a line right here after this one.
00:19:53 And we can also add auto load language support, set it to true, so we can auto load additional languages.
00:20:00 And we can add a code mirror extension equal to theme.
00:20:05 So here we're actually allowing the code editor to be of the same theme as our application, be that light or dark.
00:20:14 Also, you know that we'll be writing this in Markdown.
00:20:16 So would it not be cool to see the plain version of that Markdown, not styled?
00:20:21 Like for example, when you do this, This is basically a hash and then the title.
00:20:26 So I want to see an unstyled version so I can copy it and paste it into another Markdown editor.
00:20:32 To do that, I can run a diff source plugin and provide a viewMode equal to rich-text and a diff Markdown set to an empty string.
00:20:45 And we can add a comma after it.
00:20:48 I know this was quite a long setup, but now very soon you'll see how powerful of an editor this will be for both of our questions as well as the answers
00:20:56 to those questions.
00:20:57 But for the time being, we can test out some of the different functionalities, such as maybe creating links.
00:21:03 But I mean, check this out.
00:21:04 This is not really looking great.
00:21:06 It appears right here at the bottom left, goes over the content.
00:21:10 It's almost as this window doesn't have any styling.
00:21:16 And that's what made me realize that this toolbar doesn't have any styling as well, because otherwise the elements would nicely appear one below another,
00:21:26 or one next to another.
00:21:27 So let's figure out why the styles are not getting applied.
00:21:30 We have imported the dark editor styles, but wait, weren't there another styles?
00:21:35 If I head over to the docs, Getting started.
00:21:39 There we go.
00:21:40 These were the styles that I was thinking about.
00:21:42 I think we removed them back when I moved this back into the question form.
00:21:47 And then I forgot to add the styles here as well.
00:21:49 So let's just add the styles above our dark editor by importing the add MDX editor forward slash editor forward slash style.css.
00:21:58 And there we go.
00:21:59 This is much better.
00:22:00 And looks like our imports are complaining a bit.
00:22:03 So I'll move this above and we are Good.
00:22:06 It looks even better on desktop.
00:22:08 We can have different headings.
00:22:10 So we can say Heading 1. Since I know Markdown, I can immediately create a Heading 2 or a Heading 3. But for people that don't know Markdown,
00:22:21 they can also use these different elements from the toolbar to undo, redo, and do all sorts of different things.
00:22:29 Pretty cool.
00:22:30 Even checkboxes are supported.
00:22:32 Of course, links now look significantly better because we are actually importing the CSS.
00:22:37 We can create different tables, we can move those tables, I mean, it is a super, super powerful editor, but what we needed for the most is this little
00:22:47 thing right here.
00:22:48 Insert code block.
00:22:50 I can say something like, hi there, I'm struggling with this error.
00:22:56 By the way, don't start asking for help like this, just be specific.
00:23:00 Say what you've tried before and then say why didn't work and what you want to achieve.
00:23:06 Let's say that we thought that there should be a React query icon right here.
00:23:11 Instead, it's just giving us the default dev icon icon.
00:23:14 So I can say the React query dev icon icon is not getting rendered.
00:23:25 Instead, I'm getting a default dev icon icon.
00:23:31 There we go.
00:23:32 And now we can provide a block of code explaining our code so people can actually debug it.
00:23:38 So if I head back over to where we have that utility for the icons, I think that was in the tag.
00:23:43 So if I go into the tag card, we have the GetDevIconClassName, where we have different icons, and then we can maybe paste this block of code,
00:23:52 just as an example.
00:23:53 So I'll go here, and I'll click this, and check this out.
00:23:57 We have a complete code editor within our Markdown editor.
00:24:01 We can choose TypeScript React, paste it, and we get complete syntax highlighting.
00:24:07 Isn't that cool?
00:24:09 Also, check this out.
00:24:11 Works on both light and dark mode with proper consistent highlighting.
00:24:18 As you can see, it changes immediately.
00:24:20 Notice how this is blue right now, but if we go to dark, it turns into an orange color.
00:24:25 I mean, it's so great to have this working properly.
00:24:27 Can anyone help?
00:24:30 There we go.
00:24:30 This is a very properly written question.
00:24:33 And people are just going to say, hey, dummy, the React query icon doesn't exist under the dev icon library.
00:24:38 But hey, now we can at least ask proper questions.