Project Name | Stars | Downloads | Repos Using This | Packages Using This | Most Recent Commit | Total Releases | Latest Release | Open Issues | License | Language |
---|---|---|---|---|---|---|---|---|---|---|
Mermaid | 58,002 | 505 | 317 | 18 hours ago | 140 | September 13, 2022 | 867 | mit | JavaScript | |
Generation of diagrams like flowcharts or sequence diagrams from text in a similar manner as markdown | ||||||||||
Marktext | 39,687 | 25 days ago | 1 | January 17, 2022 | 1,033 | mit | JavaScript | |||
📝A simple and elegant markdown editor, available for Linux, macOS and Windows. | ||||||||||
Stackedit | 20,362 | 2 | 1 | 5 days ago | 67 | March 29, 2021 | 667 | apache-2.0 | JavaScript | |
In-browser Markdown editor | ||||||||||
Affine | 16,541 | 16 hours ago | 209 | mpl-2.0 | TypeScript | |||||
There can be more than Notion and Miro. AFFiNE is a next-gen knowledge base that brings planning, sorting and creating all together. Privacy first, open-source, customizable and ready to use. | ||||||||||
Tui.editor | 15,984 | 266 | 147 | 3 days ago | 38 | December 12, 2019 | 487 | mit | TypeScript | |
🍞📝 Markdown WYSIWYG Editor. GFM Standard + Chart & UML Extensible. | ||||||||||
Editor.md | 12,952 | 35 | 8 | 16 days ago | 1 | June 27, 2015 | 555 | mit | JavaScript | |
The open source embeddable online markdown editor (component). | ||||||||||
Leanote | 11,169 | 3 months ago | 516 | other | JavaScript | |||||
Not Just A Notepad! (golang + mongodb) http://leanote.org | ||||||||||
Vnote | 10,226 | 24 days ago | 568 | lgpl-3.0 | C++ | |||||
A pleasant note-taking platform. | ||||||||||
Tinacms | 8,831 | 5 | 61 | a day ago | 105 | September 21, 2022 | 85 | other | TypeScript | |
The Markdown CMS | ||||||||||
Simplemde Markdown Editor | 8,728 | 1,516 | 201 | 2 years ago | 13 | June 14, 2016 | 278 | mit | JavaScript | |
A simple, beautiful, and embeddable JavaScript Markdown editor. Delightful editing for beginners and experts alike. Features built-in autosaving and spell checking. |
React component wrapper for EasyMDE (the most fresh SimpleMDE fork).
Only two dependencies, React (peer) and EasyMDE (peer).
Built by @RIP21 👨💻
Table of contents generated with markdown-toc
>=16.8.2
easymde
now a peer dependency, please install it manuallylabel
prop has been removedoptions
shall be memoized to prevent new instances from being created on each render and other related to that bugs (more on that below)ref
, so you can easily get access to div
wrapper by using ref
prop.@babel/runtime
helpers are no longer inlined but imported.npm install --save react-simplemde-editor easymde
Note: Possibly you may need to install @babel/runtime
, try without it, but if you don't have any issues, then you shouldn't.
or to see it locally:
git clone https://github.com/RIP21/react-simplemde-editor.git
cd react-simplemde-editor
yarn install
yarn demo
open browser at localhost:3000
View the demo code for more examples.
All examples below are in TypeScript
Uncontrolled usage
import React from "react";
import SimpleMDE from "react-simplemde-editor";
import "easymde/dist/easymde.min.css";
<SimpleMDE />;
export const ControlledUsage = () => {
const [value, setValue] = useState("Initial value");
const onChange = useCallback((value: string) => {
setValue(value);
}, []);
return <SimpleMdeReact value={value} onChange={onChange} />;
};
You can set API of SimpleMDE options which you pass down as a options
prop.
If you're using TypeScript it will be inferred by compiler.
Note: if you don't specify a custom id it will automatically generate an id for you.
Note that you need to useMemo
to memoize options
so they do not change on each rerender! It will affect behavior and performance
because 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!
Also, if you change options
on each value
change you will lose focus.
So, 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
case of functional/hooks components, or class field for class
based components.
Slightly more on that here: #164
export const UsingOptions = () => {
const [value, setValue] = useState("Initial");
const onChange = useCallback((value: string) => {
setValue(value);
}, []);
const autofocusNoSpellcheckerOptions = useMemo(() => {
return {
autofocus: true,
spellChecker: false,
} as SimpleMDE.Options;
}, []);
return (
<SimpleMdeReact
options={autofocusNoSpellcheckerOptions}
value={value}
onChange={onChange}
/>
);
};
You can include key maps using the extraKeys
prop.
Read more at CodeMirror extra keys
export const UpdateableByHotKeys = () => {
const extraKeys = useMemo<KeyMap>(() => {
return {
Up: function (cm) {
cm.replaceSelection(" surprise. ");
},
Down: function (cm) {
cm.replaceSelection(" surprise again! ");
},
};
}, []);
const [value, setValue] = useState("initial");
const onChange = (value: string) => setValue(value);
return (
<SimpleMdeReact value={value} onChange={onChange} extraKeys={extraKeys} />
);
};
import ReactDOMServer from "react-dom/server";
export const CustomPreview = () => {
const customRendererOptions = useMemo(() => {
return {
previewRender() {
return ReactDOMServer.renderToString(
<ReactMarkdown
source={text}
renderers={{
CodeBlock: CodeRenderer,
Code: CodeRenderer,
}}
/>
);
},
} as SimpleMDE.Options;
}, []);
return (
<div>
<h4>Custom preview</h4>
<SimpleMdeReact options={customRendererOptions} />
</div>
);
};
See full list of events here
import { SimpleMdeReact } from "react-simplemde-editor";
import type { SimpleMdeToCodemirrorEvents } from "react-simplemde-editor";
export const CustomEventListeners = () => {
const [value, setValue] = useState("Initial value");
const onChange = useCallback((value: string) => {
setValue(value);
}, []);
// Make sure to always `useMemo` all the `options` and `events` props to ensure best performance!
const events = useMemo(() => {
return {
focus: () => console.log(value),
} as SimpleMdeToCodemirrorEvents;
}, []);
return <SimpleMdeReact events={events} value={value} onChange={onChange} />;
};
export const Autosaving = () => {
const delay = 1000;
const autosavedValue = localStorage.getItem(`smde_demo`) || "Initial value";
const anOptions = useMemo(() => {
return {
autosave: {
enabled: true,
uniqueId: "demo",
delay,
},
};
}, [delay]);
return (
<SimpleMdeReact id="demo" value={autosavedValue} options={anOptions} />
);
};
easymde
, codemirror
or cursor
info to be able to manipulate it.export const GetDifferentInstances = () => {
// simple mde
const [simpleMdeInstance, setMdeInstance] = useState<SimpleMDE | null>(null);
const getMdeInstanceCallback = useCallback((simpleMde: SimpleMDE) => {
setMdeInstance(simpleMde);
}, []);
useEffect(() => {
simpleMdeInstance &&
console.info("Hey I'm editor instance!", simpleMdeInstance);
}, [simpleMdeInstance]);
// codemirror
const [codemirrorInstance, setCodemirrorInstance] = useState<Editor | null>(
null
);
const getCmInstanceCallback = useCallback((editor: Editor) => {
setCodemirrorInstance(editor);
}, []);
useEffect(() => {
codemirrorInstance &&
console.info("Hey I'm codemirror instance!", codemirrorInstance);
}, [codemirrorInstance]);
// line and cursor
const [lineAndCursor, setLineAndCursor] = useState<Position | null>(null);
const getLineAndCursorCallback = useCallback((position: Position) => {
setLineAndCursor(position);
}, []);
useEffect(() => {
lineAndCursor &&
console.info("Hey I'm line and cursor info!", lineAndCursor);
}, [lineAndCursor]);
return (
<div>
<h4>Getting instance of Mde and codemirror and line and cursor info</h4>
<SimpleMdeReact
value="Go to console to see stuff logged"
getMdeInstance={getMdeInstanceCallback}
getCodemirrorInstance={getCmInstanceCallback}
getLineAndCursor={getLineAndCursorCallback}
/>
</div>
);
};
Here is how you do it. It requires mock of certain browser pieces to work, but this is whole example.
import { act, render, screen } from "@testing-library/react";
import { useState } from "react";
import { SimpleMdeReact } from "react-simplemde-editor";
import userEvent from "@testing-library/user-event";
// @ts-ignore
Document.prototype.createRange = function () {
return {
setEnd: function () {},
setStart: function () {},
getBoundingClientRect: function () {
return { right: 0 };
},
getClientRects: function () {
return {
length: 0,
left: 0,
right: 0,
};
},
};
};
const Editor = () => {
const [value, setValue] = useState("");
return <SimpleMdeReact value={value} onChange={setValue} />;
};
describe("Renders", () => {
it("succesfully", async () => {
act(() => {
render(<Editor />);
});
const editor = await screen.findByRole("textbox");
userEvent.type(editor, "hello");
expect(screen.getByText("hello")).toBeDefined();
});
});
export interface SimpleMDEReactProps
extends Omit<React.HTMLAttributes<HTMLDivElement>, "onChange"> {
id?: string;
onChange?: (value: string, changeObject?: EditorChange) => void;
value?: string;
extraKeys?: KeyMap;
options?: SimpleMDE.Options;
events?: SimpleMdeToCodemirrorEvents;
getMdeInstance?: GetMdeInstance;
getCodemirrorInstance?: GetCodemirrorInstance;
getLineAndCursor?: GetLineAndCursor;
placeholder?: string;
textareaProps?: Omit<
React.HTMLAttributes<HTMLTextAreaElement>,
"id" | "style" | "placeholder"
>;
}
default
- SimpleMdeReact
SimpleMdeReact
- same as default
but named
Types:
SimpleMdeReactProps
- props of the component
DOMEvent
- certain events that are used to get events exported below
CopyEvents
- only copy codemirror events
GlobalEvents
- some other global codemirror events
DefaultEvent
- default codemirror event handler function
IndexEventsSignature
- index signature that expects string as key and returns DefaultEvent
SimpleMdeToCodemirrorEvents
- manually crafted events (based off @types/[email protected]
that easymde
uses internally) +
all the above merged together into whole mapping between Codemirror event names and actual handlers for
events
prop
GetMdeInstance
- signature of the callback function that retrieves mde instance
GetCodemirrorInstance
- signature of the callback function that retrieves codemirror instance
GetLineAndCursor
- signature of the callback function that retrieves line and cursor info
simplemde
itself. Possible breaking changes, so I bumped version to v4.simplemde/dist/simplemde.min.css
now it will be easymde/dist/easymde.min.css
initialValue
prop has been removed and replaced with a value
prop, allowing direct changes to the value to be made after the component mounts.Version 1.0 did not have SimpleMDE options configured well, this readme reflects the changes made to better include options. This is still a very new project. Testing, feedback and PRs are welcome and appreciated.