Skip to content

Repository files navigation

Davince Band Logo

Next.js Testimonial Form

Production-ready testimonial collection with Google Sheets integration

CI Next.js TypeScript Biome License: MIT

FeaturesQuick StartCI/CDDeployment


About

Conversational testimonial form built with Next.js 16 App Router, featuring chat-style UI with typing animations. Data syncs automatically to Google Sheets.

Originally created for Davince Band, fully customizable for any testimonial collection use case.

Live Demo: [Coming soon]

Key Highlights

  • 🚀 Modern Stack - Next.js 16, React 19, TypeScript, Tailwind CSS 4, Biome
  • 💬 Chat Interface - Conversational flow with realistic typing animations
  • Performance - Memoized components, debounced scrolling, optimized re-renders
  • 📊 Google Sheets - Automatic data sync, no database required
  • 🔒 Secure - Environment validation, CSP headers, server-side credentials
  • 🎨 Themeable - Dark mode, customizable design tokens
  • 📱 Responsive - Mobile-first design, optimized for all screen sizes
  • Accessible - WCAG compliant, keyboard navigation

Features

User Experience

  • Multi-step chat flow with typing animations
  • Character count (250 limit) with live feedback
  • Optional internal feedback field (not published)
  • Review screen with inline editing
  • Responsive layout optimized for mobile and desktop
  • Comprehensive loading & error states

Technical

  • Server-side Google Sheets API integration
  • Build-time environment validation
  • Type-safe error handling with custom error classes
  • Optimized image formats (AVIF/WebP)
  • Security headers (CSP, X-Frame-Options, HSTS)
  • Pre-commit hooks with Husky + lint-staged

Quick Start

Prerequisites: Bun ≥1.0 or Node.js ≥24

# Clone & install
git clone https://github.com/rafactx/nextjs-testimonial-form.git
cd nextjs-testimonial-form
bun install

# Configure environment
cp .env.local.example .env.local
# Edit .env.local with your Google Sheets credentials

# Start dev server
bun dev

Open http://localhost:3000

Google Sheets Setup

Complete configuration guide

1. Create Service Account

  1. Go to Google Cloud Console
  2. Create project → APIs & ServicesCredentials
  3. Create CredentialsService Account
  4. Complete form → Create → Skip permissions → Done

2. Generate JSON Key

  1. Select service account → Keys tab
  2. Add KeyCreate new keyJSON
  3. Download and store securely (never commit to git)

3. Enable API

  1. APIs & ServicesLibrary
  2. Search "Google Sheets API" → Enable

4. Configure Spreadsheet

  1. Create new Google Sheet
  2. Share with service account email (from JSON) as Editor
  3. Copy spreadsheet ID from URL: docs.google.com/spreadsheets/d/{ID}/edit

5. Environment Variables

Map JSON values to .env.local:

GOOGLE_SHEETS_CLIENT_EMAIL=your-account@project.iam.gserviceaccount.com
GOOGLE_SHEETS_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\nYour_Key\n-----END PRIVATE KEY-----\n"
GOOGLE_SHEETS_SPREADSHEET_ID=your-spreadsheet-id
GOOGLE_SHEETS_SHEET_NAME=Sheet1

⚠️ Important: Keep \n in PRIVATE_KEY (they're line breaks, not literal text)

CI/CD Setup

GitHub Actions

The repository includes a CI workflow (.github/workflows/ci.yml) that runs on push/PR:

  1. Lint & Check - Biome linting, formatting, and type checking
  2. Build - Next.js production build with mock env vars

No secrets needed for CI - build uses mock values to validate env var existence.

Configuring Secrets (for deployment)

For production deployments on Vercel/other platforms:

  1. GitHub Environments (recommended for security):

    • Settings → Environments → New environment (e.g., "Production")
    • Add secrets:
      • GOOGLE_SHEETS_CLIENT_EMAIL
      • GOOGLE_SHEETS_PRIVATE_KEY
      • GOOGLE_SHEETS_SPREADSHEET_ID
      • GOOGLE_SHEETS_SHEET_NAME
    • Configure Required reviewers for approval gate
    • Enable Branch protection on main
  2. Vercel Integration:

    • Link GitHub repo to Vercel project
    • Add environment variables in Vercel dashboard
    • Auto-deploy on push to main

Scripts

# Development
bun dev              # Start dev server with hot reload
bun build            # Production build
bun start            # Production server

# Code Quality
bun lint             # Run Biome linter
bun lint:fix         # Auto-fix Biome issues
bun format           # Format code with Biome
bun format:check     # Verify formatting
bun check            # Lint + format + fix (pre-commit)
bun ci               # CI mode (strict, no fixes)

Deployment

Vercel (Recommended)

Deploy with Vercel

Steps:

  1. Import repository on vercel.com
  2. Add environment variables (see Google Sheets Setup)
  3. Deploy

CLI:

bun add -g vercel
vercel login
vercel --prod

Other Platforms

Works with any Next.js-supporting platform:

  • Netlify - Use Next.js Runtime plugin
  • Railway - Auto-deploy from Git
  • Render - Connect repository
  • AWS Amplify - Git-based deployment

Architecture

testimonial-form/
├── app/                    # Next.js App Router
│   ├── api/testimonials/  # Submission endpoint (POST)
│   ├── layout.tsx         # Root layout + ThemeProvider
│   └── page.tsx           # Main page
├── components/
│   ├── ui/               # Base components (shadcn/ui)
│   └── testimonial-form.tsx # Main form (585 lines)
├── lib/
│   ├── env.ts            # Build-time validation
│   ├── errors.ts         # Type-safe error classes
│   ├── google-sheets.ts  # Sheets API client
│   └── utils.ts          # Utilities (cn helper)
└── .github/workflows/    # CI pipeline

Troubleshooting

Build Errors

Variáveis de ambiente faltando

  • Cause: Missing required env vars
  • Fix: Verify .env.local has all 4 variables from Google Sheets Setup
  • CI: Use mock values (already configured in ci.yml)

Error: Could not load the default credentials

  • Cause: Invalid service account credentials
  • Fix: Re-download JSON key, verify PRIVATE_KEY includes \n characters

Runtime Errors

API Request failed with status 403

  • Cause: Service account lacks spreadsheet access
  • Fix: Share spreadsheet with service account email as Editor

Error: Unable to parse range

  • Cause: SHEET_NAME doesn't match actual sheet name
  • Fix: Check sheet tab name in Google Sheets (default: "Sheet1")

Development Issues

Pre-commit hook not running

  • Fix: chmod +x .husky/pre-commit && git add .husky/pre-commit

Biome errors on CSS files

  • Expected: Biome skips CSS (Tailwind not supported), only lints TS/JS/JSON

Port 3000 already in use

  • Fix: lsof -ti:3000 | xargs kill -9 or change port: bun dev -- -p 3001

Customization

Theme

Modify design tokens in app/globals.css:

@theme inline {
  --color-primary: #3b82f6;
  --color-secondary: #8b5cf6;
  /* Add custom colors */
}

Messages

Edit conversation flow in components/testimonial-form.tsx:

const botMessages: Record<Step, string[]> = useMemo(() => ({
  name: ["Custom greeting!", "What's your name?"],
  testimonial: ["Tell us about your experience..."],
  // ...
}), [])

Data Schema

Testimonials saved to Google Sheets:

Timestamp Name Testimonial Internal Feedback
2025-12-27 10:30 John Doe Amazing! Feature request X

Performance

Optimizations:

  • Component memoization (React.memo)
  • Callback stabilization (useCallback)
  • Derived state memoization (useMemo)
  • Debounced scroll (100ms timeout)
  • Next.js Image (AVIF/WebP with blur placeholder)
  • Tree-shaking (optimized imports)
  • Gzip/Brotli compression

Metrics (estimated):

  • First Contentful Paint: < 1.5s
  • Time to Interactive: < 3.5s
  • Cumulative Layout Shift: < 0.1

Contributing

See CONTRIBUTING.md for guidelines.

Quick start:

  1. Fork repository
  2. Create feature branch: git checkout -b feature/name
  3. Make changes + add Biome checks pass
  4. Commit with Conventional Commits
  5. Push and open PR

License

MIT License - see LICENSE

Acknowledgments


Built with ❤️ by Rafael Teixeira

↑ Back to top

About

Production-ready testimonial form with chat UI, Google Sheets integration, and modern stack (Next.js 16, React 19, TypeScript, Biome)

Topics

Resources

Contributing

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages