Architecture & Stack
Plain is built exclusively as a client-side React single-page application (SPA). There is no backend server, and all data is processed in-memory or persisted locally via modern web storage APIs.
Tech Stack
The core technologies driving Plain are:
- Framework: React 18, Vite 5
- Styling: Tailwind CSS 3,
lucide-reactfor icons - State Management: Zustand 5
- Rich-Text Editor: Tiptap 3
- Progressive Web App:
vite-plugin-pwa - Testing: Playwright (
e2e/), Vitest (vitest.config.js)
Project Structure
A typical development flow in Plain revolves around the src/ directory.
src/
├── components/ # UI elements: Sidebar, NoteList, NoteEditor
├── hooks/ # Reusable React hooks (e.g., useTheme)
├── storage/ # Storage adapters (Folder, OPFS, LocalStorage)
├── store/ # Global Zustand state and actions
├── utils/ # Note sorting, date formatters, export utilities
├── extensions/ # Tiptap custom extensions (PlainImage, etc.)
├── App.jsx # The main application shell
└── main.jsx # Vite entry point and Service Worker registrationState Management
State management lives primarily in src/store/useNotesStore.js. Zustand is used to provide an efficient, unopinionated store.
- Notes are edited in-memory as HTML strings in the
contentfield. - Note behavior (creation, pinning, trashing, restoring) is managed via global store actions.
PWA and Offline Support
Plain is configured as a Progressive Web App using vite-plugin-pwa.
- Service Worker: Registration occurs in
main.jsx. - Auto-Update: The Service Worker is configured with
registerType: 'autoUpdate'. - Offline Mode: Built assets are aggressively cached by Workbox. There are no runtime API calls to fail, so the app remains fully functional without a network connection.
No Backend Interactions
Important: There are absolutely no cloud APIs, sync servers, user accounts, or backend interactions in this codebase. If you need cloud syncing, you rely on the user pointing the app at a folder synced by iCloud, Dropbox, Google Drive, or similar desktop clients.