
Easily integrate Google Sheets with Next.js 14 using Google Apps Script. Follow our step-by-step guide to set up a dynamic and interactive application.
Sometimes, you just need to save some data into Google Sheets quickly and efficiently without going through the hassle of setting up Google’s Sheets API and SDK. If you’re looking for a fast and straightforward way to handle data storage in your Next.js 14 application, then this tutorial is for you!
In this guide, we’ll explore how to integrate Google Sheets with a Next.js 14 application. This powerful combination allows you to use Google Sheets as a simple backend for storing and retrieving data, making your Next.js application more dynamic and interactive.
Task and IS_DONE. const sheets = SpreadsheetApp.openByUrl("https://docs.google.com/spreadsheets/d/<UniqueId>/edit?gid=0#gid=0");
//if you have changed your sheet name then replace the below Sheet1 with your sheet name
const sheet = sheets.getSheetByName("Sheet1");
function doGet(e) {
var data = sheet.getDataRange().getValues();
return ContentService
.createTextOutput(JSON.stringify({
status: true,
message: "Fetched Successfully.",
data
}));
}
function doPost(e) {
const { task, is_done } = e.parameter;
sheet.appendRow([task, is_done]);
return ContentService
.createTextOutput(JSON.stringify({
status: true,
message: "Saved Successfully.",
data: e.parameter
}));
}<UniqueId> with your sheet’s unique ID.npx create-next-app@14 google-sheets-nextjs
cd google-sheets-nextjslib/api.ts and add the following code to handle API requests:const API_URL = 'https://script.google.com/macros/s/<id>/exec';
export interface ITodo {
task: string,
is_done: boolean
}
export const fetchData = async (): Promise<ITodo[]> => {
try {
const response = await fetch(API_URL);
if (!response.ok) {
throw new Error('Network response was not ok');
}
const data = await response.json();
return data.data as ITodo[];
} catch (error) {
console.error('Error fetching data:', error);
return [];
}
};
export const addData = async (payload: FormData): Promise<ITodo[]> => {
try {
const response = await fetch(API_URL, {
method: "POST",
body: payload
});
if (!response.ok) {
throw new Error('Network response was not ok');
}
const data = await response.json();
return data.data as ITodo[];
} catch (error) {
console.error('Error adding data:', error);
return [];
}
};<id> with your Web app ID from Apps Script.components/Todo.tsx .import React from "react";
const Todo = ({ todos }: { todos: any[] }) => {
return (
<div>
{todos.length === 0 ? (
<p className="text-gray-500">No tasks found.</p>
) : (
<ul className="space-y-2">
{todos.slice(1).map(([task, is_done], index) => (
<li key={index} className="p-2 border border-gray-300 rounded">
<span className={`task ${is_done ? "line-through" : ""}`}>
{task}
</span>
</li>
))}
</ul>
)}
</div>
);
};
export default Todo;app/page.tsx."use client";
import { useEffect, useState } from "react";
import { addData, fetchData, ITodo } from "./api";
import Todo from "./components/Todo";
export default function Home() {
const [todos, setTodos] = useState<any[]>([]);
const [form, setForm] = useState<ITodo>({
task: "",
is_done: false
});
const [loading, setLoading] = useState(false);
useEffect(() => {
const getData = async () => {
setLoading(true);
try {
const data = await fetchData();
setTodos(data);
} catch (error) {
console.error("Error fetching data:", error);
} finally {
setLoading(false);
}
};
getData();
}, []);
const handleSubmit = async (e: any) => {
e.preventDefault();
setLoading(true);
try {
const formData = new FormData();
formData.append('task', form.task);
formData.append('is_done', form.is_done ? 'true' : 'false');
await addData(formData);
const updatedData = await fetchData();
setTodos(updatedData);
setForm({ task: "", is_done: false });
} catch (error) {
console.error("Error adding data:", error);
} finally {
setLoading(false);
}
};
return (
<div className="mx-auto p-4 container">
<h1 className="mb-4 font-bold text-2xl">Next.js 14 with Google Sheets</h1>
<Todo todos={todos} />
{loading && (
<div className="fixed inset-0 flex justify-center items-center bg-gray-100 bg-opacity-50">
<div className="bg-white shadow-md p-4 rounded">Loading...</div>
</div>
)}
<form onSubmit={handleSubmit} className="mt-4">
<div className="flex flex-col space-y-2">
<input
type="text"
value={form.task}
onChange={(e) => setForm({ ...form, task: e.target.value })}
placeholder="Task"
className="p-2 border border-gray-300 rounded w-full"
/>
<label className="flex items-center space-x-2">
<input
type="checkbox"
checked={form.is_done}
onChange={(e) => setForm({ ...form, is_done: e.target.checked })}
className="form-checkbox"
/>
<span className="text-sm">Completed</span>
</label>
<button
type="submit"
className="bg-blue-500 p-2 rounded text-white"
disabled={loading}
>
Add Task
</button>
</div>
</form>
</div>
);
}gloabl.css.@tailwind base;
@tailwind components;
@tailwind utilities;Now run npm run dev
and open http://localhost:3000/
It should look like something this.

It will add data realtime like below.

Your app should look like this

By following these steps, you’ve successfully integrated Google Sheets with a Next.js 14 application using Google Apps Script.