Twodo is a household coordination app for couples who want to manage tasks, expenses, shopping, and shared balance with less friction and more clarity. The product is designed for quick daily check-ins, recurring chores, and calm accountability.
Twodo helps couples:
- Define recurring and one-off tasks
- Complete tasks quickly from a mobile-first interface
- Track household expenses and settlements
- Manage shopping lists together
- Review balance and progress through metrics
- Invite and onboard a partner into the same household
- React 19
- TypeScript
- Vite
- TanStack Router
- TanStack Query with IndexedDB persistence
- Supabase for auth, data, and backend functions
- i18next for localization
- Tailwind CSS v4
- Motion and React Error Boundary for interaction and resilience
- Node.js
- Docker Desktop or another running Docker engine
- A Supabase project
pnpm installCreate a local environment file from the example file and fill in the local Supabase values:
VITE_SUPABASE_URL=
VITE_SUPABASE_ANON_KEY=The repository already includes .env.example with the local defaults:
VITE_SUPABASE_URL=http://127.0.0.1:54321VITE_SUPABASE_ANON_KEY=sb_publishable_XXXRESEND_API_KEY=re_XXXAPP_URL=http://localhost:3000
Copy that file to .env.local and replace VITE_SUPABASE_ANON_KEY with the local anonymous key printed by Supabase after the stack starts.
- Start Docker if it is not already running.
- Start the local Supabase stack:
pnpm startThis runs the Supabase CLI and brings up the local containers for Postgres, Auth, Storage, and the other local services used by the app.
- Copy the local anonymous key from the Supabase output into .env.local.
- Start the frontend in a second terminal:
pnpm devThe app runs on port 3000 and listens on 0.0.0.0.
If you need to stop the local backend later, use:
pnpm exec supabase stop| Script | Description |
|---|---|
pnpm dev |
Start the Vite development server |
pnpm build |
Build the production bundle |
pnpm preview |
Preview the production build locally |
pnpm lint |
Run the TypeScript type check |
pnpm clean |
Remove the dist folder |
pnpm start |
Start Supabase locally |
pnpm update-types |
Regenerate src/lib/database.types.ts from the local Supabase schema |
pnpm db:migration:new |
Create a new Supabase migration |
pnpm db:migration:push |
Push local migrations to Supabase |
pnpm db:reset |
Reset the local Supabase database |
pnpm db:sync |
Dump public data into supabase/seed.sql and reset the local database |
src/apiis the public data-access surface for each domain. It groups React Query hooks, query keys, mutations, and prefetch helpers so components consume a stable API without importing Supabase or low-level persistence details directly.src/supabasecontains the Supabase implementation layer. It groups the typed client, typed errors, and the per-domain query and mutation functions that actually talk to the backend.src/componentscontains the main app screens and feature UI.src/contextholds global providers such as auth state.src/domaindefines shared domain models and validation.src/hookscontains reusable cross-cutting hooks, such as language, online status, and telemetry helpers.src/libholds shared client infrastructure such as Supabase, query caching, and telemetry.src/localesstores translations.supabasecontains local config, migrations, seed data, and functions.
src/apiis the stable import boundary for domain data hooks, keys, and prefetch helpers.src/supabaseis the implementation boundary where the Supabase client and domain-specific persistence logic live.- Routes are lazy loaded and protected by auth gates.
- Query data is cached with TanStack Query and persisted in IndexedDB for offline-friendly behavior.
- The app exposes lightweight client telemetry for route timing, request counts, and mutation errors.
- Section-level error boundaries keep failures isolated to the affected part of the UI.
If you update the Supabase schema, regenerate the typed client before committing changes:
pnpm update-types