npm i novel-lightweight: markdown editor for your next/react-app
Let’s just agree. Finding the right markdown editor for your react app is a hit or miss. You pick tiptap/platejs, and you’d spend the rest of the sprint in configuring extensions.
Don’t get me wrong, both options are great! Most times, we wouldn’t wanna reinvent the wheel.
That’s where I found Steven Tey’s novel.sh, It is a notion-style WYSIWYG editor and comes with AI-autocompletions out of the box, cool isn’t it!
It’s one package, which you install and just works as expected! Kudos to all the contributors :)
But, when I started building an app with Novel, I experienced tiny troubles which seem tiny at the beginning but are actual troubles.
- Image uploads work great in novel. The images are handled by vercel’s blob service. All you need to do is add the env-key of BLOB_READ_WRITE TOKEN and all your images are handled. But in my app, I use uploadthing for storing all user’s assets like images. I’d not wanna have one more service handling the images just for the markdown-editor. It needs to take a prop for handling image uploads.
- The send feedback menu on the slash-commands can’t be edited. And guess what, it redirects to github-issues of the repository! That’s not what you would want your users to see.
3. The package comes bundled with analytics on usage of the editor. I guess that it was supposed to be removed after the initial-release to track the usage of auto-completions and consequent rate-limiting. Eitherways, it results in a bulky package glittered by vercel/analytics and upstash/redis which needs to be purged.
4. Novel uses tailwind underneath the hood. Surprisingly although the styles in novel are scoped to tailwind, it was overriding styles in my tailwind app! Needs a fix :)
Enter novel-lightweight
Given these issues at the tip of the iceberg, I decided to fork novel and publish a package: novel-lightweight
Targetting the image uploads, you can just pass a callback function to handleImageUpload prop.
import { Editor } from "novel-lightweight";
export default function App() {
const [data, setData] = useState("");
return (
<Editor
defaultValue={data}
disableLocalStorage={true}
onUpdate={(editor) => {
setData(editor?.storage.markdown.getMarkdown());
}}
handleImageUpload={async (file) => {
const uploads = await startUpload([file]);
if (uploads && uploads.length > 0) {
return uploads[0].url;
}
return "www.example.com/failed-upload.png";
}}
/>
);
}
Internally, handleImageUpload function would be stored in the React.context to supply when needed in the deeply-nested slash-commands component.
Ofcourse, you don’t have to deal with the send-feedback button anymore!
I love notion. So, I plan to add more features to novel-lightweight like:
- Call-outs
- Drag and drop-anything
- Embeds of youtube/spotify
That’s it for today. Go give novel-lightweight a star at: