aith
aith2mo ago

Hey guys, I am building an app that has

Hey guys, I am building an app that has notes in it (think Obsidian). But I was curious and a bit confused about the data layer for this since I was using Convex. How would I go about saving these notes and do a fast sync like how Notion does it? Just looking for some ideas here or anything really that can point me to the right direction
19 Replies
Clever Tagline
Clever Tagline2mo ago
I added some note-taking features to a work app recently, with BlockNote as the editor. Obviously saving on every character is a bit much, so I used a debounce feature (I'm using Mantine for the UI, and it includes several debounce-related hooks) to save the changes after a 300-500 ms delay from when the user stops typing. The note state updates live so the user doesn't know anything about the save frequency.
aith
aithOP2mo ago
I was thinking on similar lines too that there is gonna be a bit of a delay. Blocknote looks really good but it's not free and since this is a personal project at the moment, I was hoping to get a free alternative to get started with it. Can I ask how you are saving the actual contents of the notes? In Convex like is that just all of that plain text dumped in a column or something else
Clever Tagline
Clever Tagline2mo ago
You can use BlockNote for free. There are just some of the more advanced features that require a subscription. In my case I'm not saving it to Convex yet. Long story short, I'm in the middle of migrating our business data and logic from Airtable, and these features I've been building needed to work with Airtable data, meaning converting to/from Markdown. Thankfully the free features in BlockNote support that. Here's the component that I made to use a BlockNote editor in multiple places:
type RichTextEditorProps {
value: string;
onChange: (value: string) => void;
readOnly: boolean;
}

export default function RichTextEditor({ value, onChange, readOnly }: RichTextEditorProps) {
const editor = useCreateBlockNote()

useEffect(() => {
async function replaceBlocks() {
if (editor) {
const currentMarkdown = editor.blocksToMarkdownLossy();
if (currentMarkdown !== value) {
const blocks = await editor.tryParseMarkdownToBlocks(value);
editor.replaceBlocks(editor.document, blocks)
}
}
}
replaceBlocks();
}, [value, editor]);

const handleChange = useDebouncedCallback(() => {
if (editor) {
onChange(editor.blocksToMarkdownLossy())
}
}, 500);

return (
<BlockNoteView
editor={editor}
onChange={handleChange}
editable={!readOnly}
/>
)
}
type RichTextEditorProps {
value: string;
onChange: (value: string) => void;
readOnly: boolean;
}

export default function RichTextEditor({ value, onChange, readOnly }: RichTextEditorProps) {
const editor = useCreateBlockNote()

useEffect(() => {
async function replaceBlocks() {
if (editor) {
const currentMarkdown = editor.blocksToMarkdownLossy();
if (currentMarkdown !== value) {
const blocks = await editor.tryParseMarkdownToBlocks(value);
editor.replaceBlocks(editor.document, blocks)
}
}
}
replaceBlocks();
}, [value, editor]);

const handleChange = useDebouncedCallback(() => {
if (editor) {
onChange(editor.blocksToMarkdownLossy())
}
}, 500);

return (
<BlockNoteView
editor={editor}
onChange={handleChange}
editable={!readOnly}
/>
)
}
jimpex
jimpex2mo ago
I’ve never seen BlockNote, that seems interesting though. I’ve tested TipTap a bit and even so much as making an ai powered text editor using it yesterday. Using a denounce with saving as mentioned by Clever would probably be the approach I’d take too
Clever Tagline
Clever Tagline2mo ago
The onChange function passed in currently calls a Convex action to save the data to Airtable. Once the migration is complete, I'll update everything to save to Convex. On that note, the raw BlockNote data can be converted to/from JSON, also using the free account. I forget where in the docs I found an example of this. I'll see if I can dig it up, but I'm late starting work, so I can't spend much more time here for now.
aith
aithOP2mo ago
That is interesting Yeah I came across TipTap too
Clever Tagline
Clever Tagline2mo ago
I looked at TipTap first because Mantine has a component that uses it, but unfortunately it doesn't support conversion to/from Markdown. I remembered a friend talking about BlockNote, and it was super easy to add.
aith
aithOP2mo ago
This is my first time using Convex and I was just curious about how I would go about saving the data of the notes. I mean do I just dump them in a column, or break it down somehow
Clever Tagline
Clever Tagline2mo ago
And it also has tie-ins to Mantine
aith
aithOP2mo ago
Wasn't sure how to make sense of that
Clever Tagline
Clever Tagline2mo ago
Yeah, just save it into a single field
aith
aithOP2mo ago
Have used mantine before and really good stuff. Have it bookmarked too lol
No description
jimpex
jimpex2mo ago
Oh it doesn’t? I thought you could, perhaps I’m remembering incorrectly
Clever Tagline
Clever Tagline2mo ago
I forget what the text limit is for fields, so depending on how detailed your notes become, you might need to split them up somehow. I think it only supports conversion one way, but not both. I forget what I found, but I tested it pretty thoroughly.
jimpex
jimpex2mo ago
It depends how large the document is. I was running into errors when storing scraped data in another project because I was surpassing the max payload. I ended up having to split into multiple documents, although that wasn’t a big deal for my use-case.
Clever Tagline
Clever Tagline2mo ago
From the docs:
Strings must be smaller than the 1MB total size limit when encoded as UTF-8.
Clever Tagline
Clever Tagline2mo ago
More about data types and limits: https://docs.convex.dev/database/types
Data Types | Convex Developer Hub
Supported data types in Convex documents
Clever Tagline
Clever Tagline2mo ago
I gotta run. Does that give you enough to start with? (I'll check back later just in case)
aith
aithOP2mo ago
Yes it does. I'll get started with this.

Did you find this page helpful?