how to connect a google sheets with next.js forms to save the data? create an implementation for it and I will implement it manually. Make sure to be as detailed as possible and we are using next.js 15 with app route and typescript
I'll provide a detailed implementation for connecting Google Sheets with Next.js forms to save data. We'll use Next.js 15 with the App Router and TypeScript. Here's a step-by-step guide:
Set up Google Cloud Project and enable Google Sheets API:
- Go to the Google Cloud Console (https://console.cloud.google.com/)
- Create a new project or select an existing one
- Search for "Google Sheets API" and enable it for your project
Create service account credentials:
- In the Google Cloud Console, go to "APIs & Services" > "Credentials"
- Click "Create Credentials" and select "Service Account"
- Fill in the details and create the service account
- Once created, go to the service account details and create a new key (JSON format)
- Download the JSON key file
Install necessary dependencies: Run the following command in your project directory:
npm install google-auth-library googleapis
- Create a server action to handle form submission:
Create a new file app/actions/submitForm.ts
:
'use server'
import { google } from 'googleapis';
import { GoogleAuth } from 'google-auth-library';
// Function to append data to Google Sheets
async function appendToSheet(data: string[]) {
const auth = new GoogleAuth({
keyFile: process.env.GOOGLE_APPLICATION_CREDENTIALS,
scopes: ['https://www.googleapis.com/auth/spreadsheets'],
});
const sheets = google.sheets({ version: 'v4', auth });
const spreadsheetId = process.env.GOOGLE_SHEET_ID;
const range = 'Sheet1!A:Z'; // Adjust this range as needed
try {
const response = await sheets.spreadsheets.values.append({
spreadsheetId,
range,
valueInputOption: 'USER_ENTERED',
requestBody: {
values: [data],
},
});
console.log('Data appended successfully');
return response.data;
} catch (error) {
console.error('Error appending data to sheet:', error);
throw error;
}
}
export async function submitForm(formData: FormData) {
const name = formData.get('name') as string;
const email = formData.get('email') as string;
const message = formData.get('message') as string;
if (!name || !email || !message) {
throw new Error('All fields are required');
}
const timestamp = new Date().toISOString();
const dataToAppend = [timestamp, name, email, message];
try {
await appendToSheet(dataToAppend);
return { success: true, message: 'Form submitted successfully' };
} catch (error) {
console.error('Error submitting form:', error);
return { success: false, message: 'Error submitting form' };
}
}
- Create a form component:
Create a new file app/components/ContactForm.tsx
:
'use client'
import { useState } from 'react';
import { useFormState } from 'react-dom';
import { submitForm } from '../actions/submitForm';
export default function ContactForm() {
const [state, formAction] = useFormState(submitForm, null);
const [isPending, setIsPending] = useState(false);
const handleSubmit = async (formData: FormData) => {
setIsPending(true);
await formAction(formData);
setIsPending(false);
};
return (
<form action={handleSubmit} className="space-y-4">
<div>
<label htmlFor="name" className="block text-sm font-medium text-gray-700">Name</label>
<input
type="text"
id="name"
name="name"
required
className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50"
/>
</div>
<div>
<label htmlFor="email" className="block text-sm font-medium text-gray-700">Email</label>
<input
type="email"
id="email"
name="email"
required
className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50"
/>
</div>
<div>
<label htmlFor="message" className="block text-sm font-medium text-gray-700">Message</label>
<textarea
id="message"
name="message"
required
rows={4}
className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50"
></textarea>
</div>
<button
type="submit"
disabled={isPending}
className="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
>
{isPending ? 'Submitting...' : 'Submit'}
</button>
{state && (
<div className={`mt-4 ${state.success ? 'text-green-600' : 'text-red-600'}`}>
{state.message}
</div>
)}
</form>
);
}
- Implement the form in a page:
Create or update the file app/page.tsx
:
import ContactForm from './components/ContactForm';
export default function Home() {
return (
<main className="container mx-auto px-4 py-8">
<h1 className="text-3xl font-bold mb-6">Contact Us</h1>
<ContactForm />
</main>
);
}
- Set up environment variables:
Create a .env.local
file in your project root and add the following:
GOOGLE_APPLICATION_CREDENTIALS=path/to/your/service-account-key.json
GOOGLE_SHEET_ID=your-google-sheet-id
Replace path/to/your/service-account-key.json
with the actual path to your downloaded service account key file, and your-google-sheet-id
with the ID of the Google Sheet you want to use (you can find this in the sheet's URL).
- Update your
next.config.js
:
/** @type {import('next').NextConfig} */
const nextConfig = {
experimental: {
serverActions: true,
},
}
module.exports = nextConfig
This implementation provides a detailed setup for connecting a Google Sheet with a Next.js form. Here's what it does:
- The
submitForm
server action handles form submission and interacts with the Google Sheets API. - The
ContactForm
component renders the form and handles client-side state. - The form data is sent to the server action, which then appends it to the specified Google Sheet.
- Error handling and success/failure messages are implemented.
To implement this manually:
- Set up your Google Cloud Project and enable the Sheets API as described.
- Create the service account and download the key file.
- Install the required dependencies.
- Create the files as shown above (
submitForm.ts
,ContactForm.tsx
,page.tsx
). - Set up your environment variables.
- Update your
next.config.js
.
Make sure to keep your service account key secure and never commit it to version control. You may want to add the key file path and .env.local
to your .gitignore
file.
This implementation uses Server Actions, which are a new feature in Next.js 13 and later 1. It also follows the App Router structure, placing components and server actions in the appropriate directories 2.
Remember to handle any necessary error cases and add additional form validation as needed for your specific use case.