Noted
A local-first note-taking app for developers. Everything stores as plain .md files in a folder you control. No accounts, no sync lock-in, no subscription.
The problem
Every note app I tried had the same issue — it optimises for features at the cost of clarity. By the time you've organised your notes, you've forgotten what you came to write.
The best tool is the one that gets out of your way.
Noted is deliberately minimal. One folder of markdown files. Instant search. That's the product.
What it does
Core features:
- Local markdown files as the source of truth
- Instant fuzzy full-text search via Fuse.js
- Live preview with syntax highlighting
- File watcher — changes sync instantly from any external editor
- Keyboard-first: every action has a shortcut
Non-features (intentional):
- No built-in sync (use iCloud, Dropbox, or Git)
- No formatting toolbar
- No tags, folders-in-folders, or wiki links
- No mobile app
Technical decisions
Why Electron
Electron gets criticism for bundle size, but for a local-first app that needs filesystem access, it is the right call. The alternatives (Tauri, native) add complexity without meaningful benefit for this use case.
import chokidar from 'chokidar';
import { BrowserWindow } from 'electron';
export function watchDirectory(dir: string, win: BrowserWindow) {
const watcher = chokidar.watch(dir, {
ignored: /(^|[\/\\])\../,
persistent: true,
});
watcher.on('change', (filePath) => {
win.webContents.send('file-changed', filePath);
});
return watcher;
}Search
Fuzzy search runs entirely in-process with Fuse.js. For a typical notes folder (< 5,000 files), results are instant:
import Fuse from 'fuse.js';
const fuse = new Fuse(notes, {
keys: ['title', 'content'],
threshold: 0.3,
includeMatches: true,
});
const results = fuse.search(query);Stack
| Layer | Technology | Why |
|---|---|---|
| Shell | Electron 28 | Filesystem access |
| UI | React 18 + TypeScript | Component model |
| Editor | CodeMirror 6 | Extensible, performant |
| Search | Fuse.js | Zero-dependency fuzzy search |
| Watcher | chokidar | Reliable cross-platform FS events |
| Build | Vite + electron-builder | Fast dev loop |
Performance
Some benchmarks on a MacBook Air M2:
- Cold start → 420ms to interactive
- Search → < 5ms for 1,000 notes
- File sync → < 50ms from disk write to UI update
- Memory → ~90MB idle
The installed size is 4.8MB. For comparison, Notion's desktop app is ~280MB.
What I learned
Building Noted taught me that constraints are generative. Every time I removed a feature, the app got clearer. The hardest decisions were not technical — they were about what not to build.
The chokidar watcher has a subtle bug on macOS when folders are moved via the Finder. The fix is dirty:
// Restart the watcher when the directory is moved
watcher.on('error', () => {
setTimeout(() => watchDirectory(dir, win), 500);
});Not elegant, but it works.