Full Setup Guide
Documentation
A complete step-by-step guide to install, configure, and deploy your cinematic portfolio using Next.js, Supabase, and Resend.
1. Getting Started
Clone the repository and install all dependencies. This project uses Next.js App Router, Tailwind CSS, GSAP, and Supabase.
git clone https://github.com/Saarangggg/nextjs-cinematic-portfolio.git cd nextjs-cinematic-portfolio npm install
After install, the project requires environment variables before it can run. Continue to the next steps.
2. Supabase Setup
Your portfolio gallery, admin dashboard, analytics, and contact data are all powered by Supabase — a free open-source Firebase alternative.
Step 1 — Create Account
Go to supabase.com, click Start your project, and sign in with GitHub or Email.
Step 2 — Create New Project
| Field | Example |
|---|---|
| Project Name | sarang-portfolio |
| Database Password | Use a strong password |
| Region | Mumbai / Singapore |
Step 3 — Copy API Keys
Go to Project Settings → API and copy these three values:
- •
Project URL→NEXT_PUBLIC_SUPABASE_URL - •
Anon/Public Key→NEXT_PUBLIC_SUPABASE_ANON_KEY - •
Service Role Key→SUPABASE_SERVICE_ROLE_KEY
3. Create Database Tables
Go to Supabase Dashboard → SQL Editor → New Query, paste the SQL below, and click Run.
-- ── Works (portfolio gallery) ──────────────────
CREATE TABLE IF NOT EXISTS works (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
title TEXT NOT NULL,
description TEXT NOT NULL DEFAULT '',
category TEXT NOT NULL DEFAULT 'Website',
tech TEXT NOT NULL DEFAULT '',
image_url TEXT NOT NULL DEFAULT '',
link TEXT NOT NULL DEFAULT '',
sort_order INTEGER NOT NULL DEFAULT 0,
visible BOOLEAN NOT NULL DEFAULT TRUE,
gallery JSONB NOT NULL DEFAULT '[]',
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- ── Projects (case-study pages) ─────────────────
CREATE TABLE IF NOT EXISTS projects (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
title TEXT NOT NULL,
description TEXT NOT NULL DEFAULT '',
category TEXT NOT NULL DEFAULT 'Website',
tech TEXT NOT NULL DEFAULT '',
image_url TEXT NOT NULL DEFAULT '',
link TEXT NOT NULL DEFAULT '',
sort_order INTEGER NOT NULL DEFAULT 0,
visible BOOLEAN NOT NULL DEFAULT TRUE,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- ── Inquiries (contact form) ─────────────────────
CREATE TABLE IF NOT EXISTS inquiries (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name TEXT NOT NULL,
email TEXT NOT NULL DEFAULT '',
message TEXT NOT NULL DEFAULT '',
ip TEXT NOT NULL DEFAULT '',
read BOOLEAN NOT NULL DEFAULT FALSE,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- ── Reviews (testimonials) ───────────────────────
CREATE TABLE IF NOT EXISTS reviews (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
author TEXT NOT NULL,
role TEXT NOT NULL DEFAULT '',
company TEXT NOT NULL DEFAULT '',
content TEXT NOT NULL,
rating INTEGER NOT NULL DEFAULT 5,
avatar TEXT NOT NULL DEFAULT '',
approved BOOLEAN NOT NULL DEFAULT FALSE,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- ── Visits (page analytics) ─────────────────────
CREATE TABLE IF NOT EXISTS visits (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
page TEXT NOT NULL DEFAULT '/',
referrer TEXT NOT NULL DEFAULT '',
userAgent TEXT NOT NULL DEFAULT '',
ip TEXT NOT NULL DEFAULT '',
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- ── Ad Clicks (link tracking) ───────────────────
CREATE TABLE IF NOT EXISTS ad_clicks (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
label TEXT NOT NULL,
url TEXT NOT NULL,
page TEXT NOT NULL DEFAULT '/',
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- ── Searches (search tracking) ──────────────────
CREATE TABLE IF NOT EXISTS searches (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
query TEXT NOT NULL UNIQUE,
count INTEGER NOT NULL DEFAULT 1,
last_searched TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- ── Settings (social links, config) ─────────────
CREATE TABLE IF NOT EXISTS settings (
key TEXT PRIMARY KEY,
value JSONB NOT NULL DEFAULT '{}'
);
INSERT INTO settings (key, value) VALUES
('social_links', '{"instagram":"","github":"","linkedin":"","twitter":"","youtube":"","behance":"","dribbble":"","whatsapp":""}'),
('coming_soon', 'false')
ON CONFLICT (key) DO NOTHING;
-- ── Row Level Security ───────────────────────────
ALTER TABLE works ENABLE ROW LEVEL SECURITY;
ALTER TABLE projects ENABLE ROW LEVEL SECURITY;
ALTER TABLE inquiries ENABLE ROW LEVEL SECURITY;
ALTER TABLE reviews ENABLE ROW LEVEL SECURITY;
ALTER TABLE visits ENABLE ROW LEVEL SECURITY;
ALTER TABLE ad_clicks ENABLE ROW LEVEL SECURITY;
ALTER TABLE searches ENABLE ROW LEVEL SECURITY;
ALTER TABLE settings ENABLE ROW LEVEL SECURITY;
CREATE POLICY "works_public_read" ON works FOR SELECT USING (visible = TRUE);
CREATE POLICY "works_service_all" ON works USING (auth.role() = 'service_role');
CREATE POLICY "projects_public_read" ON projects FOR SELECT USING (visible = TRUE);
CREATE POLICY "projects_service_all" ON projects USING (auth.role() = 'service_role');
CREATE POLICY "inquiries_public_insert" ON inquiries FOR INSERT WITH CHECK (TRUE);
CREATE POLICY "inquiries_service_all" ON inquiries USING (auth.role() = 'service_role');
CREATE POLICY "reviews_public_read" ON reviews FOR SELECT USING (approved = TRUE);
CREATE POLICY "reviews_service_all" ON reviews USING (auth.role() = 'service_role');
CREATE POLICY "visits_public_insert" ON visits FOR INSERT WITH CHECK (TRUE);
CREATE POLICY "visits_service_all" ON visits USING (auth.role() = 'service_role');
CREATE POLICY "ad_clicks_public_insert" ON ad_clicks FOR INSERT WITH CHECK (TRUE);
CREATE POLICY "ad_clicks_service_all" ON ad_clicks USING (auth.role() = 'service_role');
CREATE POLICY "searches_public_insert" ON searches FOR INSERT WITH CHECK (TRUE);
CREATE POLICY "searches_service_all" ON searches USING (auth.role() = 'service_role');
CREATE POLICY "settings_public_read" ON settings FOR SELECT USING (TRUE);
CREATE POLICY "settings_service_all" ON settings USING (auth.role() = 'service_role');
-- ── Storage Bucket (for image uploads) ──────────
INSERT INTO storage.buckets (id, name, public)
VALUES ('project-photos', 'project-photos', TRUE)
ON CONFLICT (id) DO NOTHING;
CREATE POLICY "photos_public_read" ON storage.objects FOR SELECT USING (bucket_id = 'project-photos');
CREATE POLICY "photos_service_upload" ON storage.objects FOR INSERT WITH CHECK (bucket_id = 'project-photos' AND auth.role() = 'service_role');
CREATE POLICY "photos_service_delete" ON storage.objects FOR DELETE USING (bucket_id = 'project-photos' AND auth.role() = 'service_role');4. Resend Email Setup
The contact form uses Resend to deliver messages directly to your inbox.
- Go to resend.com and sign up with GitHub.
- Go to
Dashboard → API Keys → Create API Key. - Set Name:
Portfolio, Permission:Full Access. - Copy the key — it starts with
re_— into your.env.local. - Set
ADMIN_EMAILto the email address where you want to receive notifications.
5. Environment Variables
Create a file called .env.local in the root of your project and paste the following. Fill in each value.
# ───────────────────────────────────────────── # SARANG PORTFOLIO — ENVIRONMENT VARIABLES # NEVER SHARE THIS FILE OR COMMIT TO GITHUB # ───────────────────────────────────────────── # ── SUPABASE ────────────────────────────── NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key SUPABASE_SERVICE_ROLE_KEY=your-service-role-key # ── RESEND ──────────────────────────────── RESEND_API_KEY=re_your_api_key # ── ADMIN AUTH ──────────────────────────── ADMIN_PASSWORD=choose-a-strong-password # ── JWT SECRET ──────────────────────────── JWT_SECRET=your-super-secret-random-string # ── CONTACT EMAIL ───────────────────────── ADMIN_EMAIL=you@example.com # ── WEBSITE URL ─────────────────────────── NEXT_PUBLIC_SITE_URL=https://yourdomain.com # ── WHATSAPP NUMBER ─────────────────────── NEXT_PUBLIC_WHATSAPP_NUMBER=919876543210 # ── SEO VERIFICATION ────────────────────── NEXT_PUBLIC_GOOGLE_VERIFICATION=google-code NEXT_PUBLIC_BING_VERIFICATION=bing-code
.env.local to your .gitignore to keep it private.6. Generate JWT Secret
The admin login uses JWT. Generate a secure random string and paste it as JWT_SECRET.
Mac / Linux:
openssl rand -base64 32
Windows PowerShell:
[System.Web.Security.Membership]::GeneratePassword(32,4)
7. Run Development Server
Start the local dev server. Open your browser at http://localhost:3000.
npm run dev
8. Admin Dashboard
The hidden admin panel is at /admin/login. Use the password from ADMIN_PASSWORD in your .env.local.
9. Adding Projects
No code needed. Everything goes through the Admin Dashboard.
- Log in at
/admin/login. - Go to Projects → Add New Project.
- Fill in the fields:
| Field | Example |
|---|---|
| Title | Nike Concept |
| Description | Cinematic branding project |
| Category | Website / Designs / Photos / Videos |
| Image URL | https://... |
| Project Link | https://... |
The project instantly appears in the frontend gallery after saving.
10. Website Structure
Hero
3D intro animations, GSAP cinematic transitions, smooth scroll.
About
Edit from src/app/about/content.js — bio, tech stack, experience, resume.
Work Gallery
Interactive WebGL circular gallery, powered by Supabase. Categories: Websites, Designs, Photos, Videos.
Contact
Connected to Resend email API and Supabase messages table.
Admin /admin/login
Hidden dashboard to manage all content without touching code.
11. Editing Your Personal Info
Replace all demo content with your own. Use global search in your editor — Ctrl + Shift + F (Windows) or Cmd + Shift + F (Mac) — and search these keywords one by one:
| Search For | Replace With |
|---|---|
| Sarang | Your name |
| example@gmail.com | Your email |
| 919876543210 | Your WhatsApp number |
| yourdomain.com | Your website URL |
| sarang-space.site | Your live domain |
Important files to edit directly:
src/app/about/content.jsName, bio, skills, experience, resume URL
src/app/layout.jsSEO title, description, OpenGraph, keywords
.env.localEmail, WhatsApp number, domain URL
public/Hero video, background images, portrait photos, logos
12. Google Search Console
- Go to Google Search Console and add your domain.
- Choose HTML Tag verification. Copy only the content value from the meta tag.
- Paste it into
.env.localasNEXT_PUBLIC_GOOGLE_VERIFICATION=xxxx. - After deployment, submit your sitemap:
https://yourdomain.com/sitemap.xml.
13. Bing Webmaster Tools
- Go to Bing Webmaster Tools.
- Add your website and copy the verification code.
- Paste into
.env.localasNEXT_PUBLIC_BING_VERIFICATION=xxxx.
14. Deploy to Vercel
This portfolio is optimized for Vercel — the same company that makes Next.js.
- Push your code to a new GitHub repository:
git push. - Go to vercel.com, click Add New → Project.
- Import your GitHub repo.
- Under Environment Variables, paste all keys from your
.env.local. - Click Deploy. Done — your portfolio is live.
Project Settings → Authentication → URL Configuration.15. Security Notes
Built for creators, designers, filmmakers, and developers. This portfolio is fully open-source and customizable.
Back to Open Source