SlateJS: Creating a Text Editor

Having a text editor customized can enhance your user’s experience. Slate is a text editor framework that can be customized to fit your needs.

Installing Slate

We’ll need three modules for our Slate implementation.

slate: The core module…


This content originally appeared on DEV Community and was authored by Karl Castillo

Having a text editor customized can enhance your user's experience. Slate is a text editor framework that can be customized to fit your needs.

Installing Slate

We'll need three modules for our Slate implementation.

  • slate: The core module of Slate
  • slate-react: The React wrapper for Slate
  • slate-history: Allows the user to undo their actions
npm i --save slate slate-react slate-history

or

yarn add slate slate-react slate-history

Setting up Slate

To create a new Editor using createEditor in combination with a couple of plugins -- withReact and withHistory.

...
import { createEditor } from 'slate';
import { withReact } from 'slate-react';
import { withHistory } from 'slate-history';

const Editor = () => {
  const editor = useMemo(() => withReact(withHistory(createEditor())), []);
  ...
}

Two components will then be used to render the Slate editor.

import { Slate, Editable, ... } 'slate-react';

const Editor = () => {
  ...
  return (
    <Slate editor={editor}>
      <Editable />
    </Slate>
  )
}

Adding Content

Slate would crash initially and that's because a default value is necessary.

const Editor = () => {
  ...
  const [value, setValue] = useState([
    {
      children: [{ text: 'This is my paragraph!' }]
    }
  ])

  return (
    <Slate ... value={value} setValue={setValue}>
      ...
    </Slate>
  )
}

It's important for a default Element to exist and not just an empty array. Having an empty array will cause a crash as Slate has nothing to attach the cursor to.

Custom Types

By default, the content will be considered as text but Rich Text Editors can have non-text content.

Each Element can have custom properties to help render said Element.

const [value, setValue] = useState([
  {
    type: 'paragraph',
    children: [{ text: 'This is my paragraph' }]
  },
  {
    type: 'image',
    src: 'path/to/image',
    alt: 'This is my image'
    children: [{ text: '' }]
  }
])

We can then render these custom elements using the renderElement prop of the Editor component.

const Paragraph = ({ attributes, children }) => (
  <p {...attributes}>{children}</p>
)

const Image = ({ attributes, element, children }) => (
  <div {...attributes}>
    <div contentEditable={false}>
      <img src={element.src} alt={element.src} />
    </div>
    {children}
  </div>
)

const renderElement = (props) => {
  switch(props.element.type) {
    case 'image': 
      return <Image {...props} />
    default:
      return <Paragraph {...props} />
  }
}

const Editor = () => {
  ...
  return (
    <Slate ...>
      <Editor renderElement={renderElement} />
    </Slate>
  )
}

It's important that every Element renders the children prop as this is how Slate can keep track of which element currently has focus.

Voids

Voids are Elements that cannot be edited as if it was text. Since our Image cannot be edited as if it was text, we need to tell Slate that it's a void Element.

Plugins

The editor object that we create has an isVoid function which determines whether or not an Element is void or not.

Slate allows us to create plugins that can modify the functionality of existing editor functions or add new functionality.

const withImage = (editor) => {
  const { isVoid } = editor;

  editor.isVoid = (element) =>
    element.type === 'image' ? true : isVoid(element);

  return editor;
}

const Editor = () => {
  const editor = useMemo(() => withReact(withHistory(withImages(createEditor()))), []);
  ...
}

TIP: Since you can have a lot of plugins especially for more complicated editors, you can use the pipe function from lodash/fp.

import pipe from 'lodash/fp/pipe'

const createEditorWithPlugins = pipe(
  withReact,
  withHistory,
  withImage
)

const Editor = () => {
  const editor = useMemo(() => createEditorWithPlugins(createEditor()), []);
}

Handling Events

Since Image is now considered as a void element, it loses some keyboard functionality. Luckily, the editor provides us two functions that we can extend.

insertBreak

The editor.insertBreak function is called when the user presses the enter or return for Mac.

const { isVoid, insertBreak, ... } = editor

editor.insertBreak = (...args) => {
  const parentPath = Path.parent(editor.selection.focus.path);
  const parentNode = Node.get(editor, parentPath);

  if (isVoid(parentNode)) {
    const nextPath = Path.next(parentPath);
    Transforms.insertNodes(
      editor,
      {
        type: 'paragraph',
        children: [{ text: '' }]
      }, 
      {
        at: nextPath,
        select: true // Focus on this node once inserted
      }
    );
  } else {
    insertBreak(...args);
  }
}

deleteBackward

The editor.deleteBackward function is called when the user presses the backspace or delete for Mac.

const { isVoid, deleteBackward, ... } = editor

editor.deleteBackward = (...args) => {
  const parentPath = Path.parent(editor.selection.focus.path);
  const parentNode = Node.get(editor, parentPath);

  if (isVoid(parentNode) || !Node.string(parentNode).length) {
    Transforms.removeNodes(editor, { at: parentPath });
  } else {
    deleteBackward(...args);
  }
}

Conclusion

As you can see, Slate can be heavily customizable as it gives you the necessary tools to add your own functionality.

Demo


This content originally appeared on DEV Community and was authored by Karl Castillo


Print Share Comment Cite Upload Translate Updates
APA

Karl Castillo | Sciencx (2021-04-09T15:50:13+00:00) SlateJS: Creating a Text Editor. Retrieved from https://www.scien.cx/2021/04/09/slatejs-creating-a-text-editor/

MLA
" » SlateJS: Creating a Text Editor." Karl Castillo | Sciencx - Friday April 9, 2021, https://www.scien.cx/2021/04/09/slatejs-creating-a-text-editor/
HARVARD
Karl Castillo | Sciencx Friday April 9, 2021 » SlateJS: Creating a Text Editor., viewed ,<https://www.scien.cx/2021/04/09/slatejs-creating-a-text-editor/>
VANCOUVER
Karl Castillo | Sciencx - » SlateJS: Creating a Text Editor. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2021/04/09/slatejs-creating-a-text-editor/
CHICAGO
" » SlateJS: Creating a Text Editor." Karl Castillo | Sciencx - Accessed . https://www.scien.cx/2021/04/09/slatejs-creating-a-text-editor/
IEEE
" » SlateJS: Creating a Text Editor." Karl Castillo | Sciencx [Online]. Available: https://www.scien.cx/2021/04/09/slatejs-creating-a-text-editor/. [Accessed: ]
rf:citation
» SlateJS: Creating a Text Editor | Karl Castillo | Sciencx | https://www.scien.cx/2021/04/09/slatejs-creating-a-text-editor/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.