Skip to content

Repository files navigation

Evva

Evva is a Luma clone built for India — a place to discover and host events across 30 major Indian cities. I built it to see how far Next.js Server Components and Supabase could take a full-stack app without writing a separate backend: no REST layer, no service you have to deploy and babysit, just server-rendered pages and Server Actions talking straight to Postgres.

It's not a mockup. RSVPs, sign-ups, and event creation all hit a real Supabase project — create an account and the data is really there tomorrow.

🌐 Live: evva-gray.vercel.app

What it does

  • Browse events by city or category, or search across all of them
  • Every city gets its own landing page with local imagery
  • Hosts can create events — venue or online, free or priced in INR, with a capacity cap and an optional AI-generated cover banner (pick a style — vibrant, minimal, cinematic — and Gemini generates the image)
  • Events can be saved as drafts (only the host sees them) or published; hosts can also require approval before an RSVP is confirmed instead of auto-accepting everyone
  • Email/password auth, with sessions handled server-side via cookies

Stack, and why

Layer Choice
Framework Next.js 16 (App Router, Server Components, Server Actions, Turbopack)
Language TypeScript
Styling Tailwind CSS v4
Database Supabase Postgres (ap-south-1, Mumbai)
Auth Supabase Auth — email/password, cookie-based SSR sessions
Validation zod
Deployment Vercel (app) + Supabase (everything else)

The short version of why this combination: Server Components let pages read straight from Postgres with no API layer in between, Server Actions do the same for writes (sign up, create an event, RSVP), and Supabase gives you a real Postgres database with row-level security instead of you writing your own auth and permissions layer. The result is an app with no backend to deploy, patch, or scale on its own — Vercel and Supabase handle that.

Project structure

src/
  app/
    page.tsx                  # Homepage — hero, popular cities, featured events
    discover/page.tsx         # Search + filter all events (city, category, text)
    city/[slug]/page.tsx      # Per-city landing page (one of the 30 cities)
    e/[slug]/page.tsx         # Event detail page + RSVP
    create/page.tsx           # Host: create-event form (auth required)
    dashboard/page.tsx        # Attendee/host dashboard (auth required)
    login/page.tsx, signup/page.tsx
  components/                 # Header, Footer, EventCard, CityGrid, forms, etc.
  lib/
    supabase/
      client.ts               # Browser Supabase client
      server.ts                # Server Component / Server Action Supabase client
      middleware.ts            # Session-refresh logic used by proxy.ts
      database.types.ts        # Generated Postgres types (Database, Tables<>)
    actions/
      auth.ts                  # signUp / signIn / signOut Server Actions
      events.ts                # createEvent / registerForEvent / cancelRegistration
    data.ts                    # Typed read helpers (getCities, getUpcomingEvents, …)
    imagegen.ts                # AI cover-banner generation (Gemini) → Supabase Storage
    utils.ts                   # cn(), date/price formatters, slugify
  proxy.ts                     # Refreshes the Supabase session on every request

Next.js 16 renamed middleware.ts to proxy.ts (export function proxy). This project already uses the new convention, in case you're used to the old one.

Database

Everything lives in the public schema with Row Level Security turned on:

  • profiles — one row per user, created automatically by a trigger when someone signs up
  • cities — the 30 seeded Indian cities
  • categories — 12 event categories (Tech, Music, Business, Comedy, …)
  • events — title, description, city/category, venue or online link, start/end time (stored UTC, shown in Asia/Kolkata), capacity, price, status
  • registrations — one row per RSVP, each with a unique ticket code
  • saved_events — bookmarks

The RLS policies boil down to: published events and city/category lists are public; hosts can only read/write their own events; and everyone can only touch their own registrations — except hosts, who can also see who's registered for their events.

Running it locally

npm install
cp .env.example .env.local   # fill in your Supabase project URL + anon key
npm run dev

Then open http://localhost:3000.

Environment variables

NEXT_PUBLIC_SUPABASE_URL=       # Project Settings → API
NEXT_PUBLIC_SUPABASE_ANON_KEY=  # Project Settings → API (publishable/anon key)
NEXT_PUBLIC_MAPBOX_TOKEN=       # optional — powers the city map
GOOGLE_AI_API_KEY=              # optional — powers AI-generated cover banners

Pointing it at your own Supabase project

The schema (tables, RLS policies, the handle_new_user trigger, and the 30-city/12-category seed data) was built directly via Supabase migrations during development. To run this against a fresh project, recreate that same schema — tables, then RLS policies, then seed data — before starting the app.

Scripts

npm run dev     # start the dev server (Turbopack)
npm run build   # production build + typecheck
npm run start   # run the production build
npm run lint    # eslint

A few implementation notes

  • No hand-rolled API routes for writes. Sign up/in/out, creating an event, RSVPing, cancelling — all Server Actions.
  • RLS does the security work. The browser never sees a service-role key; every read and write is scoped by Postgres row-level security tied to auth.uid().
  • Built India-first, not internationalized. Times default to Asia/Kolkata, prices default to INR, and the city list is the fixed set of 30 — this isn't trying to be a generic multi-country app.
  • RSVP button stays snappy without giving up Server Components. RegisterButton is a small client island wrapping the Server Action in useTransition, so the rest of the event page can stay server-rendered.
  • City imagery is bundled, not fetched. Cards and city pages read from a local slug → image map (lib/cityImages.ts) backed by public/cities/, so there's no dependency on a remote image host at runtime.

Deploying

The app is deployed on Vercel and connected to this repo, so pushes to main go live automatically. If you fork this, you'll need to set the same environment variables in your Vercel project settings and point them at your own Supabase project.

About

Luma clone for India — discover and host events across 30 Indian cities. Next.js 16 Server Actions + Supabase, no separate backend.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages