Skip to content

kortix-ai/vite-supabase-starter

Repository files navigation

Vite + Supabase Starter

A minimal, blazing-fast starter optimized for agentic AI tools like Lovable, Bolt, Cursor, and more.

⚑ Stack

Technology Purpose
Vite ~300ms dev server, instant HMR
React 19 Latest React
TanStack Router Type-safe file-based routing
TanStack Query Async state management
Supabase Auth, database, edge functions (optional)
Tailwind CSS v4 Utility-first styling
shadcn/ui Accessible component library

πŸš€ Quick Start

npm install
npm run dev

πŸ“ Project Structure

β”œβ”€β”€ src/
β”‚   β”œβ”€β”€ components/           # Reusable components
β”‚   β”‚   β”œβ”€β”€ ui/              # shadcn/ui components
β”‚   β”‚   β”œβ”€β”€ error-boundary.tsx
β”‚   β”‚   β”œβ”€β”€ theme-provider.tsx
β”‚   β”‚   └── theme-toggle.tsx
β”‚   β”œβ”€β”€ hooks/               # Custom React hooks
β”‚   β”œβ”€β”€ lib/                 # Utilities and clients
β”‚   β”‚   β”œβ”€β”€ supabase.ts      # Supabase client (optional)
β”‚   β”‚   β”œβ”€β”€ query-client.ts  # TanStack Query config
β”‚   β”‚   └── utils.ts         # cn() helper
β”‚   β”œβ”€β”€ routes/              # File-based routes
β”‚   β”‚   β”œβ”€β”€ __root.tsx       # Root layout (providers)
β”‚   β”‚   β”œβ”€β”€ _404.tsx         # Not found page
β”‚   β”‚   β”œβ”€β”€ index.tsx        # /
β”‚   β”‚   └── examples.tsx     # /examples (Supabase patterns)
β”‚   └── main.tsx             # Entry point
└── supabase/
    └── functions/           # Edge Functions (Deno)
        └── hello/           # Example function

πŸ€– AI Agent Conventions

These conventions help AI agents understand and extend this codebase consistently.

Adding Routes

Create a file in src/routes/:

// src/routes/dashboard.tsx β†’ /dashboard
import { createFileRoute } from '@tanstack/react-router'

export const Route = createFileRoute('/dashboard')({
  component: DashboardPage,
})

function DashboardPage() {
  return <div>Dashboard</div>
}

Dynamic routes:

// src/routes/users/$userId.tsx β†’ /users/:userId
import { createFileRoute } from '@tanstack/react-router'

export const Route = createFileRoute('/users/$userId')({
  component: UserPage,
})

function UserPage() {
  const { userId } = Route.useParams()
  return <div>User {userId}</div>
}

Nested layouts:

// src/routes/_dashboard.tsx β†’ Layout for /dashboard/*
// src/routes/_dashboard/index.tsx β†’ /dashboard
// src/routes/_dashboard/settings.tsx β†’ /dashboard/settings

Data Fetching

Use TanStack Query for all data fetching:

import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'

function MyComponent() {
  const { data, isLoading } = useQuery({
    queryKey: ['items'],
    queryFn: () => fetch('/api/items').then(r => r.json()),
  })

  const queryClient = useQueryClient()
  const mutation = useMutation({
    mutationFn: (newItem) => fetch('/api/items', {
      method: 'POST',
      body: JSON.stringify(newItem),
    }),
    onSuccess: () => queryClient.invalidateQueries({ queryKey: ['items'] }),
  })
}

Adding UI Components

Use shadcn/ui CLI:

npx shadcn@latest add dialog
npx shadcn@latest add dropdown-menu

Components are added to src/components/ui/.

Custom Hooks

Place in src/hooks/ and export from src/hooks/index.ts:

// src/hooks/use-something.ts
export function useSomething() {
  // ...
}

// src/hooks/index.ts
export { useSomething } from './use-something'

Toast Notifications

import { toast } from 'sonner'

toast.success('Saved!')
toast.error('Something went wrong')
toast.loading('Saving...')

Theme Toggle

import { ThemeToggle } from '@/components/theme-toggle'
import { useTheme } from '@/components/theme-provider'

// In a component
const { theme, setTheme, resolvedTheme } = useTheme()

Supabase (When Needed)

import { supabase, isSupabaseConfigured } from '@/lib/supabase'

if (isSupabaseConfigured()) {
  const { data } = await supabase.from('table').select('*')
}

Set in .env:

VITE_SUPABASE_URL=...
VITE_SUPABASE_ANON_KEY=...

πŸ“‹ Common Patterns

Protected Routes (when auth is added)

// src/routes/_authenticated.tsx
import { createFileRoute, redirect } from '@tanstack/react-router'

export const Route = createFileRoute('/_authenticated')({
  beforeLoad: async () => {
    const session = await getSession()
    if (!session) throw redirect({ to: '/login' })
  },
  component: AuthenticatedLayout,
})

Form Handling (when needed)

npm install react-hook-form @hookform/resolvers zod
import { useForm } from 'react-hook-form'
import { zodResolver } from '@hookform/resolvers/zod'
import { z } from 'zod'

const schema = z.object({
  email: z.string().email(),
})

function MyForm() {
  const form = useForm({
    resolver: zodResolver(schema),
  })
  
  return (
    <form onSubmit={form.handleSubmit(onSubmit)}>
      <Input {...form.register('email')} />
    </form>
  )
}

API Layer Pattern

// src/lib/api/items.ts
import { supabase } from '@/lib/supabase'

export const itemsApi = {
  list: () => supabase!.from('items').select('*'),
  get: (id: string) => supabase!.from('items').select('*').eq('id', id).single(),
  create: (data: CreateItem) => supabase!.from('items').insert(data),
  update: (id: string, data: UpdateItem) => supabase!.from('items').update(data).eq('id', id),
  delete: (id: string) => supabase!.from('items').delete().eq('id', id),
}

Edge Functions (Server-Side Logic)

For backend logic, use Supabase Edge Functions (Deno-based):

# Install Supabase CLI
npm install -g supabase

# Create a new function
supabase functions new hello

# Deploy
supabase functions deploy hello
// supabase/functions/hello/index.ts
Deno.serve(async (req) => {
  const { name } = await req.json()
  
  return new Response(
    JSON.stringify({ message: `Hello ${name}!` }),
    { headers: { 'Content-Type': 'application/json' } }
  )
})

Call from client:

const { data } = await supabase.functions.invoke('hello', {
  body: { name: 'World' },
})

Realtime Subscriptions

useEffect(() => {
  if (!supabase) return

  const channel = supabase
    .channel('todos')
    .on(
      'postgres_changes',
      { event: '*', schema: 'public', table: 'todos' },
      (payload) => {
        queryClient.invalidateQueries({ queryKey: ['todos'] })
      }
    )
    .subscribe()

  return () => {
    supabase.removeChannel(channel)
  }
}, [])

πŸ“œ Scripts

# Development
npm run dev              # Start dev server
npm run build            # Production build
npm run preview          # Preview build
npm run lint             # ESLint
npm run typecheck        # TypeScript check
npm run clean            # Clear build cache

# Supabase (requires CLI: npm i -g supabase)
npm run supabase:start   # Start local Supabase
npm run supabase:stop    # Stop local Supabase
npm run supabase:status  # Check status
npm run supabase:gen-types  # Generate TS types from DB
npm run supabase:deploy  # Deploy all Edge Functions
npm run supabase:deploy:hello  # Deploy hello function

Before using Supabase scripts, set your project ID in package.json:

"config": {
  "supabase_project_id": "your-project-id"
}

πŸ”— Resources

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages