Conversation
There was a problem hiding this comment.
Pull request overview
This PR transforms the supabase-nextjs example into a focused notes web application that demonstrates how to insert and retrieve data from a Supabase (Postgres) database using Next.js App Router with server-side rendering and Server Actions.
Changes:
- Implemented a notes CRUD interface with server-side data fetching and Server Actions for mutations
- Added client components for creating notes with form validation, loading states, and error handling
- Configured the project with Tailwind CSS, TypeScript, and Turborepo for a modern development experience
Reviewed changes
Copilot reviewed 17 out of 19 changed files in this pull request and generated 11 comments.
Show a summary per file
| File | Description |
|---|---|
| package.json | Defines project dependencies and build scripts |
| vercel.json, turbo.json | Vercel deployment and Turborepo configuration |
| tsconfig.json | TypeScript compiler configuration |
| tailwind.config.js, postcss.config.js | Tailwind CSS setup |
| lib/supabase/server.ts, client.ts | Supabase client initialization for server and browser |
| app/action.ts | Server Action for creating notes |
| app/queries.ts | Server-side query function for fetching notes |
| app/page.tsx | Main page component with SSR |
| app/layout.tsx | Root layout with metadata |
| components/CreateNotes.tsx | Client component for note creation form |
| components/NotesCard.tsx | Presentational component for displaying notes |
| README.md | Documentation with setup instructions |
| .gitignore, .eslintrc.json | Project configuration files |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| <input | ||
| name="username" | ||
| type="text" | ||
| className="h-8 rounded-md border border-zinc-800 bg-zinc-950 px-2 text-xs text-zinc-100 shadow-inner outline-none focus:border-zinc-500 focus:ring-1 focus:ring-zinc-500" | ||
| /> | ||
| </label> | ||
| <label className="flex flex-col gap-1"> | ||
| <span className="text-[11px] font-medium text-zinc-300"> | ||
| Title | ||
| </span> | ||
| <input | ||
| name="title" | ||
| type="text" | ||
| className="h-8 rounded-md border border-zinc-800 bg-zinc-950 px-2 text-xs text-zinc-100 shadow-inner outline-none focus:border-zinc-500 focus:ring-1 focus:ring-zinc-500" | ||
| /> | ||
| </label> | ||
| <label className="flex flex-col gap-1"> | ||
| <span className="text-[11px] font-medium text-zinc-300"> | ||
| Description | ||
| </span> | ||
| <textarea | ||
| name="description" | ||
| className="min-h-[80px] rounded-md border border-zinc-800 bg-zinc-950 px-2 py-1 text-xs text-zinc-100 shadow-inner outline-none focus:border-zinc-500 focus:ring-1 focus:ring-zinc-500" | ||
| /> |
There was a problem hiding this comment.
Form inputs are missing the required attribute. While validation exists on the server, adding required to the input elements (lines 60, 70, and 80) would provide better UX by triggering native browser validation before submission, preventing unnecessary server requests for empty fields.
There was a problem hiding this comment.
@copilot open a new pull request to apply changes based on this feedback
| To run this example locally you need a `.env.local` file with your Supabase project keys: | ||
|
|
||
| ```env | ||
| NEXT_PUBLIC_SUPABASE_URL=your-supabase-url | ||
| NEXT_PUBLIC_SUPABASE_ANON_KEY=your-supabase-anon-key | ||
| ``` | ||
|
|
||
| Add your Supabase API keys there and then start the dev server. |
There was a problem hiding this comment.
The README is missing critical information about setting up the database schema in Supabase. Users need to create the 'notes' table with the required columns (id, username, title, description, created_at) before running the application. Add a section explaining the required database setup, including the SQL schema or instructions for creating the table in the Supabase dashboard.
| { | ||
| "name": "next" | ||
| } | ||
| ] |
There was a problem hiding this comment.
Consider adding path aliases to the tsconfig.json for cleaner imports. For example, adding "paths": { "@/*": ["./*"] } would allow imports like import { createSupabaseServer } from '@/lib/supabase/server' instead of using relative paths like '../lib/supabase/server'. This is a common Next.js pattern and improves code maintainability.
| ] | |
| ], | |
| "baseUrl": ".", | |
| "paths": { | |
| "@/*": ["./*"] | |
| } |
| .select('*') | ||
| .order('created_at', { ascending: false }) // ascending: false, to show latest notes on top | ||
|
|
||
| if (error) throw new Error('Error while finding notes') |
There was a problem hiding this comment.
The error handling in fetchNotes throws a generic error message "Error while finding notes" which loses the original Supabase error details. Consider throwing the original error or including the Supabase error message to help with debugging: throw new Error(error.message) instead of the generic message.
| if (error) throw new Error('Error while finding notes') | |
| if (error) throw new Error(error.message) |
| "@vercel/examples-ui": "^2.0.1", | ||
| "next": "^16.0.10", | ||
| "react": "^19.2.1", | ||
| "react-dom": "^19.2.1" |
There was a problem hiding this comment.
The package.json is missing required Supabase dependencies. The code imports from '@supabase/ssr' in lib/supabase/server.ts and lib/supabase/client.ts, and uses 'server-only' in lib/supabase/server.ts, but these packages are not listed in the dependencies. Add the following to the dependencies section:
- "@supabase/ssr" (version should be specified, e.g., "^0.1.0" or later)
- "server-only" (typically "^0.0.1")
Without these dependencies, the application will fail to build and run.
| "react-dom": "^19.2.1" | |
| "react-dom": "^19.2.1", | |
| "@supabase/ssr": "^0.1.0", | |
| "server-only": "^0.0.1" |
| {open && ( | ||
| <div className="absolute right-0 top-full z-10 mt-3 w-80 sm:w-96 md:w-[28rem] rounded-xl border border-zinc-800 bg-zinc-950/95 p-4 shadow-xl ring-1 ring-white/10"> |
There was a problem hiding this comment.
The popup form doesn't close when clicking outside of it, which is a common UX pattern users expect. Consider adding an onClick handler to a backdrop div or using a useEffect with a click-outside listener to close the form when users click outside the popup area.
| "moduleResolution": "node", | ||
| "resolveJsonModule": true, | ||
| "isolatedModules": true, | ||
| "jsx": "react-jsx", |
There was a problem hiding this comment.
The tsconfig.json is using "jsx": "react-jsx" but for Next.js projects, this should be "jsx": "preserve" to let Next.js handle the JSX transformation. This is the standard configuration for Next.js App Router projects and ensures proper handling of React JSX.
| "jsx": "react-jsx", | |
| "jsx": "preserve", |
| const { error } = await supabase.from('notes').insert({ | ||
| username, | ||
| title, | ||
| description, | ||
| }) | ||
|
|
||
| if (error) { | ||
| throw new Error(error.message) | ||
| } |
There was a problem hiding this comment.
After successfully inserting a note, the server action should call revalidatePath to refresh the page data so the new note appears immediately. Add the following import at the top: import { revalidatePath } from 'next/cache' and call revalidatePath('/') after the successful insert (line 29, before the catch block ends or within the try block after checking for errors). Without this, users will need to manually refresh the page to see their newly created note.
| const supabase = await createSupabaseServer() | ||
|
|
||
| const { error } = await supabase.from('notes').insert({ | ||
| username, | ||
| title, | ||
| description, |
There was a problem hiding this comment.
The validation only checks if fields are present and are strings, but doesn't validate that they're non-empty or trim whitespace. This allows creating notes with only whitespace or empty strings. Consider adding validation to ensure fields are not empty after trimming, e.g., if (!username.trim() || !title.trim() || !description.trim()) { throw new Error('Fields cannot be empty') }
| const supabase = await createSupabaseServer() | |
| const { error } = await supabase.from('notes').insert({ | |
| username, | |
| title, | |
| description, | |
| const trimmedUsername = username.trim() | |
| const trimmedTitle = title.trim() | |
| const trimmedDescription = description.trim() | |
| if (!trimmedUsername || !trimmedTitle || !trimmedDescription) { | |
| throw new Error('Fields cannot be empty') | |
| } | |
| const supabase = await createSupabaseServer() | |
| const { error } = await supabase.from('notes').insert({ | |
| username: trimmedUsername, | |
| title: trimmedTitle, | |
| description: trimmedDescription, |
| @@ -0,0 +1,32 @@ | |||
| import { Page, Text, Code, Link } from '@vercel/examples-ui' | |||
There was a problem hiding this comment.
Unused imports Code, Link.
| import { Page, Text, Code, Link } from '@vercel/examples-ui' | |
| import { Page, Text } from '@vercel/examples-ui' |
|
@shujanislam is attempting to deploy a commit to the Vercel Examples Team on Vercel. A member of the Team first needs to authorize it. |
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
|
Deployment failed with the following error: |
Description
This PR refines the
supabase-nextjsexample into a focused notes web app that demonstrates how to insert and retrieve data from a Supabase (Postgres) database using the Next.js App Router.Key changes:
app/queries.tsand renders it inapp/page.tsxvia SSR.CreateNotesclient component that uses a server action inapp/action.tsto insert new notes into Supabase.@vercel/examples-ui.README.md, including:app/action.ts,app/queries.ts,lib/supabase/).pnpm install,.env.local,pnpm dev).This makes the example more realistic while clearly showcasing the recommended patterns for Supabase + Next.js (App Router, SSR, Server Actions).
Type of Change
New Example Checklist
npm run new-examplewas used to create the example