Monaco Editor for React - use the monaco-editor in any React application without needing to use webpack (or rollup/parcel/etc) configuration files / plugins
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
React hook for accessing the Monaco Editor instance and its APIs across components without needing to pass instances through props.
React hook that provides access to the Monaco Editor instance once it's loaded.
/**
* React hook for accessing the Monaco Editor instance
* @returns Monaco instance or null if not yet loaded
*/
function useMonaco(): Monaco | null;
type Monaco = typeof monaco;Usage Examples:
import React, { useEffect } from "react";
import { useMonaco } from "@monaco-editor/react";
// Basic Monaco instance access
function MonacoStatus() {
const monaco = useMonaco();
useEffect(() => {
if (monaco) {
console.log("Monaco Editor is ready!");
console.log("Available languages:", monaco.languages.getLanguages());
}
}, [monaco]);
return (
<div>
Monaco Status: {monaco ? "Loaded" : "Loading..."}
</div>
);
}
// Custom language configuration
function CustomLanguageSetup() {
const monaco = useMonaco();
useEffect(() => {
if (!monaco) return;
// Register custom language
monaco.languages.register({ id: 'myLang' });
// Define language syntax
monaco.languages.setMonarchTokensProvider('myLang', {
tokenizer: {
root: [
[/\[error.*/, "custom-error"],
[/\[notice.*/, "custom-notice"],
[/\[info.*/, "custom-info"],
[/\[[a-zA-Z 0-9:]+\]/, "custom-date"],
]
}
});
// Configure language features
monaco.languages.setLanguageConfiguration('myLang', {
brackets: [
['[', ']'],
['(', ')'],
['{', '}']
],
autoClosingPairs: [
{ open: '[', close: ']' },
{ open: '(', close: ')' },
{ open: '{', close: '}' },
]
});
}, [monaco]);
return <div>Custom language configured</div>;
}Use the Monaco instance to define and manage custom themes across your application.
// Theme definition interface (from Monaco Editor)
interface ThemeDefinition {
base: 'vs' | 'vs-dark' | 'hc-black';
inherit: boolean;
rules: TokenThemeRule[];
colors: { [colorId: string]: string };
}
interface TokenThemeRule {
token: string;
foreground?: string;
background?: string;
fontStyle?: string;
}Theme Examples:
import React, { useEffect } from "react";
import { useMonaco } from "@monaco-editor/react";
function ThemeManager() {
const monaco = useMonaco();
useEffect(() => {
if (!monaco) return;
// Define custom dark theme
monaco.editor.defineTheme('customDark', {
base: 'vs-dark',
inherit: true,
rules: [
{ token: 'comment', foreground: '6A9955', fontStyle: 'italic' },
{ token: 'keyword', foreground: '569CD6', fontStyle: 'bold' },
{ token: 'string', foreground: 'CE9178' },
{ token: 'number', foreground: 'B5CEA8' },
],
colors: {
'editor.background': '#1e1e1e',
'editor.foreground': '#d4d4d4',
'editorLineNumber.foreground': '#858585',
'editorCursor.foreground': '#ffffff',
'editor.selectionBackground': '#264f78',
'editor.lineHighlightBackground': '#2d2d30',
}
});
// Define custom light theme
monaco.editor.defineTheme('customLight', {
base: 'vs',
inherit: true,
rules: [
{ token: 'comment', foreground: '008000', fontStyle: 'italic' },
{ token: 'keyword', foreground: '0000ff', fontStyle: 'bold' },
{ token: 'string', foreground: 'a31515' },
{ token: 'number', foreground: '098658' },
],
colors: {
'editor.background': '#ffffff',
'editor.foreground': '#000000',
'editorLineNumber.foreground': '#237893',
'editor.selectionBackground': '#add6ff',
'editor.lineHighlightBackground': '#f0f0f0',
}
});
}, [monaco]);
const applyTheme = (themeName) => {
if (monaco) {
monaco.editor.setTheme(themeName);
}
};
return (
<div>
<button onClick={() => applyTheme('customDark')}>Dark Theme</button>
<button onClick={() => applyTheme('customLight')}>Light Theme</button>
<button onClick={() => applyTheme('vs-dark')}>VS Dark</button>
<button onClick={() => applyTheme('vs')}>VS Light</button>
</div>
);
}Configure TypeScript/JavaScript language services and other language features.
// Language service configuration interfaces (from Monaco Editor)
interface LanguageServiceDefaults {
setCompilerOptions(options: CompilerOptions): void;
setDiagnosticsOptions(options: DiagnosticsOptions): void;
addExtraLib(content: string, filePath?: string): void;
setEagerModelSync(value: boolean): void;
}Language Service Examples:
import React, { useEffect } from "react";
import { useMonaco } from "@monaco-editor/react";
function TypeScriptConfiguration() {
const monaco = useMonaco();
useEffect(() => {
if (!monaco) return;
// Configure TypeScript compiler options
monaco.languages.typescript.typescriptDefaults.setCompilerOptions({
target: monaco.languages.typescript.ScriptTarget.ES2020,
allowNonTsExtensions: true,
moduleResolution: monaco.languages.typescript.ModuleResolutionKind.NodeJs,
module: monaco.languages.typescript.ModuleKind.CommonJS,
noEmit: true,
esModuleInterop: true,
jsx: monaco.languages.typescript.JsxEmit.React,
reactNamespace: "React",
allowJs: true,
typeRoots: ["node_modules/@types"]
});
// Configure diagnostics
monaco.languages.typescript.typescriptDefaults.setDiagnosticsOptions({
noSemanticValidation: false,
noSyntaxValidation: false,
noSuggestionDiagnostics: false
});
// Add type definitions
const reactTypes = `
declare module "react" {
export interface FC<P = {}> {
(props: P & { children?: ReactNode }): ReactElement | null;
}
export type ReactNode = ReactElement | string | number | boolean | null | undefined;
export interface ReactElement<P = any> {
type: string | FC<P>;
props: P;
key: string | number | null;
}
}
`;
monaco.languages.typescript.typescriptDefaults.addExtraLib(
reactTypes,
'file:///node_modules/@types/react/index.d.ts'
);
// Enable eager model sync for better performance
monaco.languages.typescript.typescriptDefaults.setEagerModelSync(true);
}, [monaco]);
return <div>TypeScript language service configured</div>;
}
function CustomCompletionProvider() {
const monaco = useMonaco();
useEffect(() => {
if (!monaco) return;
// Register custom completion provider
const disposable = monaco.languages.registerCompletionItemProvider('javascript', {
provideCompletionItems: (model, position) => {
const suggestions = [
{
label: 'console.log',
kind: monaco.languages.CompletionItemKind.Function,
documentation: 'Log a message to the console',
insertText: 'console.log($1);',
insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
range: {
startLineNumber: position.lineNumber,
endLineNumber: position.lineNumber,
startColumn: position.column,
endColumn: position.column
}
},
{
label: 'async function',
kind: monaco.languages.CompletionItemKind.Snippet,
documentation: 'Create an async function',
insertText: 'async function ${1:functionName}(${2:params}) {\n\t$0\n}',
insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
range: {
startLineNumber: position.lineNumber,
endLineNumber: position.lineNumber,
startColumn: position.column,
endColumn: position.column
}
}
];
return { suggestions };
}
});
// Cleanup on unmount
return () => disposable.dispose();
}, [monaco]);
return <div>Custom completion provider registered</div>;
}Configure Monaco Editor settings that apply to all editor instances.
// Global configuration options
interface GlobalMonacoConfig {
/** Configure default options for all editors */
setGlobalOptions(options: editor.IEditorOptions): void;
/** Register global commands */
addGlobalCommand(keybinding: number, handler: () => void): void;
/** Configure language features globally */
configureLanguageGlobally(languageId: string, config: any): void;
}Global Configuration Examples:
import React, { useEffect } from "react";
import { useMonaco } from "@monaco-editor/react";
function GlobalMonacoSetup() {
const monaco = useMonaco();
useEffect(() => {
if (!monaco) return;
// Set global editor defaults
monaco.editor.EditorOptions.fontSize.defaultValue = 14;
monaco.editor.EditorOptions.fontFamily.defaultValue = 'Monaco, Menlo, monospace';
monaco.editor.EditorOptions.lineHeight.defaultValue = 1.6;
// Configure global key bindings
const formatAction = {
id: 'format-document',
label: 'Format Document',
keybindings: [monaco.KeyMod.Shift | monaco.KeyMod.Alt | monaco.KeyCode.KeyF],
run: (editor) => {
editor.getAction('editor.action.formatDocument').run();
}
};
// Register global actions
monaco.editor.addEditorAction(formatAction);
// Configure language defaults
const jsDefaults = monaco.languages.typescript.javascriptDefaults;
jsDefaults.setCompilerOptions({
allowNonTsExtensions: true,
allowJs: true
});
}, [monaco]);
return <div>Global Monaco configuration applied</div>;
}Create a Monaco context provider for app-wide access.
import React, { createContext, useContext, useEffect, useState } from "react";
import { useMonaco } from "@monaco-editor/react";
const MonacoContext = createContext(null);
export function MonacoProvider({ children }) {
const monaco = useMonaco();
const [isConfigured, setIsConfigured] = useState(false);
useEffect(() => {
if (!monaco || isConfigured) return;
// Apply global configuration
monaco.editor.defineTheme('appTheme', {
base: 'vs-dark',
inherit: true,
rules: [],
colors: {
'editor.background': '#1a1a1a',
}
});
setIsConfigured(true);
}, [monaco, isConfigured]);
return (
<MonacoContext.Provider value={{ monaco, isConfigured }}>
{children}
</MonacoContext.Provider>
);
}
export function useAppMonaco() {
const context = useContext(MonacoContext);
if (!context) {
throw new Error('useAppMonaco must be used within MonacoProvider');
}
return context;
}Only render Monaco-dependent components after Monaco is loaded.
import React from "react";
import { useMonaco } from "@monaco-editor/react";
import Editor from "@monaco-editor/react";
function ConditionalEditor() {
const monaco = useMonaco();
if (!monaco) {
return <div>Loading Monaco Editor...</div>;
}
return (
<div>
<Editor
height="400px"
language="javascript"
defaultValue="// Monaco is ready!"
/>
</div>
);
}