---
title: WYSIWYG Editor
desc: >-
  The QEditor Vue component is a WYSIWYG editor that enables writing and pasting
  HTML.
---
The QEditor component is a WYSIWYG (“what you see is what you get”) editor component that enables the user to write and even paste HTML. It uses the so-called Design Mode and the cross-browser `contentEditable` interface. Here are some go-to reference pages from the MDN webdocs with more detailed information about the underlying technology:

- [Making content editable](https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Editable_content)
- [Design Mode](https://developer.mozilla.org/en-US/docs/Web/API/Document/designMode)
- [execCommand() reference](https://developer.mozilla.org/en-US/docs/Web/API/document/execCommand)
- [contentEditable spec](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/contenteditable)

## QEditor API

### Props

- `fullscreen` (boolean, optional, syncable)
  Fullscreen mode
  Examples: `v-model:fullscreen="isFullscreen"`
- `no-route-fullscreen-exit` (boolean, optional)
  Changing route app won't exit fullscreen
- `model-value` (string, required, syncable)
  Model of the component; Either use this property (along with a listener for 'update:modelValue' event) OR use v-model directive
  Examples: `v-model="content"`
- `readonly` (boolean, optional)
  Put component in readonly mode
- `square` (boolean, optional)
  Removes border-radius so borders are squared
- `flat` (boolean, optional)
  Applies a 'flat' design (no borders)
- `dense` (boolean, optional)
  Dense mode; toolbar buttons are shown on one-line only
- `dark` (boolean, optional), default `null`
  Notify the component that the background is a dark color
- `disable` (boolean, optional)
  Put component in disabled mode
- `min-height` (string, optional), default `'10rem'`
  CSS unit for the minimum height of the editable area
  Examples: `'15rem'`, `'50vh'`
- `max-height` (string, optional)
  CSS unit for maximum height of the input area
  Examples: `'1000px'`, `'90vh'`
- `height` (string, optional)
  CSS value to set the height of the editable area
  Examples: `'100px'`, `'50vh'`
- `definitions` (object, optional)
  Definition of commands and their buttons to be included in the 'toolbar' prop
  Examples:
    - `{ save: { tip: 'Save your work', icon: 'save', label: 'Save', handler: saveWork }, upload: { tip: 'Upload to cloud', icon: 'cloud_upload', label: 'Upload', handler: uploadIt } }`
  Object shape:
    - `...commandName` (object, optional)
      Command definition
      Object shape:
        - `label` (string, optional)
          Label of the button
          Examples: `'Addresses'`
        - `tip` (string, optional)
          Text to be displayed as a tooltip on hover
          Examples: `'Add a contact from the Address Book'`
        - `htmlTip` (string, optional)
          HTML formatted text to be displayed within a tooltip on hover
          Examples: `'Add a <span class="red">user</span> from the address book'`
        - `icon` (string, optional)
          Icon of the button
          Examples: `'fas fa-address-book'`
        - `key` (number, optional)
          Keycode of a key to be used together with the <ctrl> key for use as a shortcut to trigger this element
          Examples: `12`, `36`
        - `handler` (Function, optional)
          Either this or "cmd" is required. Function for when button gets clicked/tapped.
          Examples: `() => this.uploadFile()`
        - `cmd` (string, optional)
          Either this or "handler" is required. This must be a valid execCommand method according to the designMode API.
          Examples: `'insertHTML'`, `'justifyFull'`
        - `param` (string, optional)
          Only set a param if using a "cmd". This is commonly text or HTML to inject, but is highly dependent upon the specific cmd being called.
          Examples: `'<img src="://uploads/001.jpg" alt="nice pic" />'`
        - `disable` (boolean | Function, optional)
          Is button disabled?
          Function signature: `() => boolean`
          Examples: `true`, `() => !checkIfUserIsActive()`
        - `type` (string, optional)
          Pass the value "no-state" if the button should not have an "active" state
          Accepts: `null`, `'no-state'`
          Examples: `'no-state'`
        - `fixedLabel` (boolean, optional)
          Lock the button label, so it doesn't change based on the child option selected.
        - `fixedIcon` (boolean, optional)
          Lock the button icon, so it doesn't change based on the child option selected.
        - `highlight` (boolean, optional)
          Highlight the toolbar button, when a child option has been selected.
- `fonts` (object, optional)
  Object with definitions of fonts
  Examples:
    - `{ arial: 'Arial', arial_black: 'Arial Black', comic_sans: 'Comic Sans MS' }`
- `toolbar` (any[], optional), default `[['left', 'center', 'right', 'justify'], ['bold', 'italic', 'underline', 'strike'], ['undo', 'redo']]`
  An array of arrays of Objects/Strings that you use to define the construction of the elements and commands available in the toolbar
  Examples:
    - `['left', 'center', 'right', 'justify']`
- `toolbar-color` (string, optional)
  Font color (from the Quasar Palette) of buttons and text in the toolbar
  Examples: `'primary'`, `'teal'`, `'teal-10'`
- `toolbar-text-color` (string, optional)
  Text color (from the Quasar Palette) of toolbar commands
  Examples: `'primary'`, `'teal'`, `'teal-10'`
- `toolbar-toggle-color` (string, optional), default `'primary'`
  Choose the active color (from the Quasar Palette) of toolbar commands button
  Examples: `'secondary'`, `'blue-3'`
- `toolbar-bg` (string, optional), default `'grey-3'`
  Toolbar background color (from Quasar Palette)
  Examples: `'secondary'`, `'blue-3'`
- `toolbar-outline` (boolean, optional)
  Toolbar buttons are rendered "outlined"
- `toolbar-push` (boolean, optional)
  Toolbar buttons are rendered as a "push-button" type
- `toolbar-rounded` (boolean, optional)
  Toolbar buttons are rendered "rounded"
- `paragraph-tag` (string, optional), default `'div'`
  Paragraph tag to be used
  Accepts: `'div'`, `'p'`
- `content-style` (object, optional)
  Object with CSS properties and values for styling the container of QEditor
  Examples: `{ backgroundColor: '#C0C0C0' }`
- `content-class` (string | any[] | object, optional)
  CSS classes for the input area
  Examples: `'my-special-class'`, `{ 'my-special-class': true }`
- `placeholder` (string, optional)
  Text to display as placeholder
  Examples: `'Type your story here ...'`
- `dropdown-hover` (boolean, optional) *(added v2.30)*
  Toolbar dropdowns also open when the pointer hovers their button and close when it leaves both the button and the menu; Click/tap and keyboard interactions keep toggling as usual (touch devices fall back to them)
- `dropdown-hover-delay` (number, optional), default `0` *(added v2.30)*
  Delay (in milliseconds) between the pointer entering a toolbar dropdown's button and the dropdown showing up; Requires the 'dropdown-hover' prop
- `dropdown-hover-hide-delay` (number, optional), default `150` *(added v2.30)*
  Grace period (in milliseconds) in which the pointer can re-enter a toolbar dropdown's button or its menu before the dropdown gets closed; Requires the 'dropdown-hover' prop

### Computed Props

- `caret` (object, optional)
  The current caret state

### Methods

- `toggleFullscreen(): void`
  Toggle the view to be fullscreen or not fullscreen
- `setFullscreen(): void`
  Enter the fullscreen view
- `exitFullscreen(): void`
  Leave the fullscreen view
- `runCmd(cmd: string, param?: string, update?: boolean): void`
  Run contentEditable command at caret position and range
  Params:
    - `cmd` (string, required)
      Must be a valid execCommand method according to the designMode API
      Examples: `'copy'`, `'cut'`, `'paste'`
    - `param` (string, optional)
      The argument to pass to the command
      Examples: `'<small>Small Text</small>'`
    - `update` (boolean, optional), default `true`
      Refresh the toolbar
- `refreshToolbar(): void`
  Hide the link editor if visible and force the instance to re-render
- `focus(): void`
  Focus on the contentEditable at saved cursor position
- `getContentEl(): Element`
  Retrieve the content of the Editor
  Returns: `Element` — Provides the pure HTML within the editable area

### Events

- `@fullscreen`
  Emitted when fullscreen state changes
  Params:
    - `value` (boolean, optional)
      Fullscreen state (showing/hidden)
- `@update:fullscreen`
  Used by Vue on 'v-model:fullscreen' prop for updating its value
  Params:
    - `value` (boolean, optional)
      Fullscreen state (showing/hidden)
- `@update:model-value`
  Emitted when the component needs to change the model; Is also used by v-model
  Params:
    - `value` (string, required)
      The pure HTML of the content
- `@dropdown-show`
  Emitted after a dropdown in the toolbar has triggered show()
  Params:
    - `evt` (Event, optional)
      JS event object
- `@dropdown-before-show`
  Emitted when a dropdown in the toolbar triggers show() but before it finishes doing it
  Params:
    - `evt` (Event, optional)
      JS event object
- `@dropdown-hide`
  Emitted after a dropdown in the toolbar has triggered hide()
  Params:
    - `evt` (Event, optional)
      JS event object
- `@dropdown-before-hide`
  Emitted when a dropdown in the toolbar triggers hide() but before it finishes doing it
  Params:
    - `evt` (Event, optional)
      JS event object
- `@link-show`
  Emitted when the toolbar for editing a link is shown
- `@link-hide`
  Emitted when the toolbar for editing a link is hidden

### Slots

- `#[command]`
  Content for the given command in the toolbar

## Examples

```vue
<template>
  <div class="q-pa-md q-gutter-sm">
    <q-editor v-model="editor" min-height="5rem" />

    <q-card flat bordered>
      <q-card-section>
        <pre style="white-space: pre-line">{{ editor }}</pre>
      </q-card-section>
    </q-card>

    <q-card flat bordered>
      <q-card-section v-html="editor" />
    </q-card>
  </div>
</template>

<script setup>
import { ref } from 'vue'

const editor = ref('What you see is <b>what</b> you get.')
</script>
```

> [!WARNING]
> In this first example, there are two cards below the editor. The first shows the unparsed html using the double-moustache, whereas the second shows the rendered version using `v-html="editor"`. Using v-html this way renders your users vulnerable to Cross Site Scripting attacks. If the content is user generated, be sure to sanitize it either on render or server side (or both).

By default, QEditor offers most if not all the commands you’d need in a WYSIWYG editor: bold, italic, strike, underline, unordered (list), ordered (list), subscript, superscript, link, fullscreen, quote, left (align), center (align), right (align), justify (align), print, outdent, indent, removeFormat, hr, undo, redo, h1 to h6, p (paragraph), code (code paragraph), size-1 to size-7.

Each of these commands is pre-configured with icons and their own internationalized tooltips. However, if you want to override some of their settings you can do so with the help of definitions Object property.

```html
:definitions="{ bold: {label: 'Bold', icon: null, tip: 'My bold tooltip'} }"
```

### Redefine bold command

```vue
<template>
  <div class="q-pa-md q-gutter-sm">
    <q-editor
      v-model="editor"
      :definitions="{
        bold: { label: 'Bold', icon: null, tip: 'My bold tooltip' }
      }"
    />
  </div>
</template>

<script setup>
import { ref } from 'vue'

const editor = ref(
  'Here we are overriding the <b>bold</b> command to include a label instead of an icon and also changing its tooltip.'
)
</script>
```

The following is an example that adds custom definitions. In such cases, make sure you don’t overlap the default commands:

### Add new commands

```vue
<template>
  <div class="q-pa-md q-gutter-sm">
    <q-editor
      v-model="editor"
      :definitions="{
        save: {
          tip: 'Save your work',
          icon: 'save',
          label: 'Save',
          handler: saveWork
        },
        upload: {
          tip: 'Upload to cloud',
          icon: 'cloud_upload',
          label: 'Upload',
          handler: uploadIt
        }
      }"
      :toolbar="[
        ['bold', 'italic', 'strike', 'underline'],
        ['upload', 'save']
      ]"
    />
  </div>
</template>

<script setup>
import { useQuasar } from 'quasar'
import { ref } from 'vue'

const $q = useQuasar()
const editor = ref(
  'After you define a new button,' +
    ' you have to make sure to put it in the toolbar too!'
)

function saveWork() {
  $q.notify({
    message: 'Saved your text to local storage',
    color: 'green-4',
    textColor: 'white',
    icon: 'cloud_done'
  })
}

function uploadIt() {
  $q.notify({
    message: 'Server unavailable. Check connectivity.',
    color: 'red-5',
    textColor: 'white',
    icon: 'warning'
  })
}
</script>
```

### Kitchen sink

```vue
<template>
  <div class="q-pa-md q-gutter-sm">
    <q-editor
      v-model="qeditor"
      :dense="$q.screen.lt.md"
      :toolbar="[
        [
          {
            label: $q.lang.editor.align,
            icon: $q.iconSet.editor.align,
            fixedLabel: true,
            list: 'only-icons',
            options: ['left', 'center', 'right', 'justify']
          },
          {
            label: $q.lang.editor.align,
            icon: $q.iconSet.editor.align,
            fixedLabel: true,
            options: ['left', 'center', 'right', 'justify']
          }
        ],
        ['bold', 'italic', 'strike', 'underline', 'subscript', 'superscript'],
        ['token', 'hr', 'link', 'custom_btn'],
        ['print', 'fullscreen'],
        [
          {
            label: $q.lang.editor.formatting,
            icon: $q.iconSet.editor.formatting,
            list: 'no-icons',
            options: ['p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'code']
          },
          {
            label: $q.lang.editor.fontSize,
            icon: $q.iconSet.editor.fontSize,
            fixedLabel: true,
            fixedIcon: true,
            list: 'no-icons',
            options: [
              'size-1',
              'size-2',
              'size-3',
              'size-4',
              'size-5',
              'size-6',
              'size-7'
            ]
          },
          {
            label: $q.lang.editor.defaultFont,
            icon: $q.iconSet.editor.font,
            fixedIcon: true,
            list: 'no-icons',
            options: [
              'default_font',
              'arial',
              'arial_black',
              'comic_sans',
              'courier_new',
              'impact',
              'lucida_grande',
              'times_new_roman',
              'verdana'
            ]
          },
          'removeFormat'
        ],
        ['quote', 'unordered', 'ordered', 'outdent', 'indent'],

        ['undo', 'redo'],
        ['viewsource']
      ]"
      :fonts="{
        arial: 'Arial',
        arial_black: 'Arial Black',
        comic_sans: 'Comic Sans MS',
        courier_new: 'Courier New',
        impact: 'Impact',
        lucida_grande: 'Lucida Grande',
        times_new_roman: 'Times New Roman',
        verdana: 'Verdana'
      }"
    />
  </div>
</template>

<script setup>
import { ref } from 'vue'

const qeditor = ref(
  '<pre>Check out the two different types of dropdowns' +
    ' in each of the "Align" buttons.</pre> '
)
</script>
```

### Custom style

```vue
<template>
  <div class="q-pa-md q-gutter-sm">
    <q-editor
      v-model="editor"
      flat
      content-class="bg-amber-3"
      toolbar-text-color="white"
      toolbar-toggle-color="yellow-8"
      toolbar-bg="primary"
      :toolbar="[
        ['bold', 'italic', 'underline'],
        [
          {
            label: $q.lang.editor.formatting,
            icon: $q.iconSet.editor.formatting,
            list: 'no-icons',
            options: ['p', 'h3', 'h4', 'h5', 'h6', 'code']
          }
        ]
      ]"
    />
  </div>
</template>

<script setup>
import { ref } from 'vue'

const editor = ref('Customize it.')
</script>
```

### Using toolbar slots

```vue
<template>
  <div class="q-pa-md q-gutter-sm">
    <q-editor
      v-model="editor"
      ref="editorRef"
      toolbar-text-color="white"
      toolbar-toggle-color="yellow-8"
      toolbar-bg="primary"
      :toolbar="[
        ['token'],
        ['bold', 'italic', 'underline'],
        [
          {
            label: $q.lang.editor.formatting,
            icon: $q.iconSet.editor.formatting,
            list: 'no-icons',
            options: ['p', 'h3', 'h4', 'h5', 'h6', 'code']
          }
        ]
      ]"
    >
      <template v-slot:token>
        <q-btn-dropdown
          dense
          no-caps
          ref="tokenRef"
          no-wrap
          unelevated
          color="white"
          text-color="primary"
          label="Token"
          size="sm"
        >
          <q-list dense>
            <q-item tag="label" clickable @click="add('email')">
              <q-item-section side>
                <q-icon name="mail" />
              </q-item-section>
              <q-item-section>Email</q-item-section>
            </q-item>
            <q-item tag="label" clickable @click="add('title')">
              <q-item-section side>
                <q-icon name="title" />
              </q-item-section>
              <q-item-section>Title</q-item-section>
            </q-item>
          </q-list>
        </q-btn-dropdown>
      </template>
    </q-editor>
  </div>
</template>

<script setup>
import { ref, useTemplateRef } from 'vue'

const editorRef = useTemplateRef('editorRef')
const tokenRef = useTemplateRef('tokenRef')
const editor = ref('Customize it.')

function add(name) {
  const edit = editorRef.value
  tokenRef.value.hide()
  edit.caret.restore()
  edit.runCmd(
    'insertHTML',
    `&nbsp;<div class="editor_token row inline items-center" contenteditable="false">&nbsp;<span>${name}</span>&nbsp;<i class="q-icon material-icons cursor-pointer" onclick="this.parentNode.parentNode.removeChild(this.parentNode)">close</i></div>&nbsp;`
  )
  edit.focus()
}
</script>

<style lang="sass">
.editor_token
  background: rgba(0, 0, 0, .6)
  color: white
  padding: 3px
  &, .q-icon
    border-radius: 3px
  .q-icon
    background: rgba(0, 0, 0, .2)
</style>
```

## Adding links

The `link` command swaps the toolbar for a URL field pointing at the current selection (or, when nothing is selected, at the word under the caret). The field starts out holding the selected text if that text already reads as an URL, and `https://` otherwise.

Your content is only touched once you commit an URL — by pressing <kbd>Enter</kbd>, by hitting the "Update" button, or simply by clicking away from the field. Leaving `https://` untouched commits nothing, so abandoning the field never leaves a broken link behind. <kbd>Escape</kbd> cancels outright, while the "Remove" button strips the link from the selection.

The command behaves the same wherever you put it, including inside one of the [dropdowns](#dropdowns) described below.

## Read-only and disabled

The `readonly` prop stops the content from being edited, so every toolbar command that would alter it is disabled. The commands that only read the content stay available: `fullscreen`, `print` and `viewsource`, which lets a read-only QEditor still work as a viewer.

The `disable` prop takes the whole component out of play instead: the content is not editable, the entire toolbar is disabled and the editor is dimmed.

## Dropdowns

### Types of dropdowns

```html
<q-editor
  v-model="model"
  :toolbar="[
    [
      {
        label: 'Icons & Label',
        icon: 'filter_1',
        fixedLabel: true,
        fixedIcon: true,
        options: ['bold', 'italic', 'strike', 'underline']
      }
    ],
    [
      {
        label: 'Only label',
        icon: 'filter_2',
        fixedLabel: true,
        fixedIcon: true,
        list: 'no-icons',
        options: ['bold', 'italic', 'strike', 'underline']
      }
    ],
    [
      {
        label: 'Only icons',
        icon: 'filter_3',
        fixedLabel: true,
        fixedIcon: true,
        list: 'only-icons',
        options: ['bold', 'italic', 'strike', 'underline']
      }
    ]
  ]"
/>
```

### Dropdowns with exclusive options

User can pick only one option from each dropdown.

- First has icon and label changing based on current selection
- Second has fixed label but dynamic icon
- Third has fixed icon but dynamic label

```html
<q-editor
  v-model="model"
  :toolbar="[
    [
      {
        label: 'Dynamic label',
        icon: 'help_outline',
        options: ['left', 'center', 'right', 'justify']
      }
    ],
    [
      {
        label: 'Static label',
        fixedLabel: true,
        options: ['left', 'center', 'right', 'justify']
      }
    ],
    [
      {
        label: 'Some label',
        icon: 'account_balance',
        fixedIcon: true,
        options: ['left', 'center', 'right', 'justify']
      }
    ]
  ]"
/>
```

### Opening dropdowns on hover *(v2.30+)*

The `dropdown-hover` prop also opens a toolbar dropdown when the pointer hovers its button, and closes it once the pointer leaves both the button and the menu. Click, tap and keyboard interactions keep toggling as usual, and touch devices fall back to them since there is no hover there.

`dropdown-hover-delay` (0 by default) is how long the pointer has to rest on a button before its dropdown opens, which keeps the toolbar quiet while the pointer is only passing over it. `dropdown-hover-hide-delay` (150 by default) is the grace period in which the pointer can re-enter the button or its menu before the dropdown closes, so a slightly off-target move across the gap does not dismiss it.

### Dropdowns on hover

```vue
<template>
  <div class="q-pa-md q-gutter-sm">
    <q-editor
      v-model="editor"
      dropdown-hover
      :dropdown-hover-delay="200"
      :toolbar="[
        [
          {
            label: 'Formatting',
            icon: 'format_size',
            fixedLabel: true,
            list: 'no-icons',
            options: ['p', 'h4', 'h5', 'h6', 'code']
          },
          {
            label: 'Align',
            icon: 'format_align_left',
            fixedLabel: true,
            list: 'only-icons',
            options: ['left', 'center', 'right', 'justify']
          }
        ],
        ['bold', 'italic', 'underline']
      ]"
    />
  </div>
</template>

<script setup>
import { ref } from 'vue'

const editor = ref(
  'Point at one of the two dropdowns to open it, then move across to the other one.'
)
</script>
```

## Accessibility *(v2.25+)*

### Keyboard navigation

Following the [WAI-ARIA toolbar pattern](https://www.w3.org/WAI/ARIA/apg/patterns/toolbar/), the toolbar is a single Tab stop: pressing <kbd>Tab</kbd> moves focus into the toolbar (onto the last used button, or the first enabled one) and a second press moves it on to the editing area, rather than walking through every button. Within the toolbar, <kbd>Arrow Left</kbd> and <kbd>Arrow Right</kbd> move focus between the buttons (wrapping at either end and following the text direction in RTL), while <kbd>Home</kbd> and <kbd>End</kbd> jump to the first or last one. Content rendered through custom toolbar slots keeps its own Tab stops.

The commands wired to the current toolbar also respond to their <kbd>CTRL</kbd> key combinations (shown in the button tooltips) while editing — <kbd>CTRL + B</kbd> for bold, for example.

You can prevent QEditor from running one of these commands by preventing its `keydown` event; for example, `@keydown.ctrl.b.prevent` leaves <kbd>CTRL + B</kbd> to your own handler instead of toggling bold.

### Labeling

The editing area is exposed as a multiline `textbox` and the toolbar carries a localized `aria-label` from the [Quasar Language Pack](../options/quasar-language-packs.md). It also mirrors its own props onto that role: a `placeholder` becomes `aria-placeholder`, while `readonly` and `disable` surface as `aria-readonly` and `aria-disabled`. Toggle commands (bold, italic, the alignment buttons and friends) report their state through `aria-pressed` rather than through their color alone, and the hyperlink editor's URL field is labeled from the same language pack. Attributes passed to QEditor itself (such as `aria-label`, `aria-labelledby` or `aria-describedby`) are applied to the editing area, so you can — and should — give it an accessible name:

```html
<q-editor v-model="model" aria-label="Post body" />
```

### Help dialog

Some WYSIWYG editors offer a dialog listing the available keyboard commands. QEditor does not ship one built in, but a custom toolbar slot gets you there:

### Help dialog

```vue
<template>
  <div class="q-pa-md q-gutter-sm">
    <q-editor
      v-model="editor"
      aria-label="Help dialog demonstration"
      min-height="5rem"
      :toolbar="[['bold', 'italic', 'underline'], ['undo', 'redo'], ['help']]"
    >
      <template v-slot:help>
        <q-btn
          dense
          flat
          size="sm"
          icon="help_outline"
          aria-label="Editor help"
          @click="showHelp = true"
        />
      </template>
    </q-editor>

    <q-dialog v-model="showHelp">
      <q-card>
        <q-card-section>
          <div class="text-h6">Editor help</div>
        </q-card-section>

        <q-card-section class="q-pt-none">
          <q-markup-table flat bordered>
            <thead>
              <tr>
                <th class="text-left">Keys</th>
                <th class="text-left">Action</th>
              </tr>
            </thead>
            <tbody>
              <tr v-for="shortcut in shortcuts" :key="shortcut.keys">
                <td
                  ><kbd>{{ shortcut.keys }}</kbd></td
                >
                <td>{{ shortcut.action }}</td>
              </tr>
            </tbody>
          </q-markup-table>
        </q-card-section>

        <q-card-actions align="right">
          <q-btn flat label="Close" color="primary" v-close-popup />
        </q-card-actions>
      </q-card>
    </q-dialog>
  </div>
</template>

<script setup>
import { ref } from 'vue'

const editor = ref('Press the help button at the end of the toolbar.')
const showHelp = ref(false)

const shortcuts = [
  { keys: 'TAB', action: 'Move between the toolbar and the editing area' },
  {
    keys: 'ARROW LEFT / ARROW RIGHT',
    action: 'Move between the toolbar buttons'
  },
  { keys: 'HOME / END', action: 'Jump to the first / last toolbar button' },
  { keys: 'CTRL + B', action: 'Bold' },
  { keys: 'CTRL + I', action: 'Italic' },
  { keys: 'CTRL + U', action: 'Underline' },
  { keys: 'CTRL + Z', action: 'Undo' },
  { keys: 'CTRL + Y', action: 'Redo' }
]
</script>
```

## Caveats

### Autocorrect & spellcheck

There may be occasions where you want to turn off the integrated autocorrect, autocomplete, autocapitalization and spelling correction "features" that many modern browsers offer. To do this, simply wrap the `<q-editor>` component in a `<form>` element, like this:

```html
<form
  autocorrect="off"
  autocapitalize="off"
  autocomplete="off"
  spellcheck="false"
>
  <q-editor v-model="editor" />
</form>
```

### Images

Pasting from the buffer and drag & dropping images into the editor is unfortunately different across browsers - and also highly dependent upon how the image got into the buffer in the first place. In fact, up until very recently, you could even resize images within the ContentEditable when using Firefox. If you want to allow image pasting / dropping, we highly recommend writing your own methods.

```html
<q-editor
  v-model="editor"
  @paste="evt => pasteCapture(evt)"
  @drop="evt => dropCapture(evt)"
/>
```

### Plaintext pasting

If the paste event content type is text and depending on the source of text, there may already be a great deal of markup that the contentEditable automatically parses. If you want to paste only "clean, markup-free" text, then you can use the approach in this example (which also turns off spelling correction as mentioned above):

### Paste Event Override

```vue
<template>
  <div class="q-pa-md q-gutter-sm">
    <form
      autocorrect="off"
      autocapitalize="off"
      autocomplete="off"
      spellcheck="false"
    >
      <q-editor ref="editorRef" @paste="onPaste" v-model="editor" />
    </form>
  </div>
</template>

<script setup>
import { ref, useTemplateRef } from 'vue'

const editorRef = useTemplateRef('editorRef')
const editor = ref(
  'Try pasting some rich text here,' +
    ' such as from Discord or Webstorm.' +
    "<br>You can't paste images either!!!"
)

/**
 * Capture the <CTL-V> paste event, only allow plain-text, no images.
 * See: https://stackoverflow.com/a/28213320
 */
function onPaste(evt) {
  // Let inputs do their thing, so we don't break pasting of links.
  if (evt.target.nodeName === 'INPUT') return
  let text, onPasteStripFormattingIEPaste
  evt.preventDefault()
  evt.stopPropagation()
  if (evt.originalEvent && evt.originalEvent.clipboardData.getData) {
    text = evt.originalEvent.clipboardData.getData('text/plain')
    editorRef.value.runCmd('insertText', text)
  } else if (evt.clipboardData && evt.clipboardData.getData) {
    text = evt.clipboardData.getData('text/plain')
    editorRef.value.runCmd('insertText', text)
  } else if (window.clipboardData && window.clipboardData.getData) {
    if (!onPasteStripFormattingIEPaste) {
      onPasteStripFormattingIEPaste = true
      editorRef.value.runCmd('ms-pasteTextOnly', text)
    }
    onPasteStripFormattingIEPaste = false
  }
}
</script>
```

### Printing

If you don't set a font (or the user doesn't choose one), the print dialogue will default to the system font, which can vary depending on browser and underlying operating system. Make sure to take this into consideration.

### Internationalization

The tooltips content of QEditor are translated by the [Quasar Language Pack](../options/quasar-language-packs.md), so merely changing the language will also change the interface. If your desired language pack is missing - or you find an error, please consider providing the update as PR.
