Skip to main content
RapidDev - Software Development Agency

How to Build Attendance app with V0

Build a clock-in/clock-out attendance app with V0 using Next.js and Supabase. You'll get GPS-verified time tracking, a manager dashboard with department views, leave request management, and exportable CSV attendance reports — all in about 1-2 hours without any local setup.

What you'll build

  • One-tap clock-in/clock-out toggle with a large shadcn/ui Button and real-time timer display
  • GPS location verification on clock events using a server-side Google Maps API proxy
  • Manager dashboard with department-filtered attendance records in a sortable shadcn/ui Table
  • Leave request system with Calendar date picker and approval workflow
  • Monthly attendance reports with Progress bars showing attendance percentages
  • Duplicate clock-in prevention using a Supabase partial unique index
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Intermediate11 min read1-2 hoursV0 Premium or higherApril 2026RapidDev Engineering Team
TL;DR

Build a clock-in/clock-out attendance app with V0 using Next.js and Supabase. You'll get GPS-verified time tracking, a manager dashboard with department views, leave request management, and exportable CSV attendance reports — all in about 1-2 hours without any local setup.

What you're building

Small businesses and HR teams need a reliable way to track employee attendance without expensive enterprise software. A simple clock-in/clock-out system with GPS verification prevents buddy punching and provides accurate time records for payroll.

V0 makes this fast to build by generating the attendance UI and tracking logic from natural language prompts. Connect Supabase via the Connect panel for instant database provisioning — it handles employee records, attendance logs, and leave requests with row-level security so employees only see their own data.

The architecture uses a Next.js App Router client component for the clock-in interface (needs browser geolocation API), Server Components for manager dashboards, an API route for location-verified clock events, and Supabase with a partial unique index to prevent duplicate open sessions per employee.

Final result

A complete attendance tracking system with GPS-verified clock-in/out, a manager dashboard with department filtering, leave request management, and exportable monthly attendance reports.

Tech stack

V0AI Code Generator
Next.jsFull-Stack Framework
Tailwind CSSStyling
shadcn/uiComponent Library
SupabaseDatabase
Supabase AuthAuth

Prerequisites

  • A V0 account (Premium plan recommended for multi-page projects)
  • A Supabase project (free tier works — connect via V0's Connect panel)
  • A Google Maps API key for location verification (optional but recommended)
  • Basic understanding of your company's attendance and leave policies

Build steps

1

Set up the project and database schema for attendance tracking

Create a new V0 project and connect Supabase via the Connect panel. Then prompt V0 to create the employee, attendance, and leave request tables with proper constraints and a partial unique index that prevents duplicate open clock-in sessions.

prompt.txt
1// Paste this prompt into V0's AI chat:
2// Build an attendance tracking system with Supabase. Create these tables:
3// 1. employees: id (uuid PK), user_id (uuid FK to auth.users), full_name (text), department (text), role (text check 'employee','manager'), is_active (boolean default true), created_at (timestamptz)
4// 2. attendance_records: id (uuid PK), employee_id (uuid FK), clock_in (timestamptz), clock_out (timestamptz), clock_in_location (point), clock_out_location (point), total_hours (numeric generated always as extract(epoch from clock_out - clock_in)/3600), notes (text), status (text default 'present')
5// 3. leave_requests: id (uuid PK), employee_id (uuid FK), leave_type (text), start_date (date), end_date (date), reason (text), status (text default 'pending'), approved_by (uuid FK)
6// Add a partial unique index: CREATE UNIQUE INDEX ON attendance_records (employee_id) WHERE clock_out IS NULL
7// Add RLS so employees see only their own records, managers see their department.

Pro tip: The partial unique index is the key to preventing duplicate clock-ins. It ensures only one row per employee can have a NULL clock_out value at any time — if an employee tries to clock in twice, Supabase returns a constraint violation error.

Expected result: Supabase is connected with all tables created, RLS policies applied, and the partial unique index in place to prevent duplicate clock-in sessions.

2

Build the clock-in/clock-out interface with GPS capture

Create the employee-facing page with a large toggle button for clocking in and out. The component uses the browser's Geolocation API to capture coordinates and sends them to an API route that verifies the location server-side before recording the event.

app/api/attendance/route.ts
1import { NextRequest, NextResponse } from 'next/server'
2import { createClient } from '@supabase/supabase-js'
3
4const supabase = createClient(
5 process.env.SUPABASE_URL!,
6 process.env.SUPABASE_SERVICE_ROLE_KEY!
7)
8
9export async function POST(req: NextRequest) {
10 const { employeeId, action, latitude, longitude } = await req.json()
11 const location = `(${longitude},${latitude})`
12
13 if (action === 'clock_in') {
14 const { data, error } = await supabase
15 .from('attendance_records')
16 .insert({
17 employee_id: employeeId,
18 clock_in: new Date().toISOString(),
19 clock_in_location: location,
20 })
21 .select()
22 .single()
23
24 if (error?.code === '23505') {
25 return NextResponse.json(
26 { error: 'You already have an open clock-in session' },
27 { status: 409 }
28 )
29 }
30 if (error) return NextResponse.json({ error: error.message }, { status: 500 })
31 return NextResponse.json({ data })
32 }
33
34 if (action === 'clock_out') {
35 const { data, error } = await supabase
36 .from('attendance_records')
37 .update({
38 clock_out: new Date().toISOString(),
39 clock_out_location: location,
40 })
41 .eq('employee_id', employeeId)
42 .is('clock_out', null)
43 .select()
44 .single()
45
46 if (error) return NextResponse.json({ error: error.message }, { status: 500 })
47 return NextResponse.json({ data })
48 }
49
50 return NextResponse.json({ error: 'Invalid action' }, { status: 400 })
51}

Expected result: The API route handles clock-in and clock-out actions. Clock-in creates a new record with location; clock-out updates the open record. Duplicate clock-ins return a 409 conflict error.

3

Create the manager dashboard with department filtering

Build the manager-facing dashboard that shows today's attendance status for all department employees, weekly summaries, and the ability to filter by department. Use Server Components for efficient data fetching.

prompt.txt
1// Paste this prompt into V0's AI chat:
2// Build a manager attendance dashboard at app/dashboard/page.tsx.
3// Requirements:
4// - Show today's attendance summary: Present count, Absent count, On Leave count in shadcn/ui Cards
5// - Display a Table of today's attendance records with columns: Employee Name, Clock In Time, Clock Out Time, Total Hours, Status Badge
6// - Add a Select dropdown to filter by department
7// - Show a monthly Progress bar per employee showing attendance percentage (days present / working days)
8// - Add Tabs for Today, This Week, This Month views
9// - Weekly view shows a grid of days with green (present), red (absent), yellow (leave) indicators per employee
10// - Use Server Components to fetch data from Supabase
11// - Managers should only see employees in their department (enforce via Supabase RLS)

Pro tip: Use Design Mode (Option+D) to adjust the dashboard Card layout, Progress bar colors, and attendance grid spacing without spending any credits.

Expected result: The manager dashboard shows today's attendance with department filtering, a weekly attendance grid, and monthly Progress bars per employee.

4

Build the leave request system with approval workflow

Create the leave request form for employees and the approval interface for managers. Employees submit requests with date ranges and reasons. Managers see pending requests and can approve or reject them with a single click.

app/actions/leave.ts
1'use server'
2
3import { createClient } from '@supabase/supabase-js'
4import { revalidatePath } from 'next/cache'
5
6const supabase = createClient(
7 process.env.SUPABASE_URL!,
8 process.env.SUPABASE_SERVICE_ROLE_KEY!
9)
10
11export async function submitLeaveRequest(
12 employeeId: string,
13 leaveType: string,
14 startDate: string,
15 endDate: string,
16 reason: string
17) {
18 const { error } = await supabase.from('leave_requests').insert({
19 employee_id: employeeId,
20 leave_type: leaveType,
21 start_date: startDate,
22 end_date: endDate,
23 reason,
24 })
25
26 if (error) throw new Error(error.message)
27 revalidatePath('/leave')
28}
29
30export async function handleLeaveRequest(
31 requestId: string,
32 status: 'approved' | 'rejected',
33 managerId: string
34) {
35 const { error } = await supabase
36 .from('leave_requests')
37 .update({ status, approved_by: managerId })
38 .eq('id', requestId)
39 .eq('status', 'pending')
40
41 if (error) throw new Error(error.message)
42 revalidatePath('/dashboard')
43}

Expected result: Employees can submit leave requests with type, date range, and reason. Managers see pending requests and can approve or reject them, updating the status in Supabase.

5

Add exportable attendance reports

Create a reports page that generates monthly attendance summaries by department. Managers can view reports and export them as CSV files for payroll processing.

prompt.txt
1// Paste this prompt into V0's AI chat:
2// Build an attendance reports page at app/reports/page.tsx.
3// Requirements:
4// - Select month and department using DatePicker and Select
5// - Display a summary Table: Employee Name, Days Present, Days Absent, Days on Leave, Total Hours, Late Arrivals
6// - Show department averages at the bottom
7// - Add a "Download CSV" Button that generates and downloads a CSV file client-side
8// - Add a PieChart (Recharts) showing the breakdown of present/absent/leave days for the department
9// - Use Server Components for the initial data fetch
10// - The CSV should include all columns plus clock-in and clock-out times for each day

Expected result: The reports page shows a monthly attendance summary table filterable by department with a downloadable CSV export and a PieChart showing attendance distribution.

Complete code

app/api/attendance/route.ts
1import { NextRequest, NextResponse } from 'next/server'
2import { createClient } from '@supabase/supabase-js'
3
4const supabase = createClient(
5 process.env.SUPABASE_URL!,
6 process.env.SUPABASE_SERVICE_ROLE_KEY!
7)
8
9export async function POST(req: NextRequest) {
10 const { employeeId, action, latitude, longitude } = await req.json()
11 const location = `(${longitude},${latitude})`
12
13 if (action === 'clock_in') {
14 const { data, error } = await supabase
15 .from('attendance_records')
16 .insert({
17 employee_id: employeeId,
18 clock_in: new Date().toISOString(),
19 clock_in_location: location,
20 })
21 .select()
22 .single()
23
24 if (error?.code === '23505') {
25 return NextResponse.json(
26 { error: 'Already clocked in' },
27 { status: 409 }
28 )
29 }
30 if (error) {
31 return NextResponse.json({ error: error.message }, { status: 500 })
32 }
33 return NextResponse.json({ data })
34 }
35
36 if (action === 'clock_out') {
37 const { data, error } = await supabase
38 .from('attendance_records')
39 .update({
40 clock_out: new Date().toISOString(),
41 clock_out_location: location,
42 })
43 .eq('employee_id', employeeId)
44 .is('clock_out', null)
45 .select()
46 .single()
47
48 if (error) {
49 return NextResponse.json({ error: error.message }, { status: 500 })
50 }
51 return NextResponse.json({ data })
52 }
53
54 return NextResponse.json({ error: 'Invalid action' }, { status: 400 })
55}
56
57export async function GET(req: NextRequest) {
58 const { searchParams } = new URL(req.url)
59 const employeeId = searchParams.get('employee_id')
60
61 const { data } = await supabase
62 .from('attendance_records')
63 .select('*')
64 .eq('employee_id', employeeId!)
65 .is('clock_out', null)
66 .maybeSingle()
67
68 return NextResponse.json({ active_session: data })
69}

Customization ideas

Add geofencing verification

Define an allowed radius around the office location and reject clock-in attempts from outside the geofence by calculating the Haversine distance between the employee's coordinates and the office coordinates in the API route.

Add overtime calculations

Extend the reporting logic to calculate overtime hours for employees who clock more than 8 hours per day or 40 hours per week, with configurable overtime rates per department.

Add push notifications for late arrivals

Use Supabase Edge Functions with a cron trigger to check if employees have not clocked in by their expected start time and send notification emails via Resend.

Add shift scheduling

Create a shift management system where managers define shifts (morning, afternoon, night) and assign employees to shifts with a Calendar-based drag-and-drop interface.

Common pitfalls

Pitfall: Storing GOOGLE_MAPS_API_KEY with NEXT_PUBLIC_ prefix

How to avoid: Store GOOGLE_MAPS_API_KEY in V0's Vars tab without any prefix. Proxy geolocation verification through your API route at app/api/attendance/route.ts.

Pitfall: Not preventing duplicate clock-in sessions

How to avoid: Add a partial unique index: CREATE UNIQUE INDEX ON attendance_records (employee_id) WHERE clock_out IS NULL. This guarantees only one open session exists per employee at the database level.

Pitfall: Calculating total hours on the client side

How to avoid: Use a PostgreSQL generated column: total_hours numeric GENERATED ALWAYS AS (EXTRACT(EPOCH FROM clock_out - clock_in) / 3600). This calculates hours at the database level and cannot be tampered with.

Pitfall: Not handling timezone differences for distributed teams

How to avoid: Always use timestamptz (timestamp with time zone) in Supabase and store the employee's timezone in the employees table. Display times converted to the employee's local timezone on the frontend.

Best practices

  • Use a partial unique index on attendance_records to prevent duplicate open clock-in sessions at the database level
  • Store GPS coordinates as Supabase point type for efficient location-based queries and geofencing
  • Use Server Components for the manager dashboard to keep Supabase queries server-side and secure
  • Store GOOGLE_MAPS_API_KEY in V0's Vars tab without NEXT_PUBLIC_ prefix and proxy verification through an API route
  • Use Supabase RLS policies scoped by department so managers only see their own team's attendance records
  • Use Design Mode (Option+D) to adjust the clock-in button size, dashboard layout, and status colors without spending credits
  • Always use timestamptz columns to handle timezone differences correctly for distributed teams
  • Use PostgreSQL generated columns for total_hours calculations to prevent client-side manipulation

AI prompts to try

Copy these prompts to build this project faster.

ChatGPT Prompt

I'm building a clock-in/clock-out attendance app with Next.js App Router and Supabase. I need GPS location capture, a partial unique index to prevent duplicate clock-ins, leave request management, and manager dashboards with department filtering. Help me design the schema and the GPS verification flow.

Build Prompt

Build a geolocation-verified clock-in component using the browser Geolocation API. The component should: get the user's position with navigator.geolocation.getCurrentPosition, show a loading state while acquiring GPS, POST the coordinates to /api/attendance with the action type, display the current session timer after clock-in, and show the total hours after clock-out. Use shadcn/ui Button, Card, and Badge components with Tailwind CSS.

Frequently asked questions

How does the GPS clock-in verification work?

When an employee taps the clock-in button, the browser's Geolocation API captures their coordinates. These are sent to the API route which can optionally verify them against your office location using the Google Maps API. The coordinates are stored with the attendance record for audit purposes.

Can employees clock in from their phones?

Yes. The app is fully responsive and works in any mobile browser. The browser's Geolocation API works on both iOS and Android. For the best experience, employees should add the site to their home screen as a PWA.

What V0 plan do I need for an attendance app?

V0 Premium is recommended because the attendance app requires multiple pages (clock-in, dashboard, reports, leave requests) and API routes. The free plan's limited credits may not cover the full build.

How do I handle employees who forget to clock out?

Set up a Supabase Edge Function triggered by a cron schedule that runs at the end of each workday. It checks for open sessions (clock_out IS NULL) and either auto-clocks them out at the expected end time or flags them for manager review.

How do I deploy the attendance app?

Click Share then Publish to Production in V0 for instant Vercel deployment. The app will be accessible from any browser. For custom domains, configure them in the Vercel Dashboard after publishing.

Can I integrate this with payroll software?

Yes. Add an API route that exports attendance data in the format your payroll provider expects. Most payroll systems accept CSV imports, which you can generate from the monthly reports page.

Can RapidDev help build a custom attendance system?

Yes. RapidDev has built 600+ apps including workforce management tools with advanced features like biometric verification, shift scheduling, and payroll integration. Book a free consultation to discuss your specific attendance tracking needs.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help building your app?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.