import { useCallback, useState } from 'react' import { createFileRoute } from '@tanstack/react-router' import { useQuery, useMutation } from 'convex/react' import { Trash2, Plus, Check, Circle } from 'lucide-react' import { api } from '../../../convex/_generated/api' import type { Id } from '../../../convex/_generated/dataModel' export const Route = createFileRoute('/demo/convex')({ ssr: false, component: ConvexTodos, }) function ConvexTodos() { const todos = useQuery(api.todos.list) const addTodo = useMutation(api.todos.add) const toggleTodo = useMutation(api.todos.toggle) const removeTodo = useMutation(api.todos.remove) const [newTodo, setNewTodo] = useState('') const handleAddTodo = useCallback(async () => { if (newTodo.trim()) { await addTodo({ text: newTodo.trim() }) setNewTodo('') } }, [addTodo, newTodo]) const handleToggleTodo = useCallback( async (id: Id<'todos'>) => { await toggleTodo({ id }) }, [toggleTodo], ) const handleRemoveTodo = useCallback( async (id: Id<'todos'>) => { await removeTodo({ id }) }, [removeTodo], ) const completedCount = todos?.filter((todo) => todo.completed).length || 0 const totalCount = todos?.length || 0 return (

Convex

Todos

Powered by real-time sync

{totalCount > 0 && (
{completedCount} completed {totalCount - completedCount} remaining
)}
setNewTodo(e.target.value)} onKeyDown={(e) => { if (e.key === 'Enter') { handleAddTodo() } }} placeholder="What needs to be done?" className="demo-input min-w-0 flex-1" />
{!todos ? (

Loading todos...

) : todos.length === 0 ? (

No todos yet

Add your first todo above to get started!

) : (
{todos.map((todo, index) => (
{todo.text}
))}
)}

Built with Convex, real-time updates, and synced state.

) }