A deep dive into the Eonebill tech stack — Next.js 15, Tailwind CSS, shadcn/ui, Prisma, and AI-powered invoice generation.
Building a SaaS product in the AI space means constantly choosing between shipping fast and building maintainable systems. When we started Eonebill — an AI-powered invoice generator for freelancers and small businesses — we had to make hard decisions about our tech stack. This is the story of what we chose and why.
Here's our core technology stack:
When we started in early 2025, Next.js 14 was dominant and Next.js 15 was in beta. We made a bet on the newer version — and it paid off.
Server Actions changed how we think about form submissions. Invoice creation, client management, payment recording — all handled by server actions with zero API boilerplate. The type safety from next-safe-action means our Zod schemas double as both runtime validators and TypeScript types.
'use server'
import { z } from 'zod'
import { safeAction } from '@/lib/safe-action'
const createInvoiceSchema = z.object({
clientId: z.string().uuid(),
items: z.array(z.object({
description: z.string().min(1),
quantity: z.number().positive(),
rate: z.number().nonnegative(),
})),
dueDate: z.string().datetime(),
notes: z.string().optional(),
})
export const createInvoice = safeAction(createInvoiceSchema, async (parsed) => {
const invoice = await db.insert(invoices).values({
...parsed.data,
userId: await getCurrentUserId(),
}).returning()
return invoice[0]
})
The App Router's nested layouts made our multi-tenant structure natural. Each workspace has its own layout, and since everything is server-rendered by default, our invoices load fast even on slower connections.
We evaluated a few UI approaches. Component libraries like MUI felt heavy and "enterprise-y." CSS-in-JS added runtime overhead we didn't want. Tailwind + shadcn/ui hit the sweet spot.
shadcn/ui isn't a component library you install — it's copy-paste code you own. When we needed a data table for our invoice list, we pulled the Radix-based table component and customized it to match our design system. Zero npm dependency lock-in.
Tailwind's @apply directives let us create semantic utility classes for our design tokens:
@layer components {
.invoice-card {
@apply bg-white border border-gray-200 rounded-xl shadow-sm
hover:shadow-md hover:border-blue-200 transition-all duration-200;
}
.invoice-status-paid {
@apply bg-green-50 text-green-700 border border-green-200;
}
.invoice-status-overdue {
@apply bg-red-50 text-red-700 border border-red-200;
}
}
The result? A design system that feels cohesive but isn't strangling us with abstraction.
We switched from Prisma to Drizzle after hitting migration speed issues in development. Drizzle's schema-as-code approach felt more like writing SQL, which our team appreciated:
// drizzle.config.ts
import { defineConfig } from 'drizzle-kit'
export default defineConfig({
schema: './src/db/schema.ts',
out: './drizzle',
dialect: 'postgresql',
dbCredentials: {
url: process.env.DATABASE_URL!,
},
})
Migrations are fast, the ORM is lean, and the generated SQL is actually readable. For a product where query performance directly affects invoice generation speed, this matters.
Here's where it gets interesting. The "AI" in AI Invoice Generator isn't a buzzword — it's integrated at multiple layers:
1. Invoice Auto-population
Users describe their project in natural language. GPT-4o parses the description and suggests line items, quantities, rates, and even payment terms based on industry norms.
2. Smart Follow-ups
When an invoice goes overdue, our AI analyzes the client's payment history and the invoice context to suggest a personalized follow-up message. Not a generic "please pay" email — something contextual.
3. Description Enhancement
Freelancers often write vague line item descriptions. Our AI can take "design work" and expand it to "UI/UX design services — 3 logo concepts with 2 revision rounds" based on the broader context of the engagement.
Supporting English and Chinese wasn't just about translation strings. Invoice standards differ between the US and China — tax IDs, currency formatting, legal disclaimers, even the concept of a "receipt" vs. an "invoice" varies. We solved this with next-intl's message files and locale-specific invoice templates:
messages/
en.json
zh.json
Each locale has its own invoice template configuration. The same underlying data renders differently based on the user's locale.
If we started today, we'd probably consider:
But overall, the stack has held up well. We shipped Eonebill to production in under 3 months, and the codebase is still something our team enjoys working in.
The AI invoice generator space is crowded, but most competitors are either template editors with a thin AI wrapper, or enterprise tools that charge $99/month minimum. We built Eonebill for the freelancer who wants AI superpowers without the enterprise complexity.
Try Eonebill free at https://www.eonebill.ai — AI-powered invoice generation for freelancers and small businesses, starting at $0.
Originally published at Eonebill — AI-powered invoice generator for freelancers and small businesses.
Ready to manage invoices, contracts & proposals in one place? Try Eonebill free — no credit card required.
Start Free →Missed the April deadline? Understand the 5 percent failure-to-file vs 0.5 percent failure-to-pay penalty difference, how Form 4868 extensions help, and the four-step protocol for catching up on years of unfiled returns.
Failing to file 1099 forms on time triggers escalating IRS penalties from 60 to 660 dollars per form. Learn the four-tier schedule, how to fix a missed filing, and when First-Time Penalty Abatement applies.
Learn how to write a professional gentle reminder email that gets results. Includes 8 ready-to-use templates for invoices, meetings, deadlines, and more.
Join the community
Subscribe to our newsletter for the latest news and updates