Skip to main content

Overview

This guide covers common issues you might encounter when using Yoopta Editor and provides solutions to resolve them quickly.

Installation Issues

Symptoms:
Module not found: Can't resolve '@yoopta/editor'
Causes:
  • Package not installed
  • Incorrect import path
  • Node modules cache issue
Solutions:
  1. Install the package:
    npm install @yoopta/editor
    
  2. Clear node modules and reinstall:
    rm -rf node_modules package-lock.json
    npm install
    
  3. Check your import statement:
    // Correct
    import { createYooptaEditor } from '@yoopta/editor';
    
    // Incorrect
    import { createYooptaEditor } from 'yoopta-editor';
    
Symptoms:
Could not find a declaration file for module '@yoopta/editor'
Cause: TypeScript cache or configuration issueSolutions:
  1. Restart your TypeScript server (VS Code: Cmd+Shift+P → “Restart TS Server”)
  2. Ensure your tsconfig.json includes:
    {
      "compilerOptions": {
        "moduleResolution": "node",
        "esModuleInterop": true,
        "jsx": "react-jsx"
      }
    }
    
  3. Clear TypeScript cache:
    rm -rf node_modules/.cache
    
Symptoms:
npm WARN @yoopta/[email protected] requires a peer of react@>=18.2.0
Cause: React version mismatchSolution: Update React to v18 or higher:
npm install react@^18.2.0 react-dom@^18.2.0
Note: Yoopta v6 supports React 18+ (tested with React 19)

Editor Initialization

Symptoms: Editor appears but shows no content or placeholderCauses:
  • Initial value not set
  • Value format incorrect
  • Plugins not registered
Solutions:
  1. Set initial value:
    const editor = useMemo(
      () => createYooptaEditor({
        plugins: PLUGINS,
        marks: MARKS,
        value: {}, // Start with empty object
      }),
      []
    );
    
  2. Or set value after creation:
    useEffect(() => {
      editor.setEditorValue(initialValue);
    }, []);
    
  3. Verify value format:
    const validValue: YooptaContentValue = {
      'block-id': {
        id: 'block-id',
        type: 'Paragraph',
        value: [{ id: 'el-id', type: 'paragraph', children: [{ text: 'Hello' }] }],
        meta: { order: 0, depth: 0 },
      },
    };
    
Symptoms: Error when trying to use editor methodsCause: Editor instance not created or not passed to componentSolution:
// Ensure editor is created
const editor = useMemo(
  () => createYooptaEditor({ plugins: PLUGINS, marks: MARKS }),
  []
);

// Pass to YooptaEditor component
<YooptaEditor editor={editor}>
  {/* ... */}
</YooptaEditor>
Symptoms: Console warning about multiple editor instancesCause: Editor instance recreated on every renderSolution: Always memoize the editor:
// Wrong: Creates new editor on every render
function Editor() {
  const editor = createYooptaEditor({ plugins: PLUGINS });
  return <YooptaEditor editor={editor} />;
}

// Correct: Editor created once
function Editor() {
  const editor = useMemo(
    () => createYooptaEditor({ plugins: PLUGINS }),
    []
  );
  return <YooptaEditor editor={editor} />;
}

Content & Rendering

Symptoms: Editor content disappears on refreshCause: Not saving content to persistent storageSolution:
function Editor() {
  const [value, setValue] = useState<YooptaContentValue>({});
  const editor = useMemo(
    () => createYooptaEditor({ plugins: PLUGINS, value }),
    []
  );
  
  // Load from localStorage on mount
  useEffect(() => {
    const saved = localStorage.getItem('editor-content');
    if (saved) {
      editor.setEditorValue(JSON.parse(saved));
    }
  }, []);
  
  // Save on change
  const handleChange = useCallback((newValue: YooptaContentValue) => {
    setValue(newValue);
    localStorage.setItem('editor-content', JSON.stringify(newValue));
  }, []);
  
  return <YooptaEditor editor={editor} onChange={handleChange} />;
}
Symptoms: Image placeholders appear but images don’t loadCauses:
  • Invalid image URL
  • CORS issues
  • Missing upload handler
Solutions:
  1. Verify image URLs are accessible:
    const imageUrl = 'https://example.com/image.jpg';
    // Test in browser: should load directly
    
  2. Implement upload handler:
    import Image from '@yoopta/image';
    
    const ImageWithUpload = Image.extend({
      options: {
        onUpload: async (file: 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 { src: url };
        },
      },
    });
    
  3. Check CORS headers on your image server:
    Access-Control-Allow-Origin: *
    
Symptoms: Empty blocks don’t show placeholder textCause: Placeholder prop not set or CSS not importedSolutions:
  1. Set global placeholder:
    <YooptaEditor
      editor={editor}
      placeholder="Type / to open menu"
    />
    
  2. Set per-element placeholder:
    const Paragraph = ParagraphPlugin.extend({
      elements: {
        paragraph: {
          placeholder: 'Type paragraph text...',
        },
      },
    });
    
  3. Ensure theme CSS is imported:
    import '@yoopta/themes-shadcn/styles.css';
    
Symptoms: Content renders but looks plain/unstyledCause: Theme not applied or CSS not importedSolutions:
  1. Apply theme to plugins:
    import { applyTheme } from '@yoopta/themes-shadcn';
    import '@yoopta/themes-shadcn/styles.css';
    
    const PLUGINS = applyTheme([Paragraph, HeadingOne, /* ... */]);
    
  2. Or import CSS manually:
    import '@yoopta/editor/dist/index.css';
    

Interaction Issues

Symptoms: Floating toolbar doesn’t show on text selectionCauses:
  • Toolbar component not rendered
  • CSS positioning issue
  • Z-index conflict
Solutions:
  1. Ensure toolbar is rendered as child:
    import { FloatingToolbar } from '@yoopta/ui';
    
    <YooptaEditor editor={editor}>
      <FloatingToolbar />
    </YooptaEditor>
    
  2. Check z-index:
    .yoopta-floating-toolbar {
      z-index: 1000; /* Ensure it's above other elements */
    }
    
  3. Verify selection is active:
    // Toolbar only shows when text is selected
    // Try selecting text with mouse
    
Symptoms: Clicking on editor doesn’t focus itCauses:
  • Conflicting click handlers
  • CSS pointer-events issue
  • Editor in read-only mode
Solutions:
  1. Check read-only mode:
    const editor = createYooptaEditor({
      plugins: PLUGINS,
      readOnly: false, // Ensure not read-only
    });
    
  2. Verify CSS isn’t blocking clicks:
    .yoopta-editor {
      pointer-events: auto; /* Not 'none' */
    }
    
  3. Use autoFocus prop:
    <YooptaEditor editor={editor} autoFocus />
    
Symptoms: Typing ”/” doesn’t trigger the menuCause: SlashCommandMenu component not renderedSolution:
import { SlashCommandMenu } from '@yoopta/ui';

<YooptaEditor editor={editor}>
  <SlashCommandMenu />
</YooptaEditor>
Symptoms: Cannot reorder blocks by draggingCause: BlockDndContext not set upSolution:
import { BlockDndContext, SortableBlock } from '@yoopta/ui';

<YooptaEditor
  editor={editor}
  renderBlock={(props) => <SortableBlock {...props} />}
>
  <BlockDndContext>
    {/* UI components */}
  </BlockDndContext>
</YooptaEditor>

Performance Issues

Symptoms: Delay between keypress and character appearingCauses:
  • Editor not memoized
  • Too many re-renders
  • Large document
  • Heavy plugins (Code, Table)
Solutions:
  1. Memoize editor instance:
    const editor = useMemo(
      () => createYooptaEditor({ plugins: PLUGINS }),
      []
    );
    
  2. Debounce onChange handler:
    const handleChange = useCallback(
      debounce((value: YooptaContentValue) => {
        saveToBackend(value);
      }, 1000),
      []
    );
    
  3. Use production build:
    NODE_ENV=production npm run build
    
  4. For large documents, implement pagination or lazy loading.
See Performance Guide for more optimization tips.
Symptoms:
Warning: Can't perform a React state update on an unmounted component
Cause: Event listeners not cleaned upSolution:
useEffect(() => {
  const handleChange = (payload: YooptaEventChangePayload) => {
    console.log('Changed:', payload);
  };
  
  editor.on('change', handleChange);
  
  // Cleanup
  return () => {
    editor.off('change', handleChange);
  };
}, [editor]);

Plugin Issues

Symptoms: Custom plugin doesn’t render or function correctlyCauses:
  • Incorrect plugin structure
  • Missing required properties
  • Plugin not registered
Solution: Verify plugin structure:
import type { Plugin, SlateElement } from '@yoopta/editor';

type MyElementMap = {
  'my-element': SlateElement<'my-element', { nodeType: 'block' }>;
};

const MyPlugin: Plugin<MyElementMap> = {
  type: 'MyPlugin', // PascalCase for plugin type
  elements: {
    'my-element': { // kebab-case for element type
      render: (props) => <div {...props.attributes}>{props.children}</div>,
      props: { nodeType: 'block' },
    },
  },
  options: {
    display: {
      title: 'My Plugin',
      description: 'My custom plugin',
    },
  },
};

// Register plugin
const PLUGINS = [MyPlugin, /* ... */];
Symptoms: Plugin shows in menu but clicking it does nothingCause: Missing beforeCreate lifecycle or invalid element structureSolution:
const MyPlugin: Plugin<MyElementMap> = {
  type: 'MyPlugin',
  elements: { /* ... */ },
  lifecycle: {
    beforeCreate: (editor) => {
      // Return initial element structure
      return editor.y('my-element', {
        children: [editor.y.text('')],
      });
    },
  },
};

Browser-Specific Issues

Symptoms: Toolbar clicks don’t work, focus issues, or rendering problemsCause: Safari-specific event handling differencesStatus: Fixed in v6.0.0-beta.18+Solution: Update to latest version:
npm install @yoopta/editor@latest @yoopta/ui@latest
Symptoms: Cannot copy or paste contentCauses:
  • Browser permissions
  • Clipboard API not available
  • HTTPS required
Solutions:
  1. Use HTTPS (required for Clipboard API):
    https://localhost:3000
    
  2. Check browser permissions for clipboard access
  3. Fallback for older browsers:
    // Yoopta handles this automatically
    // Ensure you're on latest version
    

Build & Deployment

Symptoms:
ReferenceError: process is not defined
Cause: Server-side rendering (SSR) issueSolution for Next.js:
'use client'; // Add at top of file

import dynamic from 'next/dynamic';

// Lazy load editor to avoid SSR
const Editor = dynamic(
  () => import('./Editor'),
  { ssr: false }
);

export default function Page() {
  return <Editor />;
}
Symptoms: Build size larger than expectedCauses:
  • Including unused plugins
  • Not using tree-shaking
Solutions:
  1. Only import plugins you use:
    // Good: Specific imports
    import HeadingOne from '@yoopta/headings/HeadingOne';
    
    // Bad: Imports all headings
    import * as Headings from '@yoopta/headings';
    
  2. Analyze bundle:
    npm run build -- --analyze
    
  3. Use dynamic imports for heavy plugins:
    const CodePlugin = lazy(() => import('@yoopta/code'));
    

Getting Further Help

If your issue isn’t covered here:
  1. Search existing issues: GitHub Issues
  2. Ask the community: GitHub Discussions
  3. Check examples: Example implementations
  4. Report a bug: Create an issue

When Reporting Issues

Please include:
  • Yoopta Editor version
  • React version
  • Browser and OS
  • Minimal reproduction code
  • Steps to reproduce
  • Expected vs actual behavior
  • Console errors (if any)

Migration Guide

Migrate from older versions

Performance

Optimize editor performance

Build docs developers (and LLMs) love