# Quickstart: React (Vite)
Source: https://sovaria.in/docs/quickstart-react

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](https://sovaria.in/docs/quickstart) first: create a project, copy its **project URL** and **anon key** ([Quickstart](https://sovaria.in/docs/quickstart#connect)), and create the `notes` table with its row rules ([Quickstart](https://sovaria.in/docs/quickstart#table)). 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

Terminal:
```bash
npm create vite@latest notes -- --template react-ts
cd notes
npm install
npm install @sovaria/sdk
```

## 2. Add your project URL and anon key

.env.local:
```bash
VITE_SOVARIA_URL=https://<project-ref>.sovarias.in
VITE_SOVARIA_ANON_KEY=<anon-key>
```

## 3. Create the client and a session hook

src/sovaria.ts:
```ts
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)
}
```

src/useSession.ts:
```ts
import { useEffect, useState } from 'react'
import { sovaria } from './sovaria'

export function useSession() {
  const [session, setSession] = useState(() => sovaria.auth.getSession())
  useEffect(() => sovaria.auth.onAuthStateChange((_event, s) => setSession(s)), [])
  return session
}
```

## 4. Sign in and work with notes

src/App.tsx:
```ts
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>
  )
}
```

> **Warning:** Hiding screens protects nothing by itself: the data is protected by the row rules on the server. The `session` check only decides what to show.

## 5. Run it

Terminal:
```bash
npm run dev   # http://localhost:5173
```

Before you deploy, add your site's origin under Project settings › API ([Allowed origins (CORS) for browser apps](https://sovaria.in/docs/allowed-origins)).

## Next steps

- Check who can read and change `notes` before you ship: [Tutorial: prove your table is private before you ship](https://sovaria.in/docs/tutorial-secure-a-table).
- Push new notes to other devices as they happen: [Database change events](https://sovaria.in/docs/realtime-database-changes).
- Protected routes and other React patterns: [JavaScript: React, Next.js, Vite and Node](https://sovaria.in/docs/javascript-frameworks#react).
- Every SDK method: [JavaScript SDK](https://sovaria.in/docs/javascript-reference).
