• Stars
    star
    101
  • Rank 338,166 (Top 7 %)
  • Language
    TypeScript
  • License
    MIT License
  • Created over 4 years ago
  • Updated 9 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Use πŸ—’οΈ monaco-editor in any βš›οΈ React app with simple hooks 🎣

πŸ—’οΈ use-monaco

This works with monaco-editor v0.21.2, and has NOT been updated for more recent versions.

npm

  • Simple hooks to use monaco-editor in any React app
  • No webpack plugins or AMD loaders required while maintaining full support for monaco web-workers without build tools
  • Easy API for working with web-workers.
  • Headless (just hooks), so you can:
    • Render however you want (it's just a single div, style it as you wish)
    • Decide how to render loading state (loading prop is returned by the useMonacoEditor and useMonaco hooks)
    • Work with underlying monaco objects like monaco API, the editor instance, and the text model instances
    • Use the above objects in useEffect hooks together to wire up custom functionality
  • Inspired by @monaco-editor/react.
  • Availabe on Pika CDN. There is a simple example of using this below, (no build tool, just copy this html anywhere and you are golden).
  • Built with pkger
  • In progress: docs about setting up language providers, example with parcel, next.js, vanilla

Documentation

Example

<body>
  <div id="root"></div>
  <script defer type="module">
    import {
      useMonacoEditor,
      prettier,
    } from 'https://cdn.pika.dev/[email protected]';
    import themes from 'https://cdn.pika.dev/[email protected]/themes';
    import * as React from 'https://cdn.pika.dev/react';
    import ReactDOM from 'https://cdn.pika.dev/react-dom';
    import htm from 'https://cdn.pika.dev/htm';
    const html = htm.bind(React.createElement);

    let Editor = () => {
      const { containerRef, monaco, model, loading } = useMonacoEditor({
        plugins: [prettier(['graphql'])],
        themes,
        theme: 'github',
        path: 'model.graphql',
        defaultValue: ['type Query {}'].join('\n'),
      });

      return html`<div
        ref=${containerRef}
        style=${{ height: 800, width: 600 }}
      />`;
    };

    ReactDOM.render(html`<${Editor} />`, document.getElementById('root'));
  </script>
</body>

Installation

You can get use-monaco from yarn or npm:

yarn add use-monaco

#or
npm install use-monaco

or use it directly from a CDN like Pika or unpkg in browsers with ESM support:

import { useMonacoEditor } from 'https://cdn.pika.dev/use-monaco';
import { initialize } from 'https://cdn.pika.dev/use-monaco/worker';

import { useMonacoEditor } from 'https://unpkg.com/use-monaco';

useMonacoEditor

Single hook to get all monaco-editor documentation functionality for one editor instance that wires up the three underlying hooks useMonaco, useTextModel and useEditor. If you only need a single editor, useMonacoEditor will work fine for you as it allows you to send options for all underlying hooks. For multiple editors, you would need to use some of the other hooks like useTextModel and useEditor. Most props are optional, coming with sensible defaults. useMonacoEditor accepts the props for all these hooks and returns everything they return.

function useMonacoEditor(options: {
 ...useMonacoOptions, // see below
 ...useTextModelOptions,
 ...useEditorOptions
}): {
  // assign to a div to render editor
  containerRef: React.MutableRefObject<HTMLDivElement>;
  editor: monaco.editor.IStandaloneCodeEditor;
  loading: boolean;
  monaco: typeof monaco;
  model: monaco.editor.ITextModel;
};
  • useMonaco

    • Provides you with monaco-editor API namespace to work with, includes monaco.languages, monaco.editor
    • Extended monaco.worker API with for easy support for adding custom workers and languages
    • Added monaco.plugins API to enable functionality by simply registering plugins
    • Plugins for prettier, typings, graphql available out-of-the-box (backed by web-workers and completely opt-in)
    • Dedupes the request for the loading monaco-editor from the CDN across multiple calls
function useMonaco(options: {
  // plugins to enable, eg. [prettier(["graphql", "typescript"]), typings(), ...]
  plugins?: monaco.plugin.IPlugin[];
  paths?: {
    // Custom CDN link for monaco-editor
    vs?: string;
  };
  // A collection of themes that can be selected from
  themes?: { [key: string]: monaco.editor.IStandaloneThemeData };
  // Function will fire when monaco loads from the CDN (this is where you can load custom workers and languages)
  onLoad?: (monaco: typeof monaco) => (() => void) | void;
  // Function will fire when the theme is changed by any editor
  onThemeChange?: (newTheme: string, monaco: typeof monaco) => void;
}): {
  loading: boolean;
  monaco: typeof monaco;
};
  • useEditor

    • Creates a monaco code editor which provides a containerRef that you will need to render as a div in your React app.
    • Uses models to show content.
    • Can be used multiple times with multiple models to get a bunch of editors
    • Controlled and uncontrolled based on how you control the model
    • Returns editor instance so that you can play with it
function useEditor(options: {
  // must provide monaco instance from useMonaco hook
  monaco?: typeof monaco;

  // model to assign to editor (get this from useTextModel hook)
  model?: monaco.editor.ITextModel;

  // theme for the editor (can be a custom one or name of theme providede to useMonaco hook) [theme will change across editors]
  theme?: string | monaco.editor.IStandaloneThemeData;

  // Function to wire when the value of the model changes
  onChange?: (
    newValue: string,
    editor: monaco.editor.IStandaloneCodeEditor,
    event: monaco.editor.IModelContentChangedEvent,
    monaco: typeof monaco
  ) => void;

  // Function is fired before editor is created (can return options to be provided to the editor)
  editorWillMount?: (
    monaco: typeof monaco
  ) => monaco.editor.IEditorOptions | void;

  // Function is fired after editor is created, return disposables that will be cleared on unmount
  editorDidMount?: (
    editor: monaco.editor.IStandaloneCodeEditor,
    monaco: typeof monaco
  ) => monaco.IDisposable[] | Promise<void> | void;

  // Override internal monaco services for the editor
  overrideServices?:
    | monaco.editor.IEditorOverrideServices
    | ((monaco: typeof monaco) => monaco.editor.IEditorOverrideServices);
  options?: monaco.editor.IEditorOptions;
}): {
  // assign this ref to a div with some styling and you are good to go
  containerRef: React.MutableRefObject<HTMLDivElement>;
  editor: monaco.editor.IStandaloneCodeEditor;
};
  • useTextModel

    • Create models to be viewed on monaco editors
    • Create more that one for different files to show across editors
    • Basically a super simple file system backed by monaco models
    • Use path to select model
function useTextModel(options: {
  // must provide monaco instance from useMonaco hook
  monaco?: typeof monaco;
  // just the initial value for uncontrolled model
  defaultValue?: string;
  // or value for controlled mode
  value?: string;
  // or dictionary of paths to the content of the files (path is used to determine value of the file)
  files?: { [key: string]: string };

  // path of the model you want to select, a new model is created if one doesn't exist
  path?: string;
  // language of the model (can normally be interpreted from path extension)
  language?: string;
  // create models for all files eagerly
  syncAllFiles?: boolean;
}): monaco.editor.ITextModel;

Example of using the hooks separately,

<body>
  <div id="root"></div>
  <script defer type="module">
    import {
      useMonaco,
      useEditor,
      useTextModel,
      prettier,
    } from 'https://cdn.pika.dev/[email protected]';
    import themes from 'https://cdn.pika.dev/[email protected]/themes';
    import * as React from 'https://cdn.pika.dev/react';
    import ReactDOM from 'https://cdn.pika.dev/react-dom';
    import htm from 'https://cdn.pika.dev/htm';
    const html = htm.bind(React.createElement);

    let Editor = () => {
      const { monaco, loading } = useMonaco({
        plugins: [prettier(['graphql'])],
        themes,
        theme: 'github',
      });

      const model = useTextModel({
        path: 'model.graphql',
        defaultValue: ['type Query {}'].join('\n'),
      });
      const { containerRef } = useEditor({ monaco, model });

      return html`<div
        ref=${containerRef}
        style=${{ height: 800, width: 600 }}
      />`;
    };

    ReactDOM.render(html`<${Editor} />`, document.getElementById('root'));
  </script>
</body>

Working with workers

monaco-editor is already using a bunch of workers for languages like typescript, html, css as well some editor services. You can add custom workers to offload work for your custom functionality from the main thread. This can ensure that the UI doesn't lag or your typing experience is not harmed by these computations like linting, etc. happening. You can register workers in your components using the monaco.worker api available on the main thread. Workers are also used to provide core language functionality to monaco by registering providers to things like hover, auto-complete, validation, formatting, etc. There is an easy API to enable this as well provided by use-monaco.

// Register the worker in onLoad or in an effect (remember to cleanup)
monaco.worker.register({
  label: 'babel',
  src: () =>
    new Worker('./path.to.worker.js', {
      // to use esm syntax in worker
      type: 'module',
    }),
});

You worker needs to follow a simple interface to work with use-monaco.

import { initialize, BaseWorker } from 'use-monaco/worker';
// or https://unpkg.com/use-monaco/dist/esm/worker.js to load from CDN

// Extend BaseWorker to get the ability to use the monaco models on the worker side.
class BabelWorker extends BaseWorker {
  transpile(path) {
    const text = this.getText(path);
    // do some hard work;
    const transpiled = Babel.transpile(text);
    return transpiled;
  }
}

// call initialize to setup the worker to communicate with monaco
initialize('babel', BabelWorker);

And then you can use the worker on the main thread by retreiving the worker using its label and syncing the models you would need on the backend. You can then use the functions that were exposed by the worker as async functions.

// You get a proxy to the registered worker with the contents
// of the files that you mention here synced. The worker has to extend a simple interface
const worker = await monaco.worker.get('babel', 'model1.ts', 'model2.ts');
const something = await worker.transpile('model.ts');

More Repositories

1

vinxi

The Full Stack JavaScript SDK
JavaScript
2,062
star
2

magiql

🌐 πŸ’« Simple and powerful GraphQL Client, love child of react-query ❀️ relay
TypeScript
199
star
3

vite-next

NextJS alternative built on top of Vite, React Router, React Query
TypeScript
46
star
4

magiql-browser

Web-based Browser and IDE for GraphQL (based on monaco-editor), useful for playgrounds for GraphQL servers
TypeScript
28
star
5

fully-react

TypeScript
26
star
6

lepton-footprint-extraction

Automated building polygon extraction from satellite imagery
Python
15
star
7

solid-mdx

MDX support for SolidJS
TypeScript
14
star
8

editable-jsx

TypeScript
9
star
9

scoreboard-api

Live NBA scores and boxscores API (data scraped from ESPN website)
Python
6
star
10

game-tools

TypeScript
6
star
11

excalidraw-studio

TypeScript
5
star
12

solid-three

TypeScript
4
star
13

react-box

A simple yet super powerful Box primitive (with others) for React powered by styled-system and framer-motion
TypeScript
4
star
14

vinxi-island

TypeScript
3
star
15

pkger

πŸ“¦ Simple (yet powerful) build tool for any target (browser/node/cli) βš™ Powered by rollup, babel, gluegun, typescript
TypeScript
3
star
16

create-hook-context

A better and more powerful React.createContext powered by hooks 🎣
TypeScript
2
star
17

scoreboard

Desktop App for NBA live scores and boxscores
JavaScript
2
star
18

awesome-rsc

2
star
19

vinxi-trpc

JavaScript
2
star
20

elf

YAML syntax-based task runner
TypeScript
1
star
21

ocean-theme

🌊Oceanic theme for VS Code
JavaScript
1
star
22

todo-wallpaper

β˜‘οΈ Todo App for MacOS tray with auto-updating desktop wallpaper
JavaScript
1
star
23

tanstack-router-app

HTML
1
star
24

solid-starter

Created with StackBlitz ⚑️
TypeScript
1
star
25

tavern

Super simple framework for service-based architecture in monolithic servers
TypeScript
1
star
26

next-monaco-editor

JavaScript
1
star
27

test-solid-6

TypeScript
1
star
28

game-buildings

TypeScript
1
star
29

map-gsheets

TypeScript
1
star
30

examples

An example project showing how the <TLDraw/> component can be used and controlled in a React app.
TypeScript
1
star
31

sapiens

TypeScript
1
star
32

vite-react-starter

TypeScript
1
star
33

playground

TypeScript
1
star
34

vitap

The Vite App framework
JavaScript
1
star
35

reactscript

JavaScript
1
star
36

nba-stats

Java wrapper for NBA Stats API
Java
1
star
37

malaria-index

TypeScript
1
star
38

bluwy-whyframe-vq5xek

Created with StackBlitz ⚑️
CSS
1
star
39

solid-start-hybrid

TypeScript
1
star
40

game-editor-monorepo

TypeScript
1
star