Skip to main content

Barra de ferramentas, visualização ao vivo e WYSIWYG na área de texto que você já possui

Última atualização:

Um editor de markdown que aprimora sua área de texto em vez de substituí-la, para que o envio de formulários, a validação e os campos obrigatórios continuem funcionando. Modos WYSIWYG e Markdown simples, visualização ao vivo, localização e substituição, suporte RTL, modo escuro. Funciona de forma independente com Django, Laravel, Rails, Node.js, PHP e qualquer pilha.

Não é necessário Frutjam e nem Tailwind. Os estilos são agrupados, então o editor pode ser usado em qualquer projeto.

Instalação

NPM (pacotes: Vite, webpack, Rollup, etc.)

bash
npm install markdown-text-editor
javascript
1
2
import MarkdownEditor from 'markdown-text-editor';
new MarkdownEditor('#markdown-editor');

As definições TypeScript são fornecidas com o pacote, portanto, opções, entradas da barra de ferramentas e formas variáveis ​​são verificadas e preenchidas automaticamente sem instalação extra.

CDN: módulo ES

html
1
2
3
4
<script type="module">
  import MarkdownEditor from 'https://cdn.jsdelivr.net/npm/markdown-text-editor/+esm';
  new MarkdownEditor('#markdown-editor');
</script>

CDN: etiqueta de script global (IIFE)

Nenhuma importação necessária: MarkdownEditor está disponível automaticamente como uma variável global.

html
1
2
3
4
5
6
7
8
9
<form action="/api/save" method="POST">
  <textarea id="markdown-editor" name="content"># Hello World</textarea>
  <button type="submit">Save Content</button>
</form>

<script src="https://cdn.jsdelivr.net/npm/markdown-text-editor"></script>
<script>
  new MarkdownEditor('#markdown-editor');
</script>

Demonstração do Editor de Markdown

Início rápido

Passe um objeto de opções para personalizar o editor. Todas as opções são opcionais: omita qualquer uma para usar o valor padrão.

javascript
1
2
3
4
const editor = new MarkdownEditor('#markdown-editor', {
  placeholder: 'Write your markdown...',
  toolbar: ['heading', 'bold', 'italic', 'strikethrough', 'ul', 'ol', 'checklist', 'blockquote', 'link', 'preview'],
});

A Filosofia: "Nativo Primeiro"

A maioria dos editores quebra o fluxo de trabalho padrão da web. MarkdownEditor adota isso. Porque fica diretamente em cima de um <textarea>, você não precisa aprender uma nova maneira de lidar com dados.

  • Não é necessária vinculação de dados: Funciona com <form method="POST"> fora da caixa
  • Acesso padrão: Usar document.getElementById('editor').value assim como uma entrada normal.
  • Backend Agnóstico: Funciona com qualquer backend (Python, Node.js, PHP, etc.) como um campo de formulário normal

MarkdownEditor vs EasyMDE/SimpleMDE

A maioria dos editores de markdown JavaScript (EasyMDE, SimpleMDE, editores baseados em CodeMirror) ocultam seu <textarea> e edite uma cópia, devolvendo o conteúdo quando o formulário for enviado. Isso funciona até que algo mais precise do valor: a required campo o navegador não consegue focar bloqueia totalmente o envio e .value ou FormData read before submit retorna uma string vazia, que quebra as proteções htmx, Turbo, autosave e unsaved-changes. MarkdownEditor é diferente. Ele estiliza a área de texto que você já possui e a deixa como o campo em que você digita, para que o valor esteja correto a cada momento.

RecursoEditor MarkdownEasyMDE/SimpleMDE
Textarea nativa preservada✅❌ Oculto, editado como cópia
Funciona com required campos✅❌ O navegador bloqueia o envio
.value corrija antes de enviar✅❌ Vazio até o formulário ser enviado
Serializa com FormData, htmx, turbo✅❌ Precisa da própria API do editor
Modo híbrido WYSIWYG✅❌
Localizar e substituir integrado✅❌
Suporte RTL✅ EmbutidoAtravés do CodeMirror direction opção
Manipuladores de eventos embutidos (CSP)✅ NenhumAlguns
Modo escuro / tema✅Limitado
Tamanho do pacote, compactado53 KB107 KB (JS + CSS)

Integração de Estrutura

Porque o MarkdownEditor preserva o nativo <textarea>, ele se integra a todas as estruturas de back-end sem nenhum código extra. Seu servidor recebe o conteúdo do markdown exatamente como receberia de qualquer campo de formulário padrão. Para React e Vue são necessárias algumas linhas, abordadas no final desta seção.

Django

Adicione um class ao seu widget textarea e inicialize o editor: request.POST['content'] funciona sem etapas extras.

python
1
2
3
4
5
6
7
8
# forms.py
class PostForm(forms.ModelForm):
    class Meta:
        model = Post
        fields = ['content']
        widgets = {
            'content': forms.Textarea(attrs={'class': 'markdown-editor'}),
        }
javascript
1
2
new MarkdownEditor('.markdown-editor');
// request.POST['content'] contains the markdown on submit

Laravel

Usar f.text_area com uma aula: $request->input('content') recebe a redução diretamente.

html
1
2
<textarea name="content" class="markdown-editor">{{ old('content') }}</textarea>
<script>new MarkdownEditor('.markdown-editor');</script>

Ruby nos trilhos

Funciona com form_with fora da caixa: params[:content] contém a marcação. Para Turbo Drive, use turbo:load em vez de DOMContentLoaded.

javascript
1
2
3
4
5
6
document.addEventListener('turbo:load', () => {
    document.querySelectorAll('.markdown-editor:not([data-mde-init])').forEach(el => {
        el.setAttribute('data-mde-init', 'true');
        new MarkdownEditor(el);
    });
});

Node.js/Expresso

req.body.content recebe a redução no envio do formulário: sem etapa de sincronização, sem extração personalizada.

html
1
2
3
<textarea name="content" class="markdown-editor"></textarea>
<script src="https://cdn.jsdelivr.net/npm/markdown-text-editor"></script>
<script>new MarkdownEditor('.markdown-editor');</script>

PHP

$_POST['content'] funciona exatamente como qualquer textarea padrão: insira-o e o manuseio do formulário existente não exigirá nenhuma alteração.

html
1
2
3
4
5
6
<form method="POST" action="save.php">
  <textarea name="content" class="markdown-editor"></textarea>
  <button type="submit">Save</button>
</form>
<script src="https://cdn.jsdelivr.net/npm/markdown-text-editor"></script>
<script>new MarkdownEditor('.markdown-editor');</script>

Reagir

Crie o editor em um efeito e destrua-o ao desmontar. Usar defaultValue em vez de value: o editor grava diretamente na área de texto, portanto, uma ligação controlada substituiria o que o usuário está digitando.

javascript
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
import { useEffect, useRef } from 'react';
import MarkdownEditor from 'markdown-text-editor';

function MarkdownField({ name, defaultValue = '', onChange }) {
    const ref = useRef(null);

    useEffect(() => {
        const editor = new MarkdownEditor(ref.current, { onChange });
        return () => editor.destroy();
    }, []);

    return &lt;textarea ref={ref} name={name} defaultValue={defaultValue} /&gt;;
}

Retornando editor.destroy() do efeito também abrange StrictMode, que executa efeitos duas vezes no desenvolvimento e, de outra forma, deixaria dois editores em uma área de texto. A matriz de dependência vazia é deliberada: as opções são lidas uma vez quando o editor é criado, portanto, executar novamente o efeito o destruiria e o reconstruiria a cada alteração.

Vista

A mesma ideia. Defina o valor inicial uma vez onMounted e não ligue :value, ou cada pressionamento de tecla emitido de volta v-model substituiria a área de texto.

javascript
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
&lt;script setup&gt;
import { ref, onMounted, onBeforeUnmount } from 'vue';
import MarkdownEditor from 'markdown-text-editor';

const el = ref(null);
const props = defineProps({ modelValue: { type: String, default: '' } });
const emit = defineEmits(['update:modelValue']);
let editor = null;

onMounted(() => {
    el.value.value = props.modelValue;
    editor = new MarkdownEditor(el.value, {
        onChange: value =&gt; emit('update:modelValue', value)
    });
});

onBeforeUnmount(() =&gt; editor?.destroy());
&lt;/script&gt;

&lt;template&gt;&lt;textarea ref="el"&gt;&lt;/textarea&gt;&lt;/template&gt;

Usado como <MarkdownField v-model="content" />. De forma simples você pode pular v-model inteiramente e leia o valor da área de texto no envio, como acontece com qualquer outra estrutura.

Configuração

Você pode personalizar totalmente o comportamento e a interface do editor passando um options objeto. Se você omitir uma opção, o valor padrão será usado.

As opções são lidas uma vez, quando o editor é construído. Alterá-los posteriormente não tem efeito: ligue destroy() e crie um novo editor.

PropriedadeTipoPadrãoPropósito
modestring'plain'Define a visualização inicial. Use híbrido para uma experiência WYSIWYG ou simples para sintaxe bruta.
placeholderstring'Write...'Texto mostrado quando o editor está vazio.
toolbararray[...]Define quais ferramentas aparecem e em que ordem.
footerfalso | objetotudo visívelControla a barra de status mostrada abaixo do editor. Definir como false para ocultá-lo completamente ou passar um objeto para alternar estatísticas individuais.
themestringinheritedDefine explicitamente o tema do editor (claro, escuro, snowberry, darkberry). Se omitido, o editor herda data-theme do elemento ancestral mais próximo ou do <textarea> em si.
minHeightnumber200Altura mínima em pixels para a qual o editor diminuirá quando o conteúdo for curto. Pares com maxHeight para definir o intervalo de crescimento automático.
maxHeightnumber500Altura máxima em pixels que o editor pode atingir fora do modo de tela cheia. Quando o conteúdo exceder essa altura, uma barra de rolagem aparecerá dentro do editor. O editor também possui uma alça de arrastar para que os usuários possam redimensioná-lo manualmente além desse limite.
rendererfunctionmarkedSubstitui o analisador de redução usado para a visualização. Recebe a string markdown e deve retornar uma string HTML.
sanitizerfunctionDOMPurifySubstitui o desinfetante HTML. Recebe o HTML renderizado e deve retornar o HTML seguro para exibição.
onChangefunctionundefinedRetorno de chamada disparado em cada alteração de conteúdo: digitação, ações na barra de ferramentas, desfazer/refazer e continuação de lista. Recebe a string de redução atual como seu único argumento.

🛠 Personalização da barra de ferramentas

A barra de ferramentas é modular. Você pode criar uma experiência mínima ou um conjunto de recursos completo modificando o array.

Ferramentas disponíveis

CategoriaChaves de ferramentas
Tipografiaheading, bold, italic, strikethrough, blockquote
Listasul (bullet), ol (numbered), checklist
Códigocode (em linha), codeblock (bloco cercado)
Inserçõeshr (regra horizontal), table (modelo de tabela)
Mídialink, image
Ediçãoundo, redo, indent, outdent
Visualizarpreview
Modelos{ variables: [...] }, configurado em linha

Referência de ferramenta

FerramentaDescrição
headingAbre um menu suspenso para selecionar o nível de título H1–H6
boldAtiva a formatação de texto em negrito.
italicAtiva a formatação de texto em itálico.
strikethroughPermite tachado de texto.
ol(Lista ordenada): Converte texto em formato de lista numerada.
ul(Lista não ordenada): converte o texto em uma lista com marcadores.
checklistAdiciona caixas de seleção ao seu texto, tornando-o excelente para tarefas, listas de tarefas ou acompanhamento do status de conclusão.
blockquoteDestaque o texto citado ou enfatizado.
codeQuebra o texto selecionado em crases únicos para código embutido. Clicar novamente remove os crases.
codeblockQuebra o texto selecionado em um bloco de código protegido com crase triplo. Clicar novamente remove as cercas.
hrInsere um --- regra horizontal na posição do cursor em sua própria linha.
tableInsere um modelo inicial de tabela de descontos 2x3 na posição do cursor.
imagePermite inserir imagens via sintaxe de markdown.
linkPermite adicionar hiperlinks ao seu texto.
undoPara reverter as últimas alterações.
redoPara reaplicar as últimas alterações desfeitas.
indentPara aumentar o nível de recuo.
outdentPara diminuir o nível de recuo.
previewAlterna uma visualização lado a lado em tela cheia. As caixas de seleção no painel de visualização são clicáveis ​​e atualizam a fonte de redução instantaneamente. Pressione Escape para sair da tela cheia. Se um cabeçalho fixo cobrir o editor em tela cheia, consulte --mte-fullscreen-z-index.
💡 Dicas de implementação:
  • Reordenação: os botões aparecem na ordem exata em que você os listou na matriz
  • Removendo: Simplesmente omita qualquer tecla (como image) da matriz para desabilitar esse recurso inteiramente para o usuário
  • Backup nativo: se você não fornecer um placeholder em JS, o plugin usará automaticamente o placeholder atributo do seu HTML <textarea>

📊 Rodapé (barra de status)

O rodapé fica abaixo do editor e mostra a linha, a coluna do cursor, a contagem de caracteres do documento e, opcionalmente, a contagem de palavras, tudo atualizado em tempo real. É visível por padrão e cada estatística pode ser alternada de forma independente.

ChaveTipoPadrãoDescrição
linebooleantrueMostra o número da linha atual.
colbooleantrueMostrar o número da coluna atual.
charsbooleantrueMostrar a contagem total de caracteres.
wordsbooleanfalseMostre a contagem total de palavras. Desativado por padrão: definido como true para habilitar.

Exemplos de uso

javascript
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
// Default — line, col, and chars visible
new MarkdownEditor('#editor');

// Disable the footer entirely
new MarkdownEditor('#editor', { footer: false });

// Hide only character count
new MarkdownEditor('#editor', { footer: { chars: false } });

// Hide line and column, keep character count
new MarkdownEditor('#editor', { footer: { line: false, col: false } });

// Show only line number
new MarkdownEditor('#editor', { footer: { col: false, chars: false } });

// Enable word count alongside the defaults
new MarkdownEditor('#editor', { footer: { words: true } });

// Show word count only
new MarkdownEditor('#editor', { footer: { line: false, col: false, chars: false, words: true } });

🔀 Modos de edição

MarkdownEditor oferece duas maneiras distintas de escrever e formatar seu conteúdo. Você pode alternar entre uma visualização tradicional focada na sintaxe ou uma experiência moderna com foco no visual.

  • plain (Padrão): Um ambiente Markdown limpo e de alto desempenho onde a sintaxe (como **bold** ou # heading) é visível. Ideal para desenvolvedores e puristas do Markdown
  • hybrid: uma experiência inspirada em WYSIWYG que renderiza a formatação (negrito, itálico, títulos) em tempo real enquanto você digita, mantendo a estrutura Markdown subjacente.
javascript
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// Default initialization (Plain Mode)
new MarkdownEditor('#markdown-editor');

// Explicit Plain Mode
new MarkdownEditor('#markdown-editor', {
    mode: 'plain'
});

// Hybrid (Visual) Mode
new MarkdownEditor('#markdown-editor', {
    mode: 'hybrid'
});

Visualização do modo híbrido e simples:

Modo Híbrido

A formatação visual é renderizada em tempo real enquanto você digita.

Modo Simples (Padrão)

Concentra-se na sintaxe bruta do Markdown para uma experiência leve.

🌙 Tema

MarkdownEditor herda automaticamente seu tema da página ao redor: nenhuma configuração é necessária. O editor lê data-theme do ancestral mais próximo na inicialização, para que fique sincronizado com o tema do seu site imediatamente.

Como o tema é resolvido (ordem de prioridade)

  1. theme opção: substituição explícita passada no objeto de opções
  2. data-theme no <textarea>: definido diretamente no elemento
  3. **data-theme em qualquer ancestral **: por ex. <html>, <body>ou um invólucro <div>

Temas disponíveis

'light' (padrão), 'dark', 'snowberry', 'darkberry'

Opção 1, herdar de <html> ou qualquer ancestral (configuração zero)
html
1
2
3
4
5
6
<html data-theme="dark">
  ...
  <textarea id="markdown-editor"></textarea>
  <script>
    new MarkdownEditor('#markdown-editor'); // picks up dark automatically
  </script>
Opção 2: definir data-theme diretamente no <textarea>
html
1
2
3
4
<textarea id="markdown-editor" data-theme="dark"></textarea>
<script>
  new MarkdownEditor('#markdown-editor');
</script>
Opção 3: explícito theme opção (substitui tudo)
javascript
1
2
3
new MarkdownEditor('#markdown-editor', {
    theme: 'dark'
});

🎨 Tema personalizado por meio de variáveis ​​CSS

Você pode personalizar totalmente a aparência do editor substituindo suas variáveis ​​CSS no .markdown-editor-wrapper elemento ou qualquer [data-theme] seletor. Todas as cores usam o Espaço de cores OKLCH para resultados perceptualmente uniformes.

VariávelPropósitoPadrão claroPadrão escuro
--color-basePlano de fundo do editoroklch(100% 0 0)oklch(10.9% 0 0)
--color-on-baseCor do texto principaloklch(22% 0 0)oklch(98% 0 0)
--color-primaryAcento primário (barra de ferramentas ativa, links)oklch(51.1% .262 277)oklch(66.4% .184 286)
--color-on-primaryTexto em superfícies de cores primáriasoklch(96.2% .018 272)oklch(10% .01 270)
--color-secondarySotaque secundáriooklch(59.1% .293 323)oklch(65% .18 220)
--color-accentRealçar acento (código embutido, itálico)oklch(54.1% .281 293)oklch(75% .18 50)
--color-neutralSuperfícies neutras (bordas, divisórias)oklch(15% 0 0)oklch(85% 0 0)
--color-errorCor do estado de errooklch(57.7% .245 27)oklch(60% .22 30)
--border-radiusArredondamento dos cantos da moldura do editor0.25rem
Exemplo de tema personalizado

Substitua qualquer variável em .markdown-editor-wrapper após a inicialização do editor ou defina um personalizado [data-theme] bloco na sua folha de estilo:

css
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
/* Override individual variables */
.markdown-editor-wrapper {
    --color-primary: oklch(60% 0.2 30);   /* orange accent */
    --border-radius: 0.5rem;
}

/* Or define a full custom theme */
[data-theme="brand"] .markdown-editor-wrapper,
.markdown-editor-wrapper[data-theme="brand"] {
    --color-base:       oklch(15% 0.01 250);
    --color-on-base:    oklch(95% 0 0);
    --color-primary:    oklch(65% 0.22 145);   /* green */
    --color-on-primary: oklch(10% 0 0);
    --color-accent:     oklch(75% 0.18 60);
    --color-neutral:    oklch(80% 0 0);
    --border-radius:    0.75rem;
}
javascript
new MarkdownEditor('#markdown-editor', { theme: 'brand' });

🏷 Variáveis

Adiciona um menu suspenso na barra de ferramentas para inserir espaços reservados. Útil quando a pessoa que escreve um documento não é o desenvolvedor que definiu a sintaxe do espaço reservado, como acontece com modelos de e-mail, fatura ou contrato: eles escolhem um nome legível e a sintaxe correta é inserida para eles.

javascript
1
2
3
4
5
6
7
8
9
new MarkdownEditor('#markdown-editor', {
    toolbar: ['bold', 'italic', 'link',
        { variables: [
            { label: 'Customer Name', value: '{{customer.name}}' },
            { label: 'Invoice No',    value: '{{invoice.number}}' }
        ]},
        'preview'
    ]
});

A ferramenta é configurada inline no toolbar array, então sua posição entre os outros botões depende de você. O botão lista os rótulos e clicar em um deles insere seu value no cursor, substituindo qualquer seleção. Nada será renderizado se a lista estiver vazia.

Valores de amostra na visualização

Uma entrada dada uma sample mostra essa amostra na visualização, enquanto a área de texto mantém o espaço reservado real. As entradas sem uma aparecem como estão escritas, então você pode misturar as duas.

javascript
1
2
3
4
5
6
7
{ variables: [
    { label: 'Customer Name', value: '{{customer.name}}', sample: 'Hannes' },
    { label: 'Invoice No',    value: '{{invoice.number}}' }
]}

// textarea : Hi {{customer.name}}, invoice {{invoice.number}}
// preview  : Hi Hannes, invoice {{invoice.number}}

Agrupamento

Dê uma entrada items em vez de um value para renderizar uma seção com cabeçalho. Entradas agrupadas e simples podem ser misturadas em uma lista.

javascript
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
{ variables: [
    { label: 'Today', value: '{{today}}', sample: '10 September 2026' },
    { label: 'Customer', items: [
        { label: 'Name',  value: '{{customer.name}}',  sample: 'Hannes' },
        { label: 'Email', value: '{{customer.email}}', sample: 'hannes@example.com' }
    ]},
    { label: 'Invoice', items: [
        { label: 'Number', value: '{{invoice.number}}' }
    ]}
]}

Coisas para saber

  • O editor nunca resolve variáveis. Ele insere texto e seu aplicativo substitui valores reais posteriormente, geralmente no lado do servidor. sample afeta apenas o que a visualização exibe.
  • Os rótulos são mostrados como texto simples, portanto a marcação em um rótulo nunca é renderizada.
  • Entradas malformadas são ignoradas em vez de lançadas, e o botão não é renderizado quando nada utilizável é configurado.

🖋 Renderizador personalizado

A visualização é renderizada com marcado e higienizado com DOMPurificar. Ambos podem ser substituídos. Use isto quando seu aplicativo já renderiza markdown com outra biblioteca e você deseja que a visualização corresponda exatamente à produção.

Ambas as opções são funções simples que pegam uma string e retornam uma string, portanto, qualquer analisador e sanitizador funcionam.

javascript
1
2
3
4
5
6
7
8
9
import MarkdownIt from 'markdown-it';
import taskLists from 'markdown-it-task-lists';
import MarkdownEditor from 'markdown-text-editor';

const md = new MarkdownIt({ linkify: true, breaks: true }).use(taskLists);

new MarkdownEditor('#markdown-editor', {
    renderer: markdown => md.render(markdown)
});

DOMPurify ainda roda na saída, então você mantém a proteção XSS sem configurar nada.

Desinfetante personalizado

Necessário apenas quando o padrão remove algo que seu renderizador emite. DOMPurify remove <iframe> por padrão, portanto, as incorporações de vídeo precisam ser permitidas explicitamente. DOMPurify deve ser importado em seu próprio código, pois a cópia que acompanha o editor é interna.

javascript
1
2
3
4
5
6
7
8
9
import DOMPurify from 'dompurify';

new MarkdownEditor('#markdown-editor', {
    renderer:  markdown => md.render(markdown),
    sanitizer: html => DOMPurify.sanitize(html, {
        ADD_TAGS: ['iframe'],
        ADD_ATTR: ['allow', 'allowfullscreen', 'frameborder']
    })
});

Usando uma tag de script em vez de um bundler, carregue DOMPurify junto com o editor:

html
1
2
<script src="https://cdn.jsdelivr.net/npm/dompurify"></script>
<script src="https://cdn.jsdelivr.net/npm/markdown-text-editor"></script>

Coisas para saber

  • Apenas visualização. A formatação ao vivo do modo híbrido usa um renderizador interno separado e não é afetada.
  • As listas de tarefas precisam de suporte de plug-in. As caixas de seleção clicáveis ​​são encontradas procurando por input[type="checkbox"] na saída, então markdown-é necessário markdown-it-task-lists. O editor registra um aviso se detectar a sintaxe da lista de tarefas e nenhuma caixa de seleção.
  • Ambos devem ser síncronos e retornar uma string. Um async escrita de função [object Promise] na visualização.
  • Substituir o desinfetante substitui sua proteção. Uma passagem como html => html desativa totalmente a higienização e é seguro apenas para conteúdo totalmente confiável.

🎨 Estilização de elementos internos

Variáveis ​​CSS cobrem a maioria dos temas porque são herdadas, portanto, definir uma .markdown-editor-wrapper chega à barra de ferramentas, botões, visualização e rodapé. Procure um nome de classe somente quando nenhuma variável expõe o que você precisa.

css
1
2
3
4
5
/* Variables set on the wrapper inherit down to every child */
.markdown-editor-wrapper {
    --border-radius: 12px;   /* rounds the editor and its toolbar buttons */
    --color-primary: oklch(60% 0.2 30);
}

Referência de classe

Esses nomes de classe são estáveis ​​e seguros para estilização.

AulaElemento
.markdown-editor-wrapperContêiner externo envolvendo todo o editor
.toolbarFaixa da barra de ferramentas acima da área de edição
.markdown-btnBotão individual da barra de ferramentas
.preview-btnO botão de alternância de visualização/tela cheia
.editor-layoutGrade contendo a área de edição e visualização lado a lado
.textarea-wrapperWrapper em torno da área de edição
.editor-textareaO elemento textarea subjacente
.display-layerCamada de formatação renderizada, somente modo híbrido
.preview-wrapperColuna de visualização
.preview-contentMarkdown renderizado dentro da visualização
.editor-footerBarra de status abaixo do editor
.find-replace-panelEncontre e substitua o painel
css
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
/* Some variables are set by the component on itself, which beats an
   inherited value. Target the element directly for those. */
.markdown-editor-wrapper .markdown-btn {
    --btn--font-size: 0.875rem;
}

/* And use classes for anything no variable exposes */
.markdown-editor-wrapper .toolbar {
    border-bottom: 2px solid oklch(60% 0.2 30);
}

Visando um editor

Com vários editores em uma página, direcione um deles com data-editor. O wrapper espelha o id de seu <textarea>, então <textarea id="notes"> te dá [data-editor="notes"]. O próprio id permanece na área de texto, então getElementById continua funcionando.

css
1
2
3
4
/* one editor only */
[data-editor="notes"] {
    --border-radius: 0;
}

🪟 Tela cheia e camadas (índice z)

Em tela cheia o editor usa z-index: 10000, que limpa as camadas que a maioria das estruturas de UI reserva para cabeçalhos fixos e sobreposições. Se um cabeçalho fixo ou barra lateral ainda cobrir o editor, aumente esse valor.

Substitua-o por --mte-fullscreen-z-index. O valor se aplica apenas em tela cheia e é herdado, portanto, defini-lo em qualquer ancestral abrange todos os editores abaixo dele.

css
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
/* every editor on the page */
.markdown-editor-wrapper {
    --mte-fullscreen-z-index: 20000;
}

/* one section only - the value inherits down */
#admin-panel {
    --mte-fullscreen-z-index: 20000;
}

/* a single editor, by its textarea id */
[data-editor="notes"] {
    --mte-fullscreen-z-index: 20000;
}

API de conteúdo

Um dos principais pontos fortes do MarkdownEditor é que ele mantém o <textarea> perfeitamente sincronizado. Esteja você usando uma estrutura JavaScript moderna ou um back-end tradicional como Django, PHP ou Laravel, o fluxo de trabalho permanece simples e nativo.

Lendo e escrevendo conteúdo

1. O jeito nativo (recomendado)

Como o editor aprimora uma área de texto padrão, você pode usar métodos DOM familiares. Esta é a maneira mais rápida de interagir com seus dados sem aprender uma nova API.

javascript
1
2
3
4
5
// Retrieve content via ID
const markdown = document.getElementById('markdown-editor').value;

// Set content via ID (The editor UI updates automatically)
document.getElementById('markdown-editor').value = "# New Heading Content";

2. Usando uma referência de variável

Se você tiver uma referência ao elemento textarea, poderá usá-lo diretamente: não é necessária nenhuma API específica da biblioteca.

javascript
1
2
3
4
5
6
7
const textarea = document.getElementById('markdown-editor');

// Retrieve content
const markdown = textarea.value;

// Set content (the editor UI reflects this immediately)
textarea.value = "## Updated via JS";

3. Configurando o conteúdo inicial do lado do servidor

A maneira recomendada de definir o conteúdo inicial é diretamente no <textarea> HTML: funciona naturalmente com todos os frameworks de backend (Django, Laravel, Rails, PHP, etc.) e o editor o renderiza automaticamente no init.

html
1
2
<!-- Recommended: set content server-side -->
<textarea id="markdown-editor"># Hello World</textarea>

Para ler ou atualizar conteúdo em tempo de execução, use o nativo textarea valor. Chamar editor.render() após uma atualização para atualizar a visualização e a camada híbrida.

javascript
1
2
3
4
5
6
7
8
const textarea = document.getElementById('markdown-editor');

// Read
const markdown = textarea.value;

// Update at runtime
textarea.value = '# New content';
editor.render();

4. Derrubando o editor: destroy()

Chamar editor.destroy() para remover o wrapper DOM do editor e restaurar o original <textarea> à sua posição no documento. Útil em aplicativos de página única ao desmontar uma visualização.

javascript
1
2
3
4
const editor = new MarkdownEditor('#markdown-editor');

// Remove the editor and restore the plain textarea
editor.destroy();

Reagindo às mudanças com onChange

Passe um onChange retorno de chamada para ser notificado sobre cada alteração de conteúdo. Recebe a string de redução atual.

javascript
1
2
3
4
5
const editor = new MarkdownEditor('#markdown-editor', {
    onChange(value) {
        console.log('Content changed:', value.length, 'characters');
    }
});

Rascunho salvo automaticamente com localStorage

Usar onChange para salvar um rascunho a cada pressionamento de tecla. Restaure-o preenchendo previamente a área de texto antes de inicializar o editor.

javascript
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
const DRAFT_KEY = 'my-page-draft';

// Restore saved draft before init (only if textarea starts empty)
const textarea = document.getElementById('markdown-editor');
const saved = localStorage.getItem(DRAFT_KEY);
if (saved && !textarea.value) textarea.value = saved;

// Save on every change
const editor = new MarkdownEditor('#markdown-editor', {
    onChange(value) {
        localStorage.setItem(DRAFT_KEY, value);
    }
});

// Clear draft after successful form submission
document.querySelector('form').addEventListener('submit', () => {
    localStorage.removeItem(DRAFT_KEY);
});

Envio de formulário

Como o MarkdownEditor é criado diretamente no software nativo <textarea>, é compatível com todos os frameworks de back-end (Django, Laravel, PHP, Ruby on Rails, etc.) imediatamente.

É aqui que brilha a filosofia “Native-First”. Você não precisa sincronizar os dados manualmente antes de enviar um formulário. O navegador trata o editor exatamente como um campo de entrada padrão.

html
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
<form method="POST" action="/api/submit">
  <textarea id="markdown-editor" name="content" class="h-48" rows="5">
    # Initial Content
  </textarea>

  <button type="submit">Submit to Server</button>
</form>

<script>
  // Just initialize it. That's it.
  new MarkdownEditor('#markdown-editor');
</script>

Observação: Inicialização do plugin MarkdownEditor obrigatória

Basta usar um HTML padrão <form>. O name O atributo na textarea é o que seu servidor usará para identificar o conteúdo.

🚀 Por que é uma virada de jogo para back-ends

Como o editor preserva o nativo <textarea> comportamento, seu servidor manipula os dados como uma string padrão. Não há necessidade de lógica extra: não preventDefault() e sem manual FormData construção.

💡 Por que este é um "recurso matador":

A maioria dos editores (como Quill, Editor.js, simpleMDE, easyMDE) salva dados em estruturas JSON complexas. Se um desenvolvedor usar isso, ele terá que reescrever o esquema do banco de dados e a lógica de renderização.

Com o MarkdownEditor, um desenvolvedor pode pegar um site antigo e substituir um site simples <textarea> com seu editor, e o backend nem sabe que mudou. Ele apenas recebe o mesmo texto bruto de sempre, mas o usuário obtém uma experiência 10 vezes melhor.

Estrutura / LinguagemComo acessar o conteúdo do Markdown
PHP$_POST['content']
Djangorequest.POST.get('content')
Node.js (Expresso)req.body.content
Laravel$request->input('content')
Ruby nos trilhosparams[:content]

🖼️ Upload avançado de imagens

Lidar com uploads de imagens nativamente: em vez de depender de strings Base64 lentas e com muita memória, é uma vitória significativa tanto para o desempenho quanto para o SEO.

Opções de configuração

A ferramenta de imagem suporta um fileInput configuração para lidar com uploads diretos do servidor.

  • accept: Defina uma matriz de formatos de imagem permitidos (por exemplo, 'webp', 'avif')
  • uploadUrl: especifique o endpoint de back-end onde o File objeto será enviado via POST
  • params: objeto opcional para enviar dados adicionais (como tokens CSRF, IDs de usuários ou nomes de pastas) junto com o arquivo de imagem

Exemplo de uso (configuração completa)

javascript
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
const options = {
  placeholder: 'Start writing...',
  toolbar: [
    'link',
    {
      image: {
        fileInput: {
            accept: ['webp', 'avif'], // restrict the image upload format
            uploadUrl: '/api/upload', // Your upload endpoint
            params: {
                _token: 'your_csrf_token_here', // Essential for Laravel/Django
                folder: 'blog_posts'
            }
        },
        // Supports boolean: true/false OR object: { required: true }
        altInput: { required: true }
      }
    },
    'preview'
  ],
}
const editor = new MarkdownEditor('#markdown-editor', options);

📡 Integração de Servidor

O Pedido

O editor envia um POST solicitar como multipart/form-data. Por padrão, inclui:

  • image_file: O objeto de arquivo real
  • image_alt: O texto alternativo inserido pelo usuário
  • ...mais quaisquer dados personalizados definidos no params objeto

A resposta necessária

Para confirmar um upload bem-sucedido e inserir a imagem no editor, seu servidor deve retornar a seguinte estrutura JSON:

json
1
2
3
4
{
  "success": true,
  "image_path": "https://cdn.yourdomain.com/uploads/image.webp"
}

Observação: Certifique-se de usar a chave image_path para o URL da imagem enviada.

Validação de texto alternativo da imagem (altInput)

Para garantir que seu conteúdo permaneça acessível e otimizado para SEO, o MarkdownEditor aplica a validação de texto alternativo por padrão.

  • Comportamento padrão: Se altInput não está definido, o padrão é { required: true }
  • Aplicar acessibilidade: os usuários serão impedidos de inserir uma imagem até que uma descrição alternativa seja fornecida

1. Padrão (nenhuma configuração necessária)

javascript
1
2
3
4
// Alt text is REQUIRED by default
image: {
  fileInput: { uploadUrl: '/api/upload' }
}

2. Abreviação (desativar validação)

Se você quiser permitir imagens sem descrições, basta definir o booleano como false.

javascript
1
2
3
image: {
  altInput: false // Users can now skip the alt text field
}

3. Baseado em objeto (explícito)

javascript
1
2
3
4
5
image: {
    altInput: {
        required: false // Disables alt text validation — users can skip the alt field
    }
}

Uso de imagem padrão (não fileInput)

Se fileInput não estiver configurado, o editor usará como padrão um modal simples baseado em URL. Isso é ideal se seus usuários estiverem vinculando principalmente a hosts de imagens externos.

javascript
1
2
3
4
5
6
7
8
const options = {
  toolbar: [
    'link',
    'image',
    'preview'
  ],
}
const editor = new MarkdownEditor('#markdown-editor', options);

💡 Por que usar parâmetros?

Em frameworks como Laravel ou Django, você não pode fazer upload de arquivos sem um token CSRF. Ao adicionar _token para o params objeto, sua solicitação passará perfeitamente pelo middleware de segurança do back-end, mantendo a filosofia "Zero Logic" para seus controladores do lado do servidor.

⌨️ Atalhos de teclado

Ações comuns de formatação podem ser acionadas diretamente no teclado, sem tocar na barra de ferramentas. Cada atalho também é mostrado na dica de ferramenta do botão da barra de ferramentas correspondente.

AtalhoAção
Ctrl + B  /  ⌘ BAlternar Negrito
Ctrl + I  /  ⌘ IAlternar Itálico
Ctrl + K  /  ⌘ KInserir link
Ctrl + `  /  ⌘ `Alternar in-line Code
Ctrl + Shift + S  /  ⌘ ⇧ SAlternar Tachado
Ctrl + 1  /  ⌘ 1Título 1
Ctrl + 2  /  ⌘ 2Título 2
Ctrl + 3  /  ⌘ 3Título 3
Ctrl + L  /  ⌘ LAlternar lista com marcadores
Ctrl + Z  /  ⌘ ZDesfazer
Ctrl + Shift + Z  /  ⌘ ⇧ ZRefazer
TabRecuar linhas selecionadas
Shift + TabRecuar linhas selecionadas
Ctrl + F  /  ⌘ FAbra o painel Localizar
Ctrl + H  /  ⌘ HAbra o painel Localizar e Substituir
Ctrl + Shift + F  /  ⌘ ⇧ FAlternar visualização em tela cheia
F11Alternar visualização em tela cheia
EscapeFechar painel Localizar / Sair da visualização em tela cheia

🔍 Localizar e substituir

Um painel integrado de localização e substituição está disponível dentro do editor: nenhuma extensão de navegador ou ferramenta separada é necessária.

  • Imprensa Ctrl + F (ou ⌘ F) para abrir o painel Localizar
  • Imprensa Ctrl + H (ou ⌘ H) para abrir o painel Localizar e substituir
  • A pesquisa não diferencia maiúsculas de minúsculas e mostra um contador de partidas ao vivo (por exemplo, 3 de 12)
  • Navegue pelas partidas com os botões ▲ / ▼ ou Enter / Shift + Enter
  • Substituir substitui a correspondência realçada atual; Substituir tudo substitui todas as ocorrências de uma só vez
  • Imprensa Escape para fechar o painel e retornar o foco ao editor

O painel flutua no canto superior direito da área de conteúdo do editor e não interrompe a escrita.

Características

🔌 Integração de formulário nativo

Funciona exatamente como um padrão <textarea>. Sem APIs complexas: basta usar o value ou name atributo. Ele "simplesmente funciona" com envios de formulários HTML padrão em PHP, Django ou Node.js.

🖼️ Upload avançado de imagens

Configure uploads de servidores nativos via API. Evite strings Base64 pesadas para garantir carregamentos de página mais rápidos e SEO superior hospedando imagens em seu próprio CDN.

🔀 Modos Híbrido e Simples

Alternar entre um Híbrido (WYSIWYG) experiência em edição visual ou Remarcação simples modo para uma sensação de codificação tradicional.

🚀 Alto desempenho

UM 53 KB compactados pacote (252 KB minificado, CSS incluído) otimizado para "Conteúdo Pesado". Lida com documentos e arquivos grandes sem qualquer atraso de entrada ou queda de desempenho. Atualizações de visualização eliminadas, cálculos de estilo em cache e manuseio de teclado sem conflitos: portanto, Tab e Enter sempre fazem exatamente uma coisa.

🌍 Suporte RTL integrado

Suporte nativo para idiomas da direita para a esquerda, como árabe, urdu e farsi. Perfeito para criar aplicativos acessíveis globalmente.

✨ Destaque de sintaxe

Legibilidade aprimorada com código claro e formatação remarcada.

🌙 Tema Adaptativo

Inclui suporte automático ao Modo Escuro. Ele sincroniza com as configurações do sistema ou com o IU do Frutjam biblioteca para uma experiência visual perfeita.

📝 Edição Inteligente

Continuação de lista automática no estilo GitHub para listas ordenadas, listas não ordenadas e listas de verificação: pressione Enter e o editor continua o padrão. As caixas de seleção no painel de visualização podem ser clicadas e sincronizadas instantaneamente com a fonte de redução.

📱 Totalmente responsivo

Uma interface de usuário fluida e voltada para dispositivos móveis que se adapta perfeitamente a desktops, tablets e smartphones para edição em qualquer lugar.

📦 Suporte universal

Compatível com ESM, UMD, CommonJS e IIFE. Funciona imediatamente via CDN (<script src>), npm ou qualquer bundler (Vite, webpack, Rollup): nenhuma configuração extra necessária.

♿ Acessível por padrão

Suporte completo para ARIA integrado: ponto de referência da barra de ferramentas, região de visualização rotulada, botões fáceis de ler na tela, aria-pressed no botão de visualização, disabled e aria-disabled em ferramentas inativas e restauração correta do foco quando os modais fecham.

🛡️ Zero conflitos de CSS

Os estilos do editor têm escopo total para .markdown-editor-wrapper. A comprovação global do Tailwind é excluída para que o editor viva com segurança ao lado do Bootstrap, Tailwind ou qualquer outra estrutura sem quebrar seus estilos.

⌨️ Atalhos de teclado

Ctrl+B, Ctrl+I, Ctrl+K, Ctrl+`, Ctrl+Shift+S: ações comuns de formatação sem tocar no mouse. Cada atalho é mostrado na dica de ferramenta do botão da barra de ferramentas.

🔍 Localizar e substituir

Imprensa Ctrl+F encontrar ou Ctrl+H para abrir localizar e substituir. Pesquisa sem distinção entre maiúsculas e minúsculas com contador de correspondências ao vivo, navegação seguinte/anterior, substituição única e substituição de todas: sem sair do editor.

🔒 Visualização segura do XSS

A visualização renderizada é higienizada via DOMPurificar antes de ser gravado no DOM. Tags de script, manipuladores de eventos in-line e URLs maliciosos na entrada de markdown criada são removidos automaticamente: nenhuma configuração é necessária.

▶️ Visualização em tempo real

Veja sua redução renderizada instantaneamente enquanto você digita.

🔗 Fácil integração

Integre-se perfeitamente a qualquer projeto web com configuração mínima.

🛠️ Barra de ferramentas personalizável

Configure e reordene dinamicamente as opções da barra de ferramentas, como negrito, itálico e muito mais.

Exemplo de configuração completa

Use este exemplo abrangente para inicializar o MarkdownEditor com todos os recursos principais, incluindo ordenação personalizada da barra de ferramentas e gerenciamento avançado de upload de imagens.

javascript
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
const editor = new MarkdownEditor('#markdown-editor', {
    mode: 'hybrid',
    placeholder: 'Start writing...',
    footer: {
        line: true,
        col: true,
        chars: true,
        words: true,
    },
    onChange(value) {
        console.log('Content updated:', value.length, 'characters');
    },
    toolbar: [
        'heading', 'bold', 'italic', 'strikethrough', 'blockquote',
        'ul', 'ol', 'checklist',
        'code', 'codeblock', 'hr', 'table',
        {
            image: {
                fileInput: {
                    accept: ['webp', 'avif', 'png'],
                    uploadUrl: '/api/upload'
                }
            }
        },
        'link', 'undo', 'redo', 'indent', 'outdent', 'preview'
    ],
});

// Read content natively
const markdown = document.getElementById('markdown-editor').value;

// destroy() when the view unmounts (SPAs)
// editor.destroy();

Achou isso útil?

Uma estrela do GitHub ajuda outros desenvolvedores a descobrir o editor. Faz parte Frutjam. Uma estrela ali também ajuda.

Estrela no GitHub
Edit page

Última atualização: