Getting started
The five-minute path from install to a workbench or editor on screen, in TypeScript, React or Vue
This page mounts a workbench, mounts a standalone editor, covers the React and Vue wrappers, adds one extension, and renders either on a server. Read Introduction first if you haven't — it covers installing codelet and what each package entry gives you.
#Mount a workbench
A workbench is a file tree, tabs, a command palette and a panel, backed by an in-memory
FileSystem.
import { FileSystem, Workbench } from "codelet/workbench";
const workbench = new Workbench({
parent: document.getElementById("app")!,
fs: new FileSystem({ "/README.md": "# Hello\n\nEdit me." }),
});#Mount an editor
Editor is a single code field. Construct one with the element to mount into; everything else
is optional.
import { Editor } from "codelet";
const editor = new Editor({
parent: document.getElementById("editor")!,
doc: "console.log('hello')\n",
lang: "ts",
});editor.value reads the current document. Call editor.destroy() when the element is removed,
to tear the view down.
#React and Vue
import { CodeEditor } from "codelet/react";
function Example() {
return (
<CodeEditor
initialValue={"console.log('hello')\n"}
lang="ts"
onChange={(value) => console.log(value)}
style={{ height: 300 }}
/>
);
}initialValue mounts uncontrolled: codelet owns the document from then on, and edits are
reported through onChange. Pass value instead for a controlled editor, kept in sync with
state on every render. onCreate hands you the underlying Editor once it exists, and
className/style reach the wrapping <div> (style is merged over the wrapper's own
positioning).
Both wrappers also accept lang, theme and readOnly, and update the live editor whenever
those props change.
#Adding an extension
A workbench with no extensions has no Search icon, no terminal, nothing beyond the built-in
explorer, tabs and command palette. Pass extensions to add them:
import { search } from "codelet/extensions/search";
import { FileSystem, Workbench } from "codelet/workbench";
const workbench = new Workbench({
parent: document.getElementById("app")!,
fs: new FileSystem({ "/README.md": "# Hello" }),
extensions: [search],
});That adds a Search icon to the activity bar, backed by codelet/extensions/search. Every
built-in extension is its own package entry, so a workbench only pays for the ones it imports.
#Server rendering
renderEditorHTML (from codelet) and renderWorkbench (from codelet/workbench/server)
return static markup — a stand-in editor or a full shell — rendered without CodeMirror ever
running. The client mounts Editor or Workbench over the same element and takes over from
there:
import { FileSystem } from "codelet/workbench";
import { renderWorkbench } from "codelet/workbench/server";
export default {
fetch() {
const shell = renderWorkbench({
fs: new FileSystem({ "/README.md": "# Hello\n\nEdit me." }),
open: "/README.md",
});
return new Response(
`<!doctype html><div id="app">${shell}</div><script type="module" src="/client.js"></script>`,
{ headers: { "content-type": "text/html;charset=utf-8" } },
);
},
};Workbench hydrates automatically when parent already has children, and mounts fresh
otherwise, so the same client code works for a server-rendered page or a client-only one.
Warning
open, active, view, panel, secondary, theme, extensions and themes are markup.
Pass the exact same values to renderWorkbench() on the server and to Workbench on the
client, or hydration has nothing to match and the shell rebuilds instead of taking over.
renderEditorHTML is the same idea for a standalone editor:
import { renderEditorHTML } from "codelet";
const html = renderEditorHTML({ doc: "console.log('hello')\n", lang: "ts" });#No CSS to import
codelet ships no CSS file. Every rule is inline, so mounting the editor or the workbench is one import, with nothing to add to a stylesheet or a build pipeline.