Quickstart: React (Vite)
Build the notes app with React and Vite: a session hook, a sign-in form, and a list of the signed-in user's notes with an add box.
A React single-page app built with Vite, where people sign up, sign in, add notes and see only their own.
#Before you start
Do steps 1 to 3 of the Quickstart first: create a project, copy its project URL and anon key (Quickstart), and create the notes table with its row rules (Quickstart). Every signed-in user then sees and changes only their own notes, whatever the app code does.
#1. Create the app and install the SDK
npm create vite@latest notes -- --template react-ts
cd notes
npm install
npm install @sovaria/sdk#2. Add your project URL and anon key
VITE_SOVARIA_URL=https://<project-ref>.sovarias.in
VITE_SOVARIA_ANON_KEY=<anon-key>#3. Create the client and a session hook
import { createClient } from '@sovaria/sdk'
export const sovaria = createClient(import.meta.env.VITE_SOVARIA_URL, import.meta.env.VITE_SOVARIA_ANON_KEY)
// data.sql answers { columns, rows, row_count }; rows are arrays in column order.
export function toObjects<T>(result: { columns: string[]; rows: unknown[][] }): T[] {
return result.rows.map((row) => Object.fromEntries(result.columns.map((c, i) => [c, row[i]])) as T)
}#4. Sign in and work with notes
import { useEffect, useState, type FormEvent } from 'react'
import { SovariaError } from '@sovaria/sdk'
import { sovaria, toObjects } from './sovaria'
import { useSession } from './useSession'
type Note = { id: number; body: string }
export default function App() {
const session = useSession()
const [notes, setNotes] = useState<Note[]>([])
const [error, setError] = useState('')
const fail = (err: unknown) => setError(err instanceof SovariaError ? err.message : String(err))
const load = () =>
sovaria.data.sql('select id, body from notes order by id desc')
.then((r) => setNotes(toObjects<Note>(r as never)))
.catch(fail)
useEffect(() => { if (session) load() }, [session])
async function signIn(form: HTMLFormElement, create: boolean) {
const f = new FormData(form)
const [email, password] = [String(f.get('email')), String(f.get('password'))]
try {
await (create ? sovaria.auth.signUp(email, password) : sovaria.auth.signIn(email, password))
} catch (err) { fail(err) }
}
async function add(e: FormEvent<HTMLFormElement>) {
e.preventDefault()
const input = e.currentTarget.elements.namedItem('body') as HTMLInputElement
try {
await sovaria.data.sql('insert into notes (body) values ($1)', [input.value])
input.value = ''
await load()
} catch (err) { fail(err) }
}
if (!session) return (
<form onSubmit={(e) => { e.preventDefault(); signIn(e.currentTarget, false) }}>
<input name="email" type="email" required /> <input name="password" type="password" required />
<button>Sign in</button>
<button type="button" onClick={(e) => signIn(e.currentTarget.form!, true)}>Create account</button>
{error && <p role="alert">{error}</p>}
</form>
)
return (
<main>
<form onSubmit={add}><input name="body" required maxLength={2000} /> <button>Add</button></form>
{error && <p role="alert">{error}</p>}
<ul>{notes.map((n) => <li key={n.id}>{n.body}</li>)}</ul>
<button onClick={() => sovaria.auth.signOut()}>Sign out</button>
</main>
)
}#5. Run it
npm run dev # http://localhost:5173Before you deploy, add your site's origin under Project settings › API (Allowed origins (CORS) for browser apps).
#Next steps
- Check who can read and change
notesbefore you ship: Tutorial: prove your table is private before you ship. - Push new notes to other devices as they happen: Database change events.
- Protected routes and other React patterns: JavaScript: React, Next.js, Vite and Node.
- Every SDK method: JavaScript SDK.
#Coming from Firebase or Supabase?
| Platform | What this is called there |
|---|---|
| Firebase | Firebase with React |
| Supabase | Supabase React quickstart |
| Sovaria | Quickstart: React (Vite) |
Moving an existing app? Migrate from Supabase or Firebase imports users (with their passwords), data, files and rules.