A modern, full-featured invoice management system for freelancers and small businesses. Built with TanStack Start, Convex, Clerk, and a polished UI powered by Tailwind CSS v4 and Shadcn/UI.
- 🔐 Secure Authentication – Seamless user management via Clerk
- 💼 Client Management – Create, edit, and track client records
- 📄 Invoice Builder – Structured invoices with line items and expenses
- 📊 Dashboard – At-a-glance KPIs for revenue and status tracking
- 📥 PDF Export – Generate professional PDFs (with embedded receipts)
- ⚙️ Flexible Settings – Configure tax, numbering, and due dates
- 🌓 Dark Mode – System-aware theming with persistent preference
- ⚡ Real-time Sync – Convex keeps data updated instantly across sessions
- 🤖 Smart Client Creation – Add clients inline during invoice creation
- 🔎 Searchable Selector – Quick combobox search for any client
- 🧾 Receipt Images – Attach and preview expense receipts
- 🧮 Configurable Tax – Support for 0–100% tax, per user
- 📬 Email Invoices – Optional email delivery to clients
- ✅ Bulk Invoice Actions – Multi-select invoices to update status, delete, or download PDFs in one go
- Frontend: TanStack Start + TanStack Router + React 19 + TypeScript
- Bundler: Vite 7
- Styling: Tailwind CSS v4, Shadcn/UI, clsx, class-variance-authority
- State & Data: Convex mutations/queries with end-to-end type safety
- Auth: Clerk (JWT template integration for Convex)
- Forms & Validation: React Hook Form + Zod
- PDF Generation: @react-pdf/renderer
- Routing: TanStack Router (file-based routing)
- Tooling: pnpm, ESLint, TypeScript strict mode
- Node.js 20+
- pnpm (
npm install -g pnpm) - Clerk account for authentication
- Convex account for database + functions
git clone <your-repo-url>
cd invoicething
pnpm install- Open the Clerk Dashboard
- Create a new application (or reuse an existing one)
- Enable Email + Password auth
- Copy the publishable and secret keys
pnpm convex devFollow the prompt to create a new Convex project. When complete, copy the deployment URL.
Create .env in the project root:
# Clerk Authentication
VITE_CLERK_PUBLISHABLE_KEY=pk_test_...
# Note: Clerk secret key is not needed for client-side usage
# Convex Backend
VITE_CONVEX_URL=https://<your-deployment>.convex.cloudNote: TanStack Start uses Vite, which requires the VITE_ prefix for environment variables. The app also supports NEXT_PUBLIC_ prefix for backwards compatibility.
-
In Clerk, go to JWT Templates
-
Create a template named
convex -
Add the claim:
{ "userId": "{{user.id}}" } -
Save the template and ensure it is active
pnpm devpnpm convex devVisit http://localhost:3000 and sign in with your Clerk user.
invoicething/
├── src/
│ ├── app/ # TanStack Router file-based routes
│ │ ├── __root.tsx # Root layout route
│ │ ├── index.tsx # Home page (/)
│ │ ├── dashboard/ # Dashboard routes
│ │ │ └── index.tsx # /dashboard
│ │ ├── clients/ # Client routes
│ │ │ ├── -columns.tsx # Helper file (excluded from routes)
│ │ │ ├── index.tsx # /clients
│ │ │ └── $id.tsx # /clients/$id
│ │ ├── invoices/ # Invoice routes
│ │ │ ├── -columns.tsx # Helper file (excluded from routes)
│ │ │ ├── index.tsx # /invoices
│ │ │ ├── $id.tsx # /invoices/$id
│ │ │ └── new.tsx # /invoices/new
│ │ ├── invoices_/ # Pathless route group for nested invoice routes
│ │ │ └── $id/
│ │ │ └── edit.tsx # /invoices/$id/edit
│ │ ├── settings/ # Settings routes
│ │ │ └── index.tsx # /settings
│ │ ├── sign-in/ # Sign-in routes
│ │ │ └── $.tsx # /sign-in/$ (splat route)
│ │ └── sign-up/ # Sign-up routes
│ │ └── $.tsx # /sign-up/$ (splat route)
│ ├── components/
│ │ ├── ui/ # Shadcn/UI primitives
│ │ ├── invoice-pdf.tsx # PDF renderer template
│ │ ├── invoice-table.tsx # Invoice list table + bulk actions
│ │ └── providers.tsx # Top-level providers (Clerk, Convex, Theme)
│ ├── hooks/ # Custom React hooks
│ ├── lib/ # Utilities, helpers, constants
│ ├── router.tsx # TanStack Router configuration
│ └── routeTree.gen.ts # Auto-generated route tree (do not edit)
├── convex/ # Convex backend functions + schema
│ ├── schema.ts # Convex data model
│ ├── invoices.ts # Invoice queries + mutations
│ ├── clients.ts # Client endpoints
│ ├── files.ts # File storage helpers
│ └── users.ts # User bootstrap + metadata
├── public/ # Static assets
├── vite.config.ts # Vite configuration
├── tailwind.config.ts # Tailwind CSS v4 configuration
└── README.md # Project documentation (this file)
- Mirrors Clerk users
- Tracks user-level settings and metadata
- Name, email, billing address, contact person
- Linked one-to-many with invoices
- Invoice number, issue/due dates, status, totals
- Tied to clients, line items, and optional claims
- Supports bulk operations and PDF downloads
- Description, quantity, unit price, total
- Multiple entries per invoice
- Description, amount, date, optional receipt image
- Calculated totals roll into invoice summary
- Invoice prefix, starting number
- Default due date offset and tax rate (0–100%)
- Toggle via navigation switch (light, dark, system)
- Persists using
next-themes - Smooth transitions via Tailwind CSS v4 animations
- Configurable defaults on the Settings page
- Applied to new invoices while preserving existing ones
- Multi-select rows in the invoice table
- Update status, delete invoices, or download combined PDFs
- Optimistic UI updates with Convex mutations
- Upload images up to 5 MB (JPG, PNG, GIF, WebP)
- Inline previews with modal expansion
- Included automatically in generated PDFs
pnpm dev # Run TanStack Start (Vite) in development mode
pnpm build # Create a production build
pnpm start # Serve the production build
pnpm lint # Run ESLint + TypeScript checks
pnpm convex dev # Start Convex in dev mode
pnpm convex deploy # Deploy Convex backend- Invoice Prefix – Custom text before invoice numbers (e.g.
INV) - Starting Number – Initial invoice counter value
- Due Date Offset – Default days until payment is due
- Tax Rate – Default percentage added to invoices (0 disables tax)
TanStack Start uses Vite, which requires the VITE_ prefix for environment variables to be exposed to the client. The app supports both VITE_ and NEXT_PUBLIC_ prefixes for compatibility.
Routes are organized using TanStack Router's file-based routing system:
- Files in
src/app/become routes automatically - Use
-prefix to exclude files from route generation (e.g.,-columns.tsx) - Dynamic routes use
$prefix (e.g.,$id.tsxfor/invoices/$id) - Splat routes use
$.tsx(e.g.,$.tsxfor/sign-in/$)
- Double-check Clerk keys in
.env(useVITE_CLERK_PUBLISHABLE_KEY) - Ensure the JWT template is named
convex - Confirm the
userIdclaim is present
- Run
pnpm convex devin its own terminal - Verify
VITE_CONVEX_URLmatches your deployment - Make sure your Convex project is deployed if testing production builds
- File size must be ≤ 5 MB
- Supported MIME types:
image/jpeg,image/png,image/gif,image/webp - Check Convex storage quota usage
- Ensure all required invoice fields are filled
- Large embedded images can increase render time—consider compressing receipts
- TanStack Start Documentation
- TanStack Router Documentation
- Convex Documentation
- Clerk Documentation
- Shadcn/UI Docs
- Tailwind CSS v4 Docs
- Vite Documentation
Crafted with ❤️ using TanStack Start, Convex, and Clerk.