Editor is a format-aware TipTap component (HTML or Markdown) with:
- Bubble menu formatting controls
- Slash commands
- Links, images (including ALT editing), and tables
Component export:
Editorfromsrc/components/ui/editor.tsx
npm install
npm run devBuild:
npm run build
npm run registry:buildnpm run build now:
- builds client assets
- builds SSR entry
- prerenders
/intodist/index.html
- Uses shadcn-style Tailwind utility classes and semantic tokens.
- Uses local shadcn component files where needed (
button,tabs). - Demo/base styles live in
src/index.css.
Registry source:
registry.jsonregistry/default/editor/*
Build registry artifacts:
npm run registry:buildGenerated files:
public/r/registry.jsonpublic/r/editor.json
Install this component directly from GitHub:
npx shadcn@latest add https://editor.pagescms.org/r/editor.jsonimport { useState } from "react";
import { Editor } from "@/components/ui/editor";
export function Example() {
const [value, setValue] = useState("");
return (
<Editor
value={value}
onChange={setValue}
/>
);
}Notes:
- Default
formatis"html". formatsupports"markdown"and"html".classNameapplies classes to the root wrapper (cn-editor).editorClassNameapplies classes to the WYSIWYG surface only.
Editor supports both pre-uploaded URLs and local file uploads.
- Slash image selector:
- Return
{ kind: "url", src }fromonRequestImageif the image is already uploaded. - Return
{ kind: "file", file }fromonRequestImageto run optimistic upload flow.
- Return
- Paste/drop:
- Enable with
enableImagePasteDrop. - Provide
onUploadImageto upload files and return final{ src }. - Optional fallback with
imageFallback="data-url"for base64 insertion.
- Enable with
- Pending uploads:
- Use
onPendingUploadsChangeto disable autosave/publish while uploads are in progress.
- Use
Editor is intentionally focused on rich-text editing. If you need a source view, keep a single shared value state and switch between Editor and your own text input.
import { useState } from "react";
import { Editor } from "@/components/ui/editor";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { Textarea } from "@/components/ui/textarea";
type View = "editor" | "source";
export function EditorWithSourceToggle() {
const [view, setView] = useState<View>("editor");
const [value, setValue] = useState("");
return (
<Tabs value={view} onValueChange={(next) => setView(next as View)} className="w-full">
<TabsList>
<TabsTrigger value="editor">Editor</TabsTrigger>
<TabsTrigger value="source">Source</TabsTrigger>
</TabsList>
<TabsContent value="editor">
<Editor
value={value}
onChange={setValue}
format="markdown"
/>
</TabsContent>
<TabsContent value="source">
<Textarea
value={value}
onChange={(event) => setValue(event.target.value)}
className="min-h-64 font-mono"
/>
</TabsContent>
</Tabs>
);
}| Prop | Type | Default | Description |
|---|---|---|---|
value |
string |
"" |
Current editor content. |
onChange |
(value: string) => void |
- | Called whenever content changes. |
format |
"markdown" | "html" |
"html" |
Parsing and output format. |
disabled |
boolean |
false |
Disables editing and toolbar actions. |
enableImages |
boolean |
true |
Enables image-related behaviors (slash command and image actions). |
enableImagePasteDrop |
boolean |
false |
Enables image file paste and drag/drop insertion. |
onRequestImage |
(ctx) => { kind: "url", src, alt?, title? } | { kind: "file", file, alt?, title? } | null | Promise<...> |
- | Called by Image slash command. Return a URL result for already-uploaded images or a file result for local upload flow. |
onUploadImage |
(file, ctx) => { src, alt?, title? } | null | Promise<...> |
- | Upload hook for local files (paste/drop/slash file). |
imageFallback |
"data-url" | "prompt-url" | "none" |
"prompt-url" |
Fallback when no callback inserts an image. |
maxImageBytes |
number |
1000000 |
Max file size used by "data-url" fallback. |
onPendingUploadsChange |
(count: number) => void |
- | Receives pending optimistic upload count. |
className |
string |
- | Extra classes for the root wrapper (cn-editor). |
editorClassName |
string |
- | Extra classes for the WYSIWYG surface. |
...props |
HTMLAttributes<HTMLDivElement> |
- | Forwarded to the root container. |
Use Git integration with these settings:
- Framework preset:
None - Root directory:
editor - Build command:
npm run build - Build output directory:
dist
This deploys the prerendered static HTML + hydrated React demo.