Getting Started
Installation
Install the package for your framework:
npm install @vizel/react
# or
pnpm add @vizel/react
# or
yarn add @vizel/reactnpm install @vizel/vue
# or
pnpm add @vizel/vue
# or
yarn add @vizel/vuenpm install @vizel/svelte
# or
pnpm add @vizel/svelte
# or
yarn add @vizel/sveltePeer Dependencies
Each framework package has peer dependencies on its respective framework:
@vizel/reactrequiresreact@^19andreact-dom@^19@vizel/vuerequiresvue@^3@vizel/svelterequiressvelte@^5
Quick Start
Use the Vizel component:
import { Vizel } from '@vizel/react';
import '@vizel/core/styles.css';
function App() {
return <Vizel placeholder="Type '/' for commands..." />;
}<script setup lang="ts">
import { Vizel } from '@vizel/vue';
import '@vizel/core/styles.css';
</script>
<template>
<Vizel placeholder="Type '/' for commands..." />
</template><script lang="ts">
import { Vizel } from '@vizel/svelte';
import '@vizel/core/styles.css';
</script>
<Vizel placeholder="Type '/' for commands..." />The Vizel component includes the editor, bubble menu, and slash command menu.
Import Styles
Import the default stylesheet in your application entry point:
import '@vizel/core/styles.css';This includes both CSS variables and component styles. For custom theming, see Theming.
Advanced Usage
To customize the editor, use individual components:
React
import { VizelEditor, VizelBubbleMenu, useVizelEditor } from '@vizel/react';
import '@vizel/core/styles.css';
function Editor() {
const editor = useVizelEditor({
placeholder: "Type '/' for commands...",
});
return (
<div className="editor-container">
<VizelEditor editor={editor} />
{editor && <VizelBubbleMenu editor={editor} />}
</div>
);
}
export default Editor;Vue
<script setup lang="ts">
import { VizelEditor, VizelBubbleMenu, useVizelEditor } from '@vizel/vue';
import '@vizel/core/styles.css';
const editor = useVizelEditor({
placeholder: "Type '/' for commands...",
});
</script>
<template>
<div class="editor-container">
<VizelEditor :editor="editor" />
<VizelBubbleMenu v-if="editor" :editor="editor" />
</div>
</template>Svelte
<script lang="ts">
import { VizelEditor, VizelBubbleMenu, createVizelEditor } from '@vizel/svelte';
import '@vizel/core/styles.css';
const editor = createVizelEditor({
placeholder: "Type '/' for commands...",
});
</script>
<div class="editor-container">
<VizelEditor editor={editor.current} />
{#if editor.current}
<VizelBubbleMenu editor={editor.current} />
{/if}
</div>Working with Content
Initial Content
Initialize the editor with Markdown or JSON format.
Using Markdown
Initialize with Markdown:
import { Vizel } from '@vizel/react';
<Vizel initialMarkdown="# Hello World\n\nStart editing..." /><script setup lang="ts">
import { Vizel } from '@vizel/vue';
</script>
<template>
<Vizel initialMarkdown="# Hello World\n\nStart editing..." />
</template><script lang="ts">
import { Vizel } from '@vizel/svelte';
</script>
<Vizel initialMarkdown="# Hello World\n\nStart editing..." />Or with the hook/composable/rune:
const editor = useVizelEditor({
initialMarkdown: '# Hello World\n\nStart editing...',
});const editor = useVizelEditor({
initialMarkdown: '# Hello World\n\nStart editing...',
});const editor = createVizelEditor({
initialMarkdown: '# Hello World\n\nStart editing...',
});Using JSON
Initialize with JSON format:
const editor = useVizelEditor({
initialContent: {
type: 'doc',
content: [
{
type: 'heading',
attrs: { level: 1 },
content: [{ type: 'text', text: 'Hello World' }],
},
{
type: 'paragraph',
content: [{ type: 'text', text: 'Start editing...' }],
},
],
},
});const editor = useVizelEditor({
initialContent: {
type: 'doc',
content: [
{
type: 'heading',
attrs: { level: 1 },
content: [{ type: 'text', text: 'Hello World' }],
},
{
type: 'paragraph',
content: [{ type: 'text', text: 'Start editing...' }],
},
],
},
});const editor = createVizelEditor({
initialContent: {
type: 'doc',
content: [
{
type: 'heading',
attrs: { level: 1 },
content: [{ type: 'text', text: 'Hello World' }],
},
{
type: 'paragraph',
content: [{ type: 'text', text: 'Start editing...' }],
},
],
},
});Getting Content
Access the editor content:
// Get JSON content
const json = editor.getJSON();
// Get Markdown content
import { getVizelMarkdown } from '@vizel/core';
const markdown = getVizelMarkdown(editor);
// Get HTML content
const html = editor.getHTML();
// Get plain text
const text = editor.getText();Listening to Changes
const editor = useVizelEditor({
onUpdate: ({ editor }) => {
const content = editor.getJSON();
console.log('Content updated:', content);
// Save to your backend
},
});const editor = useVizelEditor({
onUpdate: ({ editor }) => {
const content = editor.getJSON();
console.log('Content updated:', content);
// Save to your backend
},
});const editor = createVizelEditor({
onUpdate: ({ editor }) => {
const content = editor.getJSON();
console.log('Content updated:', content);
// Save to your backend
},
});Syncing Markdown Content
For two-way Markdown synchronization, use the dedicated hooks/composables/runes:
import { useVizelEditor, useVizelMarkdown, VizelEditor } from '@vizel/react';
function Editor() {
const editor = useVizelEditor();
const { markdown, setMarkdown, isPending } = useVizelMarkdown(() => editor);
// markdown updates automatically when editor content changes
// setMarkdown() updates editor content from markdown
return (
<div>
<VizelEditor editor={editor} />
<textarea
value={markdown}
onChange={(e) => setMarkdown(e.target.value)}
/>
{isPending && <span>Syncing...</span>}
</div>
);
}<script setup lang="ts">
import { Vizel } from '@vizel/vue';
import { ref } from 'vue';
const markdown = ref('# Hello World');
</script>
<template>
<!-- v-model:markdown provides two-way binding -->
<Vizel v-model:markdown="markdown" />
<textarea v-model="markdown" />
</template><script lang="ts">
import { Vizel } from '@vizel/svelte';
let markdown = $state('# Hello World');
</script>
<!-- bind:markdown provides two-way binding -->
<Vizel bind:markdown={markdown} />
<textarea bind:value={markdown} />Enabling Features
Enable optional features through the features option:
const editor = useVizelEditor({
features: {
// Enabled by default
slashCommand: true,
table: true,
image: true,
codeBlock: true,
dragHandle: true,
characterCount: true,
textColor: true,
taskList: true,
link: true,
// Disabled by default - enable as needed
markdown: true,
mathematics: true,
embed: true,
details: true,
diagram: true,
},
});const editor = useVizelEditor({
features: {
markdown: true,
mathematics: true,
embed: true,
details: true,
diagram: true,
},
});const editor = createVizelEditor({
features: {
markdown: true,
mathematics: true,
embed: true,
details: true,
diagram: true,
},
});See Features for detailed configuration of each feature.
Image Upload
Configure image uploads with a custom handler:
const editor = useVizelEditor({
features: {
image: {
onUpload: async (file) => {
// Upload to your server/CDN
const formData = new FormData();
formData.append('file', file);
const response = await fetch('/api/upload', {
method: 'POST',
body: formData,
});
const { url } = await response.json();
return url;
},
maxFileSize: 10 * 1024 * 1024, // 10MB
allowedTypes: ['image/jpeg', 'image/png', 'image/gif', 'image/webp'],
},
},
});const editor = useVizelEditor({
features: {
image: {
onUpload: async (file) => {
const formData = new FormData();
formData.append('file', file);
const response = await fetch('/api/upload', {
method: 'POST',
body: formData,
});
const { url } = await response.json();
return url;
},
},
},
});const editor = createVizelEditor({
features: {
image: {
onUpload: async (file) => {
const formData = new FormData();
formData.append('file', file);
const response = await fetch('/api/upload', {
method: 'POST',
body: formData,
});
const { url } = await response.json();
return url;
},
},
},
});Dark Mode
Use VizelThemeProvider for theme support:
import { VizelThemeProvider, useVizelTheme } from '@vizel/react';
function App() {
return (
<VizelThemeProvider defaultTheme="system" storageKey="my-theme">
<Editor />
<ThemeToggle />
</VizelThemeProvider>
);
}
function ThemeToggle() {
const { resolvedTheme, setTheme } = useVizelTheme();
return (
<button onClick={() => setTheme(resolvedTheme === 'dark' ? 'light' : 'dark')}>
{resolvedTheme === 'dark' ? '☀️' : '🌙'}
</button>
);
}<script setup lang="ts">
import { VizelThemeProvider, useVizelTheme } from '@vizel/vue';
const { resolvedTheme, setTheme } = useVizelTheme();
function toggleTheme() {
setTheme(resolvedTheme.value === 'dark' ? 'light' : 'dark');
}
</script>
<template>
<VizelThemeProvider defaultTheme="system" storageKey="my-theme">
<Editor />
<button @click="toggleTheme">
{{ resolvedTheme === 'dark' ? '☀️' : '🌙' }}
</button>
</VizelThemeProvider>
</template><script lang="ts">
import { VizelThemeProvider, getVizelTheme } from '@vizel/svelte';
const theme = getVizelTheme();
function toggleTheme() {
theme.setTheme(theme.resolvedTheme === 'dark' ? 'light' : 'dark');
}
</script>
<VizelThemeProvider defaultTheme="system" storageKey="my-theme">
<Editor />
<button onclick={toggleTheme}>
{theme.resolvedTheme === 'dark' ? '☀️' : '🌙'}
</button>
</VizelThemeProvider>See Theming for more customization options.
Next Steps
- Configuration - Editor options
- Features - Configure individual features
- Theming - Customize appearance with CSS variables
- Auto-Save - Persist content automatically
- React Guide - React-specific patterns
- Vue Guide - Vue-specific patterns
- Svelte Guide - Svelte-specific patterns