Bottom Line Up Front
I use a slightly modified version of the steps mentioned in this GitHub comment. Modifications were necessary because I use TailwindCSS with Next.js.
- YouTube recording (18 mins)
- Dev.to Embed:
Motivations
Monaco Editor is the open source editor used in VS Code, which itself is open source. I used to write my blogposts in VS Code, and as I make my own Dev.to CMS, I wanted to have all the familiar trappings of Monaco to help me out while I write.
Problems
However there are some issues we have to deal with:
- Monaco is framework agnostic, so it requires writing some React bindings.
- You could do it yourself, but also you could just skip that and use https://github.com/react-monaco-editor/react-monaco-editor
- Monaco is written for a desktop Electron app, not for a server-side rendered web app.
- This is solved by using
import dynamic from "next/dynamic"
and making Monaco a dynamic import.
- This is solved by using
- Monaco also wants to offload syntax highlighting to web workers, and we need to figure that out
- Next.js doesn't want any dependencies importing CSS from within
node_modules
, as this assumes a bundler and loader setup (e.g. webpack) and can have unintentional global CSS side effects (all global CSS is intended to be in_app.js
).- we can re-enable this with
@zeit/next-css
andnext-transpile-modules
- we can re-enable this with
We can solve this with a solution worked out by Elliot Hesp on GitHub and a config from Joe Haddad of the Next.js team.
Solution
The solution I use is informed by my usage of Tailwind CSS, which requires a recent version of PostCSS, which @zeit/next-css
only has at 3.0 (because it is deprecated and not maintained).
I also use TypeScript, which introduces a small wrinkle, because Monaco Editor attaches a MonacoEnvironment
global on the window
object - I just @ts-ignore
it.
// next.config.js
const MonacoWebpackPlugin = require("monaco-editor-webpack-plugin");
const withTM = require("next-transpile-modules")([
// `monaco-editor` isn't published to npm correctly: it includes both CSS
// imports and non-Node friendly syntax, so it needs to be compiled.
"monaco-editor"
]);
module.exports = withTM({
webpack: config => {
const rule = config.module.rules
.find(rule => rule.oneOf)
.oneOf.find(
r =>
// Find the global CSS loader
r.issuer && r.issuer.include && r.issuer.include.includes("_app")
);
if (rule) {
rule.issuer.include = [
rule.issuer.include,
// Allow `monaco-editor` to import global CSS:
/[\\/]node_modules[\\/]monaco-editor[\\/]/
];
}
config.plugins.push(
new MonacoWebpackPlugin({
languages: [
"json",
"markdown",
"css",
"typescript",
"javascript",
"html",
"graphql",
"python",
"scss",
"yaml"
],
filename: "static/[name].worker.js"
})
);
return config;
}
});
and then in your Next.js app code:
import React from "react";
// etc
import dynamic from "next/dynamic";
const MonacoEditor = dynamic(import("react-monaco-editor"), { ssr: false });
function App() {
const [postBody, setPostBody] = React.useState("");
// etc
return (<div>
{/* etc */}
<MonacoEditor
editorDidMount={() => {
// @ts-ignore
window.MonacoEnvironment.getWorkerUrl = (
_moduleId: string,
label: string
) => {
if (label === "json")
return "_next/static/json.worker.js";
if (label === "css")
return "_next/static/css.worker.js";
if (label === "html")
return "_next/static/html.worker.js";
if (
label === "typescript" ||
label === "javascript"
)
return "_next/static/ts.worker.js";
return "_next/static/editor.worker.js";
};
}}
width="800"
height="600"
language="markdown"
theme="vs-dark"
value={postBody}
options={{
minimap: {
enabled: false
}
}}
onChange={setPostBody}
/>
</div>)
}
Since I'm using Tailwind, I'm also using PostCSS, which also tries to eliminate Monaco's CSS. You have to tell it to ignore that:
// postcss.config.js
const purgecss = [
"@fullhuman/postcss-purgecss",
{
// https://purgecss.com/configuration.html#options
content: ["./components/**/*.tsx", "./pages/**/*.tsx"],
css: [],
whitelistPatternsChildren: [/monaco-editor/], // so it handles .monaco-editor .foo .bar
defaultExtractor: content => content.match(/[\w-/.:]+(?<!:)/g) || []
}
];
Catch up on the Dev.to CMS LiveStream!
- Day 1 - Setup Next.js and Tailwind UI, list posts through API routes - 90 mins
- Day 2 - setting up a Markdown Editor with Next.js, Tailwind UI, Highlight.js, React Hook Form, and React Query - 3 hours
- Quick Fix - How To Add Monaco Editor to a Next.js app - 18 mins
- Day 3 - Refactoring to Edit Existing Posts - 3 hours
- Day 4 - Polish Day! Implementing Notifications, Markdown preview, and programmatic Redirects, and Using Web Components in Next.js - 3 hours
- Instant GraphQL with OneGraph - Screenshares in Public with Sean Grove - refactoring handrolled Dev.to API access with OneGraph and GraphQL
- How and Why to Un-Reset Tailwind's CSS Reset
Top comments (17)
This is pretty fucking amazing. I've been meaning to work on something like this since all the syntax highlighting choices available are nowhere near as good as the Monaco editor. And I built the Shades of Purple theme because I like the right set of colors for the syntax — this will be a huge help for the new version of my site.
Thank you! 👌
Note that Monaco is not mobile supported. That turns me off, and had to fall back to CodeMirror...
doesnt monaco use codemirror under the hood?
I don't know. I just believe what it says in the website -- microsoft.github.io/monaco-editor/
ah. i've tried it briefly on mobile web sites. it can work. but maybe not as well as you might want.
This is fantastic. Took me 15 minutes to add to my own starter and get it running. Running pretty smoothly on localhost. Hosting to now breaks it a little bit.
github.com/jaakkolantero/monaco-st...
Big thanks!
thanks! looks like the production build is missing this file github.com/microsoft/vscode/blob/a...
I figured it out! postcss is clearing monaco's overflow guard styling since it thinks it is unused.
@swyx Greetings,
have you integrated monaco-languageclient i was getting this error
Can you share what steps to resolve this issue?
I resolved this error and then i am getting these errors. I mentioned the error and reference code in this github issue github.com/TypeFox/monaco-language... @swyx can you check what i was missing here?
Can someone please help me , I have tried this everything is working file but when i am doing npm run buld its showing webpack error.
Please Some one help me I want to upload it to digital ocean. I cant beacuse its not building .❤️
Error Image:
dev-to-uploads.s3.amazonaws.com/up...
Thanks for the awesome article!
With Next 9.3 this works perfect.
However, with Next 9.5 this stopped working.
Anyone an idea why that could be?
no idea. project not active already. what errors do you get?
It turns out, the solution can be much simpler :D
This also works fine with the latest Next.js
github.com/vercel/next.js/tree/can...
Do you know how to get JSX syntax highlighting working with react-monaco-editor and react? This has me stumped 🧐
i dont think i did anything special here. sorry.
looks not work in Next.js 10...
I had the same problem...
I ended up just using
@monaco-editor/react
.This works great without any hacky approach.