ESLint MDX plugin and remark-lint
We have added linting for our code by creating a custom ESLint 9 flat config, next we will add linting for our MDX (markdown) content
Adding and configuring the MDX ESLint plugin
First we install the ESLint MDX plugin:
Then we can update our ESLint configuration and to add the MDX plugin:
Line 12: we import the eslint plugin mdx
Lines 130 to 153: we create a mdxConfig (config array) with two configurations one for mdx content and a second config for the content of markdown codeblocks (which you might have in your mdx documents)
Line 139: Because off performance problems (linting process was very long) I decided to disable linting of codeblocks content by setting the lintCodeBlocks option in the mdx config to false. I left the second config MDX config (for codeblocks content) unchanged as we might use it in the future with a faster parser. If you want to lint the content of codeblocks, make sure to set the lintCodeBlocks option to true
Line 160 we add the mdxConfig to the config export
Side note: eslint-mdx already had a support flat config PR #468 merged in Aug. 2023 😮
Markdown linting using remark-lint
Now that we have added MDX ESLint plugin to our Next.js 15 project, we can easily add the remark-lint plugin to our setup, all we need to do is create a configuration file and import a few recommend rule sets for markdown linting
First we need to add few more remark-lint dependencies, these remark-lint presets will each add different rules (plugins) to our markdown linting setup:
By installing them using the following command:
Then we create a remark linting configuration file, with the following content:
The first 3 imports are presets with recommended rules, we then use them in the plugins config
The other imports are single rules we want to configure, we then use them in the plugins config, the configuration for each rule consists of an array where the first value is the rule and the second value is the configuration we want to apply, here is an explanation for the ones I suggest adding (but feel free to configure them differently, to match your use case):
- for the maximum headings length rule, we use 1,100, which tells remark-lint that headings should not have a length lower than 1 character and not greater than 100
- for the unordered lists marker style, we set it to consistent, when set to consistent, the rule will check which marker is the most used and then enforce it everywhere, this is nice because it means it is flexible and will adapt to what you use, do you often define lists by using an asterisk (*), then this is what the rule will enforce or do you prefer using a hyphen-minus (-) then that is what will get enforced, consistent is nice setting to ensure that your styling is consistent based on what you use the most
- the no undefined references will check if you are using undefined references, we add
!NOTE
and a few more to the allow list as those are exceptions to the rule (those get used by the plugin that we will add in the rehype-github-alerts page, and the last two are for the remark-gfm task lists) - images and links can have a title, the title needs to be enclosed by two symbols, here we define that we want to use a single quote (') for titles, so an image with a title would look like this
![IMAGE_ALT_TEXT](IMAGE_PATH 'IMAGE_TITLE')
, if you prefer, you can enforce double quotes or even set it toconsistent
to let it choose the one you use most - the maximum line length rule I disabled it, some people like to stick to 80 characters, I try to keep my lines short, but if a line is bigger, that's ok (for me), too
- finally, the list item spacing I disabled it because I had a lot of false positives when using that rule (I might re-enable it in the future and check if the problems I encountered got fixed)
Each remark lint presets package we just installed has installed a bunch of rules (packages) for us; each preset has a readme that list the rules they support:
And then we are already done, because the MDX plugin in our ESLint setup will automatically detect that we have a remark-lint configuration file. The potential warnings and errors from remark-lint will now get displayed along other ESLint messages every time you use the linting command (npm run lint
)
Testing our new lint command
After all the coding let's finally make a linting test, by using the following command in our terminal:
You probably notice that the linting command will output some errors and warnings in the terminal, those are files that got automatically created by the CNA or the sentry wizard (which we used earlier), but this is a good as it we can now also test our lint-fix command, that should automatically fix those errors for us
To fix the linting errors, use the following command:
And then run the lint command one more time and you should have no more warnings and errors as they got all fixed:
We had to do a lot of changes to get this new linting setup up and running, but I hope you agree with me that it was worth it, we have greatly improved our DX by automating both the linting of code and content, which will ensure we write cleaner code and ensure our markdown content is well formatted
remark-lint disable comments (in MDX)
To disable remark-lint rules we added by using MDX plugin you do NOT specify the rule name:
If you do, you will get an error like this:
Error: Definition for rule remark-lint-no-undefined-references was not found
Instead, you need to use mdx/remark for ANY rule you want to disable
In MDX files to add an eslint-disable comment, you need to use JSX comments
So if we do both things, we get something like this:
Clearing the ESLint cache
Because by default, we use a cache to speed up the linting process when using the npm run lint
command, we need to delete the cache manually after making changes to the .eslintrc.js
ESLint configuration file or the .remarkrc.mjs
remark-lint configuration file
To delete the cache manually, open the .next
folder in the root of your project, then go into the cache
folder and finally delete the eslint
file
If you do a lot of tests and don't want to delete the cache manually after every change, use the npm run lint-nocache
instead, until you are done testing, then delete the cache once and use the regular npm run lint command
one last time to do final test
Congratulations 🎉 you just added linting for all your MDX (markdown) content in your project's MDX pages
If you liked this post, please consider buying me a coffee ☕ or sponsor ❤️ me on GitHub, as it will help me create more content and keep it free for everyone