Skip to content

React single-page app

A complete single-page React app built with Vite. src/lib/vaif.ts creates the client with the publishable key, and src/main.tsx wraps the app in the React SDK’s provider, so the hooks work in every component and requests run as the signed-in user.

Terminal window
vaif init --template react-spa

Run it in a new, empty folder. It writes vaif.config.json and the template's files there. It stops if the folder already has a vaif.config.json, and leaves any other file that already exists alone; --force overwrites both.

It asks which features to include. This template has files for Database, Authentication, Realtime, Storage and Functions; Database and Authentication are selected to start with. To skip the question, name them: vaif init --template react-spa --features database,auth,realtime,storage,functions. Add one later with vaif init --template react-spa --add-features <feature>.

FileWritten
package.jsonAlways
tsconfig.jsonAlways
vite.config.tsAlways
index.htmlAlways
src/main.tsxAlways
src/App.tsxAlways; replaced by the Authentication version when you include that feature
src/lib/vaif.tsAlways
src/vite-env.d.tsAlways
.env.exampleAlways
.gitignoreAlways
README.mdAlways
drizzle/0001_initial.sqlWith Database
drizzle.config.tsWith Database
src/db/schema.tsWith Database
src/pages/Login.tsxWith Authentication
src/pages/Signup.tsxWith Authentication
src/components/AuthGuard.tsxWith Authentication
src/hooks/useRealtimeSubscription.tsWith Realtime
src/hooks/useFileUpload.tsWith Storage
functions/hello.tsWith Functions

With Authentication, the app gets sign-in and sign-up pages and an AuthGuard component that shows its children only to a signed-in user.

Its package.json lists @vaifstudio/client and @vaifstudio/react, with react, react-dom and react-router-dom, so npm install in the folder installs everything.

Copy .env.example to .env and fill it in. The project ID and the keys are on Security > Credentials in the console.

VariableValue
VITE_VAIF_API_URLThe API address. Keep the value in the file, https://api.vaif.studio.
VITE_VAIF_PROJECT_IDYour project ID, for the app.
VITE_VAIF_PUBLISHABLE_KEYThe project's publishable key (vk_pub_). Row rules apply to it, so it is safe in an app.

Vite only exposes variables that start with VITE_ to the browser, which is why the names have that prefix. A browser app needs only the project ID and the publishable key; never put a secret key in it.

The template writes one SQL file for your database. The comment line above each CREATE TABLE sets that table's row rules.

drizzle/0001_initial.sql, written with the Database feature, is an example: edit it to describe your own tables before you apply it.

drizzle/0001_initial.sql
-- Initial migration. Customize this schema for your project; vaif db push
-- applies it. The comment directly above each CREATE TABLE sets that table's
-- row rules: here each signed-in user reads and changes only their own rows,
-- and the owner column is filled in from their sign-in on insert. Keep a rule
-- comment on every table you add: a table with no row rules is closed to
-- signed-out visitors but open to every signed-in user.
-- vaif:owner id
CREATE TABLE IF NOT EXISTS "users" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid(),
"email" text NOT NULL UNIQUE,
"name" text,
"created_at" timestamp with time zone NOT NULL DEFAULT now()
);
-- vaif:owner user_id
CREATE TABLE IF NOT EXISTS "posts" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid(),
"user_id" uuid NOT NULL REFERENCES "users"("id"),
"title" text NOT NULL,
"content" text,
"published" boolean NOT NULL DEFAULT false,
"created_at" timestamp with time zone NOT NULL DEFAULT now()
);
  • users (-- vaif:owner id): each signed-in user reads and changes only their own rows, and id is filled in from their sign-in.
  • posts (-- vaif:owner user_id): each signed-in user reads and changes only their own rows, and user_id is filled in from their sign-in.

Put your project ID in vaif.config.json ("projectId"), sign in, and apply it. With no --dir, vaif db push finds the template's SQL folder, or a ./migrations folder, and applies its .sql files in name order.

Terminal window
vaif login
vaif db push --dry-run # what each file would change
vaif db push

Keep a rule comment on every table you add: a table with no row rules is open to every signed-in user. See Rules from a schema file.

Terminal window
npm run dev