🚧 VeloxKit is pre-release software. APIs may change before v1.0. Get started β†’
Examples
Notes App

Notes App

The reference VeloxKit application. A full-featured notes app with SQLite storage, full-text search, tagging, and a custom title bar.

Get the code

npx veloxkit-cli create my-notes --template notes
cd my-notes
npm run dev

Or clone directly:

git clone https://github.com/yourname/veloxkit-examples
cd veloxkit-examples/notes-app
npm install
npm run dev

Features

  • Create, edit, delete notes with live auto-save
  • Full-text search with result highlighting
  • Pin notes to the top
  • Custom transparent title bar (macOS & Windows)
  • Keyboard shortcuts: ⌘N new note, ⌘F search, ⌘⌫ delete
  • Dark mode by default, follows system preference

Architecture

screens/
β”œβ”€β”€ _layout.tsx       ← sidebar + content layout
β”œβ”€β”€ index.tsx         ← redirects to /notes
└── notes/
    β”œβ”€β”€ index.tsx     ← notes list + search
    └── [id].tsx      ← note editor

src/
β”œβ”€β”€ main.ts
β”œβ”€β”€ db.ts             ← schema, migrations, query helpers
└── hooks/
    └── useNotes.ts   ← useLiveQuery wrapper

Key code

Database setup

// src/db.ts
import { db } from 'veloxkit'
 
export function initDatabase() {
  db.migrate([
    `CREATE TABLE notes (
      id         INTEGER PRIMARY KEY AUTOINCREMENT,
      title      TEXT    NOT NULL DEFAULT '',
      body       TEXT    NOT NULL DEFAULT '',
      pinned     INTEGER NOT NULL DEFAULT 0,
      created_at INTEGER NOT NULL DEFAULT (unixepoch()),
      updated_at INTEGER NOT NULL DEFAULT (unixepoch())
    )`,
    `CREATE VIRTUAL TABLE notes_fts USING fts5(
      title, body, content=notes, content_rowid=id
    )`,
    // triggers for fts sync (see full source)
  ])
}
 
export function searchNotes(query: string) {
  if (!query.trim()) {
    return db.query<Note>(
      'SELECT * FROM notes ORDER BY pinned DESC, updated_at DESC'
    )
  }
  return db.query<Note>(
    `SELECT notes.*
     FROM notes_fts
     JOIN notes ON notes.id = notes_fts.rowid
     WHERE notes_fts MATCH ?
     ORDER BY rank`,
    [query]
  )
}

Auto-saving editor

// screens/notes/[id].tsx
import { db } from 'veloxkit'
import { useDebouncedCallback } from 'use-debounce'
 
export default function NoteDetail() {
  const { id } = useParams()
  const [note, setNote] = useState<Note | null>(null)
 
  useEffect(() => {
    setNote(db.query<Note>('SELECT * FROM notes WHERE id = ?', [id])[0])
  }, [id])
 
  const save = useDebouncedCallback((patch: Partial<Note>) => {
    db.execute(
      'UPDATE notes SET title=COALESCE(?,title), body=COALESCE(?,body), updated_at=unixepoch() WHERE id=?',
      [patch.title ?? null, patch.body ?? null, id]
    )
  }, 300)
 
  if (!note) return null
 
  return (
    <View style={{ flex: 1, padding: 24, gap: 12 }}>
      <TextInput
        value={note.title}
        onChangeText={(title) => { setNote(n => n && { ...n, title }); save({ title }) }}
        style={{ fontSize: 24, fontWeight: 700, color: '#F0F0F2' }}
        placeholder="Title"
      />
      <TextInput
        value={note.body}
        onChangeText={(body) => { setNote(n => n && { ...n, body }); save({ body }) }}
        multiline
        style={{ flex: 1, fontSize: 15, color: '#D0D0E0', lineHeight: 24 }}
        placeholder="Start writing..."
      />
    </View>
  )
}

veloxkit.config.ts

import { defineConfig } from 'veloxkit'
 
export default defineConfig({
  name: 'velox-notes',
  productName: 'Notes',
  version: '0.1.0',
  identifier: 'dev.veloxkit.examples.notes',
  capabilities: ['db', 'fs', 'dialog', 'clipboard'],
  window: {
    titleBarStyle: 'transparent',
    trafficLightsPosition: { x: 14, y: 14 },
    width: 1100,
    height: 720,
    minWidth: 600,
    minHeight: 400,
  },
})