Nightly Patch Machine

    TypeScript icon, indicating that this package has built-in type declarations

    1.0.1 • Public • Published

    Textarea Markdown

    CI status codecov NPM version

    npm install textarea-markdown-editor

    There are some breaking changes since v1.0.0 released, checkout the release note

    Textarea Markdown is a simple markdown editor using only <textarea/>. It extends textarea by adding formatting features like shortcuts, list-wrapping, invoked commands and other to make user experience better 🙃

    Essentially this library just provides the textarea Component. You can choose any markdown parser, create your own layout, and use your own textarea component that is styled and behaves however you like


    • Lists wrapping
    • Auto formatting pasted links
    • Indent tabulation
    • Keyboard shortcuts handling
    • 17 built-in customizable commands


    import React, { Fragment, useRef, useState } from 'react';
    import TextareaMarkdown, { TextareaMarkdownRef } from 'textarea-markdown-editor';
    function App() {
        const [value, setValue] = useState('');
        const ref = useRef<TextareaMarkdownRef>(null);
        return (
                <button onClick={() => ref.current?.trigger('bold')}>Bold</button>
                <br />
                <TextareaMarkdown ref={ref} value={value} onChange={(e) => setValue(} />

    ℹ️ Ref instance provide the trigger function to invoke commands

    Custom textarea Component

    You can use custom textarea Component. Just wrap it with TextareaMarkdown.Wrapper

    import React, { useRef, useState } from 'react';
    import TextareaMarkdown, { TextareaMarkdownRef } from 'textarea-markdown-editor';
    import TextareaAutosize from 'react-textarea-autosize';
    function App() {
        const [value, setValue] = useState('');
        const ref = useRef<TextareaMarkdownRef>(null);
        return (
            <TextareaMarkdown.Wrapper ref={ref}>
                <TextareaAutosize value={value} onChange={(e) => setValue(} />

    ℹ️ This solution will not create any real dom wrapper

    Customize commands

    You can specify or overwrite shortcuts for built-in commands or create your own

    import React, { useRef, useState } from 'react';
    import TextareaMarkdown, { CommandHandler, TextareaMarkdownRef } from 'textarea-markdown-editor';
    /** Inserts 🙃 at the current position and select it */
    const emojiCommandHandler: CommandHandler = ({ cursor }) => {
        // MARKER - means a cursor position, or a selection range if specified two markers
    function App() {
        const [value, setValue] = useState('');
        const ref = useRef<TextareaMarkdownRef>(null);
        return (
                <button onClick={() => ref.current?.trigger('insert-emoji')}>Insert 🙃</button>
                <br />
                    onChange={(e) => setValue(}
                            name: 'code',
                            shortcut: ['command+/', 'ctrl+/'],
                            shortcutPreventDefault: true,
                            name: 'insert-emoji',
                            handler: emojiCommandHandler,

    ℹ️ Note that mutation element.value will not trigger change event on textarea element. Use cursor.setValue(...) or other method of Cursor.

    ℹ️ Mousetrap.js is used under the hood for shortcuts handling. It is great solution with simple and intuitive api. You can read more about combination in the documentation

    β Usage without react

    import { bootstrapTextareaMarkdown } from 'textarea-markdown-editor/dist/bootstrap';
    const textarea = document.querySelector('textarea'); // element can be obtained from anywhere, this is just an example;
    const { trigger, dispose } = bootstrapTextareaMarkdown(textarea, {
        options: {}, // optional options config
        commands: [], // optional commands configs

    ℹ️ Checkout sandbox example

    ℹ️ Although this is possible, this feature is more of a workaround, since the library was originally written to be used with react, your package manager probably will warn you about missing peer-dependencies

    👀 You can find more examples here



    ℹ️ TextareaMarkdown accepts all props which native textarea supports

    options TextareaMarkdownOptions

    Options config

    commands Command[]

    Array of commands configuration


    Name Type Description
    name TType Built-in or custom command name
    shortcut? string | string[] Shortcut combinations (Mousetrap.js)
    shortcutPreventDefault? boolean Toggle key event prevent default:false
    handler? CommandHandler Handler function for custom commands
    enable? boolean Toggle command enabling


    export type CommandHandler = (context: CommandHandlerContext) => void | Promise<void>;
    export type CommandHandlerContext = {
        textarea: HTMLTextAreaElement;
        cursor: Cursor;
        keyEvent?: KeyboardEvent;
        clipboardEvent?: ClipboardEvent;
        options: TextareaMarkdownOptions;

    Built-in commands

    Name Description Shortcut
    bold Inserts or wraps bold markup ctrl/command+b
    italic Inserts or wraps italic markup ctrl/command+i
    strike-through Inserts or wraps strike-through markup ctrl/command+shift+x
    link Inserts or wraps link markup
    image Inserts or wraps image markup
    unordered-list Inserts or wraps unordered list markup
    ordered-list Inserts or wraps ordered list markup
    code-block Inserts or wraps code block markup
    code-inline Inserts or wraps inline code markup
    code Inserts or wraps inline or block code markup dependent of selected
    block-quotes Inserts or wraps block-quotes markup
    h1 Inserts h1 headline
    h2 Inserts h2 headline
    h3 Inserts h3 headline
    h4 Inserts h4 headline
    h5 Inserts h5 headline
    h6 Inserts h6 headline


    Name Type Description
    preferredBoldSyntax "**" | "__" Preferred bold wrap syntax default: '**'
    preferredItalicSyntax "*" | "_" Preferred italic wrap syntax default: '*'
    preferredUnorderedListSyntax "-" | "*" | "+" Preferred unordered list prefix default: '-'
    enableIndentExtension boolean Will handle tab and shift+tab keystrokes, on which will insert/remove indentation instead of the default behavior default:true
    enableLinkPasteExtension boolean Will handle paste event, on which will wrap pasted with link/image markup if pasted is URL default:true
    enablePrefixWrappingExtension boolean Will handle enter keystroke, on which will wrap current list sequence if needed default:true
    enableProperLineRemoveBehaviorExtension boolean Will handle command/ctrl+backspace keystrokes, on which will remove only a current line instead of the default behavior default:true
    customPrefixWrapping (PrefixWrappingConfig | string)[] Array of custom prefixes, that need to be wrapped. (Will not work with enablePrefixWrappingExtension:false)
    blockQuotesPlaceholder string default: 'quote'
    boldPlaceholder string default: 'bold'
    codeBlockPlaceholder string default: 'code block'
    codeInlinePlaceholder string default: 'code'
    headlinePlaceholder string | (level: number) => string default: (lvl) => 'headline ' + lvl
    imageTextPlaceholder string Used inside default image markup ![<example>](...) default: 'example'
    imageUrlPlaceholder string Used inside default image markup ![...](<image.png>) default: 'image.png'
    italicPlaceholder string default: 'italic'
    linkTextPlaceholder string Used inside default link markup [<example>](...) default: 'example'
    linkUrlPlaceholder string Used inside default image markup ![...](<url>) default: 'url'
    orderedListPlaceholder string default: 'ordered list'
    strikeThroughPlaceholder string default: 'strike through'
    unorderedListPlaceholder string default: 'unordered list'


    ℹ️ Extends HTMLTextAreaElement instance

    trigger: (command: string) => void;
    cursor: Cursor


    npm i textarea-markdown-editor

    DownloadsWeekly Downloads






    Unpacked Size

    77.6 kB

    Total Files


    Last publish


    • resetand