Create a React app with Vite, add a Supabase project for sign-in and a notes table protected by row-level security, push the code to GitHub and connect the repository to Cloudflare Pages. Every service used here has a free plan, and the whole build takes about three hours.
Tools you need
The 11 steps
Install the basics
Install Node.js LTS, Git and VS Code. Create a free GitHub account if you do not have one. Check with node -v and git --version.
Create the React app
Run npm create vite@latest my-notes -- --template react, then cd my-notes, npm install and npm run dev. Open the local URL to confirm the starter page loads.
Create a Supabase project
Sign in to Supabase, create a new project on the free plan and wait for it to provision. In Project Settings, open the API section and copy the Project URL and the anon (publishable) key.
Create the notes table with row-level security
In the SQL editor run:
create table notes (
id uuid primary key default gen_random_uuid(),
user_id uuid not null default auth.uid() references auth.users,
content text not null,
created_at timestamptz default now()
);
alter table notes enable row level security;
create policy "own notes" on notes
for all using (auth.uid() = user_id) with check (auth.uid() = user_id);Row-level security is what stops users reading each other's notes.
Connect the app to Supabase
Run npm install @supabase/supabase-js. Create .env.local with VITE_SUPABASE_URL and VITE_SUPABASE_ANON_KEY, and add .env.local to .gitignore. Create src/supabase.js:
import { createClient } from '@supabase/supabase-js'
export const supabase = createClient(
import.meta.env.VITE_SUPABASE_URL,
import.meta.env.VITE_SUPABASE_ANON_KEY
)Add sign-in
In Supabase, under Authentication, keep the Email provider enabled. In the app, add a form that calls supabase.auth.signInWithOtp({ email }) for a magic link, and listen with supabase.auth.onAuthStateChange to know when the user is signed in.
Build the notes screen
After sign-in, load notes with supabase.from('notes').select('*').order('created_at', { ascending: false }), insert with .insert({ content }) and delete with .delete().eq('id', id). Render a list with a text box and a delete button.
Push to GitHub
Run git init, commit, create an empty repository on GitHub and push. Confirm .env.local is not in the repository.
Deploy on Cloudflare Pages
In the Cloudflare dashboard open Workers & Pages, create a Pages project and connect your GitHub repository. Set the build command to npm run build and the output directory to dist. Add the two environment variables from step 5. Save and deploy; you get a *.pages.dev URL.
Allow the live URL in Supabase
In Supabase Authentication, open URL Configuration and set the Site URL and a redirect URL to your Pages URL. Without this, magic links send users back to localhost.
Test and share
Open the live URL in a private window, sign in, add a note, sign out, sign in as a second email and confirm you cannot see the first user's notes. Put the link in your README and resume.
Make it yours
- A todo list with due dates and a completed filter.
- An expense tracker with a monthly total per user.
- A habit tracker with a streak count.
- A shared list: add a
team_idcolumn and a policy that checks membership.
Troubleshooting
| Problem | Fix |
|---|---|
| Blank page after deploy | The output directory is wrong. Vite builds to dist; set that in Pages. |
| Magic link sends me to localhost | Add the Pages URL in Supabase URL Configuration (step 10). |
| Insert fails with a policy error | Row-level security is on but the policy is missing or the user is not signed in. Re-run the policy SQL and check the session. |
| Keys committed to GitHub | Rotate the anon key in Supabase and remove the file from history; the anon key is public by design but rotate anyway if a service key leaked. |
Frequently asked questions
Is Cloudflare Pages free?
Yes. The free plan covers unlimited static requests and hundreds of builds a month, enough for student and portfolio projects.
Is Supabase free?
Supabase has a free plan with a Postgres database, auth and storage limits that cover small apps. Projects on the free plan pause after a period of inactivity; open the dashboard to resume.
Do I need a backend server?
No. Supabase provides the database and auth; row-level security policies enforce who can read and write.
Can I use Next.js instead of Vite?
Yes. Cloudflare Pages supports Next.js; the Supabase steps are the same.
Learn this with mentors
Programmes for builders and engineers with admissions open: live classes, mentors and project work.





