{"id":13422177,"url":"https://github.com/RIP21/react-simplemde-editor","last_synced_at":"2025-03-15T10:31:43.231Z","repository":{"id":2890037,"uuid":"47739963","full_name":"RIP21/react-simplemde-editor","owner":"RIP21","description":"React wrapper for simplemde (easymde) markdown editor ","archived":false,"fork":false,"pushed_at":"2023-10-07T07:00:52.000Z","size":7086,"stargazers_count":771,"open_issues_count":11,"forks_count":103,"subscribers_count":8,"default_branch":"master","last_synced_at":"2024-10-29T15:49:12.393Z","etag":null,"topics":["easymde","editor","markdown","marked","marked-js","react","simplemde"],"latest_commit_sha":null,"homepage":"https://react-simplemde-edtior.netlify.com/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/RIP21.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2015-12-10T05:27:33.000Z","updated_at":"2024-10-16T17:40:33.000Z","dependencies_parsed_at":"2023-07-05T19:33:08.771Z","dependency_job_id":"ff7bfad8-70d2-4847-bbea-9567e6c93cdb","html_url":"https://github.com/RIP21/react-simplemde-editor","commit_stats":{"total_commits":208,"total_committers":35,"mean_commits":5.942857142857143,"dds":0.7644230769230769,"last_synced_commit":"6e36bf67265fa03a337948aa6a1a735eef0847a1"},"previous_names":["benrlodge/react-simplemde-editor"],"tags_count":29,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RIP21%2Freact-simplemde-editor","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RIP21%2Freact-simplemde-editor/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RIP21%2Freact-simplemde-editor/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RIP21%2Freact-simplemde-editor/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/RIP21","download_url":"https://codeload.github.com/RIP21/react-simplemde-editor/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243719123,"owners_count":20336592,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["easymde","editor","markdown","marked","marked-js","react","simplemde"],"created_at":"2024-07-30T23:00:38.583Z","updated_at":"2025-03-15T10:31:42.827Z","avatar_url":"https://github.com/RIP21.png","language":"TypeScript","funding_links":[],"categories":["UI Components","TypeScript"],"sub_categories":["Form Components"],"readme":"# React SimpleMDE (EasyMDE) Markdown Editor\n\n[![NPM version][npm-badge]][npm]\n\nReact component wrapper for\n[EasyMDE (the most fresh SimpleMDE fork)](https://github.com/Ionaru/easy-markdown-editor).\n\nOnly two dependencies, React (peer) and EasyMDE (peer).\n\nBuilt by [@RIP21](https://twitter.com/rip212) 👨‍💻\n\n- [New in v5](#new-in-v5)\n- [Install](#install)\n- [Demo](#demo)\n- [Usage](#usage)\n  - [Controlled usage](#controlled-usage)\n  - [Options](#options)\n  - [Hotkeys](#hotkeys)\n  - [Custom preview rendering example](#custom-preview-rendering-example)\n  - [Events / Additional event listeners for events of CodeMirror](#events--additional-event-listeners-for-events-of-codemirror)\n  - [Autosaving](#autosaving)\n  - [Retrieve `easymde`, `codemirror` or `cursor` info to be able to manipulate it.](#retrieve-easymde-codemirror-or-cursor-info-to-be-able-to-manipulate-it)\n  - [Basic testing](#basic-testing)\n- [API](#api)\n  - [Props](#props)\n  - [All exports list](#all-exports-list)\n- [Changelog](#changelog)\n  - [New in v4](#new-in-v4)\n  - [New in v3](#new-in-v3)\n  - [New in v2](#new-in-v2)\n\n\u003csmall\u003e\u003ci\u003e\u003ca href='http://ecotrust-canada.github.io/markdown-toc/'\u003eTable of contents generated with markdown-toc\u003c/a\u003e\u003c/i\u003e\u003c/small\u003e\n\n## New in v5\n\n- [breaking] Full rewrite to hooks. Means more reactive so, probably, less bugs related with updates. Minimum React version required `\u003e=16.8.2`\n- [breaking] `easymde` now a peer dependency, please install it manually\n- [breaking] `label` prop has been removed\n- [breaking] SSR safe nets removed, please make sure to import it dynamically\n- [breaking] `options` shall be memoized to prevent new instances from being created on each render and other related to that bugs (more on that below)\n- [potentially-breaking] Forwards `ref`, so you can easily get access to `div` wrapper by using `ref` prop.\n- [potentially-breaking] Lots of bugs fixed, examples updated\n- [potentially-breaking] `@babel/runtime` helpers are no longer inlined but imported.\n\n## Install\n\n```\nnpm install --save react-simplemde-editor easymde\n```\n\nNote: Possibly you may need to install `@babel/runtime`, try without it, but if you don't have any issues, then you shouldn't.\n\n## Demo\n\n[Hosted demo](https://react-simplemde-edtior.netlify.com/)\n\nor to see it locally:\n\n```\ngit clone https://github.com/RIP21/react-simplemde-editor.git\ncd react-simplemde-editor\nyarn install\nyarn demo\nopen browser at localhost:3000\n```\n\n## Usage\n\nView the [demo code](https://github.com/rip21/react-simplemde-editor/tree/master/src/demo) for more examples.\n\nAll examples below are in TypeScript\n\nUncontrolled usage\n\n```tsx\nimport React from \"react\";\nimport SimpleMDE from \"react-simplemde-editor\";\nimport \"easymde/dist/easymde.min.css\";\n\n\u003cSimpleMDE /\u003e;\n```\n\n### Controlled usage\n\n```tsx\nexport const ControlledUsage = () =\u003e {\n  const [value, setValue] = useState(\"Initial value\");\n\n  const onChange = useCallback((value: string) =\u003e {\n    setValue(value);\n  }, []);\n\n  return \u003cSimpleMdeReact value={value} onChange={onChange} /\u003e;\n};\n```\n\n### Options\n\nYou can set API of [SimpleMDE options](https://github.com/Ionaru/easy-markdown-editor#configuration) which you pass down as a `options` prop.\nIf you're using TypeScript it will be inferred by compiler.\n\nNote: if you don't specify a custom id it will automatically generate an id for you.\n\nNote that you need to `useMemo` to memoize `options` so they do not change on each rerender! It will affect behavior and performance\nbecause then on each render of the parent that renders `SimpleMdeReact` you'll get a new instance of the editor, which you definitely want to avoid!\nAlso, if you change `options` on each `value` change you will lose focus.\nSo, put `options` as a `const` outside of the component, or if `options` shall be partially or fully set by `props` make sure to `useMemo` in\ncase of functional/hooks components, or class field for `class` based components.\nSlightly more on that here: [#164](https://github.com/RIP21/react-simplemde-editor/issues/164)\n\n```tsx\nexport const UsingOptions = () =\u003e {\n  const [value, setValue] = useState(\"Initial\");\n\n  const onChange = useCallback((value: string) =\u003e {\n    setValue(value);\n  }, []);\n\n  const autofocusNoSpellcheckerOptions = useMemo(() =\u003e {\n    return {\n      autofocus: true,\n      spellChecker: false,\n    } as SimpleMDE.Options;\n  }, []);\n\n  return (\n    \u003cSimpleMdeReact\n      options={autofocusNoSpellcheckerOptions}\n      value={value}\n      onChange={onChange}\n    /\u003e\n  );\n};\n```\n\n### Hotkeys\n\nYou can include key maps using the `extraKeys` prop.\nRead more at [CodeMirror extra keys](https://codemirror.net/doc/manual.html#option_extraKeys)\n\n```tsx\nexport const UpdateableByHotKeys = () =\u003e {\n  const extraKeys = useMemo\u003cKeyMap\u003e(() =\u003e {\n    return {\n      Up: function (cm) {\n        cm.replaceSelection(\" surprise. \");\n      },\n      Down: function (cm) {\n        cm.replaceSelection(\" surprise again! \");\n      },\n    };\n  }, []);\n\n  const [value, setValue] = useState(\"initial\");\n  const onChange = (value: string) =\u003e setValue(value);\n\n  return (\n    \u003cSimpleMdeReact value={value} onChange={onChange} extraKeys={extraKeys} /\u003e\n  );\n};\n```\n\n### Custom preview rendering example\n\n```tsx\nimport ReactDOMServer from \"react-dom/server\";\n\nexport const CustomPreview = () =\u003e {\n  const customRendererOptions = useMemo(() =\u003e {\n    return {\n      previewRender() {\n        return ReactDOMServer.renderToString(\n          \u003cReactMarkdown\n            source={text}\n            renderers={{\n              CodeBlock: CodeRenderer,\n              Code: CodeRenderer,\n            }}\n          /\u003e\n        );\n      },\n    } as SimpleMDE.Options;\n  }, []);\n\n  return (\n    \u003cdiv\u003e\n      \u003ch4\u003eCustom preview\u003c/h4\u003e\n      \u003cSimpleMdeReact options={customRendererOptions} /\u003e\n    \u003c/div\u003e\n  );\n};\n```\n\n### Events / Additional event listeners for events of CodeMirror\n\nSee full list of events [here](https://codemirror.net/doc/manual.html#events)\n\n```tsx\nimport { SimpleMdeReact } from \"react-simplemde-editor\";\nimport type { SimpleMdeToCodemirrorEvents } from \"react-simplemde-editor\";\n\nexport const CustomEventListeners = () =\u003e {\n  const [value, setValue] = useState(\"Initial value\");\n\n  const onChange = useCallback((value: string) =\u003e {\n    setValue(value);\n  }, []);\n\n  // Make sure to always `useMemo` all the `options` and `events` props to ensure best performance!\n  const events = useMemo(() =\u003e {\n    return {\n      focus: () =\u003e console.log(value),\n    } as SimpleMdeToCodemirrorEvents;\n  }, []);\n\n  return \u003cSimpleMdeReact events={events} value={value} onChange={onChange} /\u003e;\n};\n```\n\n### Autosaving\n\n```tsx\nexport const Autosaving = () =\u003e {\n  const delay = 1000;\n  const autosavedValue = localStorage.getItem(`smde_demo`) || \"Initial value\";\n  const anOptions = useMemo(() =\u003e {\n    return {\n      autosave: {\n        enabled: true,\n        uniqueId: \"demo\",\n        delay,\n      },\n    };\n  }, [delay]);\n\n  return (\n    \u003cSimpleMdeReact id=\"demo\" value={autosavedValue} options={anOptions} /\u003e\n  );\n};\n```\n\n### Retrieve `easymde`, `codemirror` or `cursor` info to be able to manipulate it.\n\n```tsx\nexport const GetDifferentInstances = () =\u003e {\n  // simple mde\n  const [simpleMdeInstance, setMdeInstance] = useState\u003cSimpleMDE | null\u003e(null);\n\n  const getMdeInstanceCallback = useCallback((simpleMde: SimpleMDE) =\u003e {\n    setMdeInstance(simpleMde);\n  }, []);\n\n  useEffect(() =\u003e {\n    simpleMdeInstance \u0026\u0026\n      console.info(\"Hey I'm editor instance!\", simpleMdeInstance);\n  }, [simpleMdeInstance]);\n\n  // codemirror\n  const [codemirrorInstance, setCodemirrorInstance] = useState\u003cEditor | null\u003e(\n    null\n  );\n  const getCmInstanceCallback = useCallback((editor: Editor) =\u003e {\n    setCodemirrorInstance(editor);\n  }, []);\n\n  useEffect(() =\u003e {\n    codemirrorInstance \u0026\u0026\n      console.info(\"Hey I'm codemirror instance!\", codemirrorInstance);\n  }, [codemirrorInstance]);\n\n  // line and cursor\n  const [lineAndCursor, setLineAndCursor] = useState\u003cPosition | null\u003e(null);\n\n  const getLineAndCursorCallback = useCallback((position: Position) =\u003e {\n    setLineAndCursor(position);\n  }, []);\n\n  useEffect(() =\u003e {\n    lineAndCursor \u0026\u0026\n      console.info(\"Hey I'm line and cursor info!\", lineAndCursor);\n  }, [lineAndCursor]);\n\n  return (\n    \u003cdiv\u003e\n      \u003ch4\u003eGetting instance of Mde and codemirror and line and cursor info\u003c/h4\u003e\n      \u003cSimpleMdeReact\n        value=\"Go to console to see stuff logged\"\n        getMdeInstance={getMdeInstanceCallback}\n        getCodemirrorInstance={getCmInstanceCallback}\n        getLineAndCursor={getLineAndCursorCallback}\n      /\u003e\n    \u003c/div\u003e\n  );\n};\n```\n\n### Basic testing\n\nHere is how you do it. It requires mock of certain browser pieces to work, but this is whole example.\n\n```tsx\nimport { act, render, screen } from \"@testing-library/react\";\nimport { useState } from \"react\";\nimport { SimpleMdeReact } from \"react-simplemde-editor\";\nimport userEvent from \"@testing-library/user-event\";\n\n// @ts-ignore\nDocument.prototype.createRange = function () {\n  return {\n    setEnd: function () {},\n    setStart: function () {},\n    getBoundingClientRect: function () {\n      return { right: 0 };\n    },\n    getClientRects: function () {\n      return {\n        length: 0,\n        left: 0,\n        right: 0,\n      };\n    },\n  };\n};\n\nconst Editor = () =\u003e {\n  const [value, setValue] = useState(\"\");\n  return \u003cSimpleMdeReact value={value} onChange={setValue} /\u003e;\n};\n\ndescribe(\"Renders\", () =\u003e {\n  it(\"succesfully\", async () =\u003e {\n    act(() =\u003e {\n      render(\u003cEditor /\u003e);\n    });\n    const editor = await screen.findByRole(\"textbox\");\n    userEvent.type(editor, \"hello\");\n    expect(screen.getByText(\"hello\")).toBeDefined();\n  });\n});\n```\n\n## API\n\n### Props\n\n```tsx\nexport interface SimpleMDEReactProps\n  extends Omit\u003cReact.HTMLAttributes\u003cHTMLDivElement\u003e, \"onChange\"\u003e {\n  id?: string;\n  onChange?: (value: string, changeObject?: EditorChange) =\u003e void;\n  value?: string;\n  extraKeys?: KeyMap;\n  options?: SimpleMDE.Options;\n  events?: SimpleMdeToCodemirrorEvents;\n  getMdeInstance?: GetMdeInstance;\n  getCodemirrorInstance?: GetCodemirrorInstance;\n  getLineAndCursor?: GetLineAndCursor;\n  placeholder?: string;\n  textareaProps?: Omit\u003c\n    React.HTMLAttributes\u003cHTMLTextAreaElement\u003e,\n    \"id\" | \"style\" | \"placeholder\"\n  \u003e;\n}\n```\n\n### All exports list\n\n`default` - SimpleMdeReact \u003cbr\u003e\u003cbr\u003e\n`SimpleMdeReact` - same as `default` but named\u003cbr\u003e\u003cbr\u003e\n**Types:** \u003cbr\u003e\u003cbr\u003e\n`SimpleMdeReactProps` - props of the component \u003cbr\u003e\u003cbr\u003e\n`DOMEvent` - certain events that are used to get events exported below \u003cbr\u003e\u003cbr\u003e\n`CopyEvents` - only copy codemirror events \u003cbr\u003e\u003cbr\u003e\n`GlobalEvents` - some other global codemirror events \u003cbr\u003e\u003cbr\u003e\n`DefaultEvent` - default codemirror event handler function \u003cbr\u003e\u003cbr\u003e\n`IndexEventsSignature` - index signature that expects string as key and returns `DefaultEvent` \u003cbr\u003e\u003cbr\u003e\n`SimpleMdeToCodemirrorEvents` - manually crafted events (based off `@types/codemirror@0.0.109` that `easymde` uses internally) +\nall the above merged together into whole mapping between Codemirror event names and actual handlers for\n`events` prop \u003cbr\u003e\u003cbr\u003e\n`GetMdeInstance` - signature of the callback function that retrieves mde instance \u003cbr\u003e\u003cbr\u003e\n`GetCodemirrorInstance` - signature of the callback function that retrieves codemirror instance \u003cbr\u003e\u003cbr\u003e\n`GetLineAndCursor` - signature of the callback function that retrieves line and cursor info \u003cbr\u003e\u003cbr\u003e\n\n## Changelog\n\n## New in v4\n\n- Now uses [EasyMDE (the most fresh SimpleMDE fork)](https://github.com/Ionaru/easy-markdown-editor)\n  instead of `simplemde` itself. Possible breaking changes, so I bumped version to v4.\n- One obvious breaking change. Is how CSS is have to be imported. It used to be `simplemde/dist/simplemde.min.css` now it will be `easymde/dist/easymde.min.css`\n\n## New in v3\n\n- The `initialValue` prop has been removed and replaced with a `value` prop, allowing direct changes to the value to be made after the component mounts.\n- v3.6.8 if rendering server-side, you can set static ids to avoid errors in rendering synchronization.\n- v3.6.17 TypeScript typings added.\n- v3.6.19 All props will be passed to the wrapper now (except a id, onChange and few others that are ignored)\n- v3.6.21 React 17 support (UNSAFE methods are no longer used)\n\n## New in v2\n\nVersion 1.0 did not have SimpleMDE options configured well, this readme reflects the changes made to better include options.\nThis is still a very new project. Testing, feedback and PRs are welcome and appreciated.\n\n[npm-badge]: http://badge.fury.io/js/react-simplemde-editor.svg\n[npm]: http://badge.fury.io/js/react-simplemde-editor\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FRIP21%2Freact-simplemde-editor","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FRIP21%2Freact-simplemde-editor","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FRIP21%2Freact-simplemde-editor/lists"}