Skip to content

Theming

Lexical tries to make theming straight-forward, by providing a way of passing a customizable theming object that maps CSS class names to the editor on creation. Here's an example of a plain-text theme:

js
const exampleTheme = {
  ltr: 'ltr',
  rtl: 'rtl',
  paragraph: 'editor-paragraph',
}

In your CSS, you can then add something like:

css
.ltr {
  text-align: left;
}

.rtl {
  text-align: right;
}

.editor-placeholder {
  color: #999;
  overflow: hidden;
  position: absolute;
  top: 15px;
  left: 15px;
  user-select: none;
  pointer-events: none;
}

.editor-paragraph {
  margin: 0 0 15px 0;
  position: relative;
}

To apply it, you need to pass it to your editor instance. This is done by passing it as a property of the initialConfig to <LexicalComposer>, like shown:

vue
<script setup>
import { LexicalComposer, LexicalContentEditable, LexicalPlainTextPlugin } from 'lexical-vue'
import { exampleTheme } from './exampleTheme'

const initialConfig = { namespace: 'MyEditor', theme: exampleTheme }
</script>

<template>
  <LexicalComposer :initial-config="initialConfig">
    <LexicalPlainTextPlugin>
      <template #contentEditable>
        <LexicalContentEditable />
      </template>
      <template #placeholder>
        <div class="editor-placeholder">
          Enter some text...
        </div>
      </template>
    </LexicalPlainTextPlugin>
  </LexicalComposer>
</template>

Many of the Lexical's core nodes also accept theming properties. Here's a more comprehensive theming object:

js
const exampleTheme = {
  ltr: 'ltr',
  rtl: 'rtl',
  paragraph: 'editor-paragraph',
  quote: 'editor-quote',
  heading: {
    h1: 'editor-heading-h1',
    h2: 'editor-heading-h2',
    h3: 'editor-heading-h3',
    h4: 'editor-heading-h4',
    h5: 'editor-heading-h5',
    h6: 'editor-heading-h6',
  },
  list: {
    nested: {
      listitem: 'editor-nested-listitem',
    },
    ol: 'editor-list-ol',
    ul: 'editor-list-ul',
    listitem: 'editor-listItem',
    listitemChecked: 'editor-listItemChecked',
    listitemUnchecked: 'editor-listItemUnchecked',
  },
  hashtag: 'editor-hashtag',
  image: 'editor-image',
  link: 'editor-link',
  text: {
    bold: 'editor-textBold',
    code: 'editor-textCode',
    italic: 'editor-textItalic',
    strikethrough: 'editor-textStrikethrough',
    subscript: 'editor-textSubscript',
    superscript: 'editor-textSuperscript',
    underline: 'editor-textUnderline',
    underlineStrikethrough: 'editor-textUnderlineStrikethrough',
  },
  code: 'editor-code',
  codeHighlight: {
    'atrule': 'editor-tokenAttr',
    'attr': 'editor-tokenAttr',
    'boolean': 'editor-tokenProperty',
    'builtin': 'editor-tokenSelector',
    'cdata': 'editor-tokenComment',
    'char': 'editor-tokenSelector',
    'class': 'editor-tokenFunction',
    'class-name': 'editor-tokenFunction',
    'comment': 'editor-tokenComment',
    'constant': 'editor-tokenProperty',
    'deleted': 'editor-tokenProperty',
    'doctype': 'editor-tokenComment',
    'entity': 'editor-tokenOperator',
    'function': 'editor-tokenFunction',
    'important': 'editor-tokenVariable',
    'inserted': 'editor-tokenSelector',
    'keyword': 'editor-tokenAttr',
    'namespace': 'editor-tokenVariable',
    'number': 'editor-tokenProperty',
    'operator': 'editor-tokenOperator',
    'prolog': 'editor-tokenComment',
    'property': 'editor-tokenProperty',
    'punctuation': 'editor-tokenPunctuation',
    'regex': 'editor-tokenVariable',
    'selector': 'editor-tokenSelector',
    'string': 'editor-tokenSelector',
    'symbol': 'editor-tokenProperty',
    'tag': 'editor-tokenProperty',
    'url': 'editor-tokenOperator',
    'variable': 'editor-tokenVariable',
  },
}

Released under the MIT License.