Skip to Content
QuickstartsVite (React)

Vite (React)

Build and query a ZXCV database from a Vite + React single-page app, then ship it to the edge. You’ll end with a component that lists live data in the browser.

AI Assistance
Help me connect ZXCV to my Vite + React app. Do the following:
1. Install the @zxcv.build/sdk package.
2. Initialize a client with my project ref + anon key (from Settings → API), using Vite's env convention (VITE_ prefix in .env, read off import.meta.env).
3. Fetch rows from a "todos" table in a useEffect and render them idiomatically.
4. Keep the anon key on the client — authorization is enforced server-side by my policies.
Ask me for my project ref and anon key if I haven't given them.

A Vite SPA has no server, so it fetches from the browser. That’s fine here — the anon key is public and safe to ship, because access is enforced server-side by your authorization rules.

Create a project

In the dashboard  click Start building, or run zxcv from your terminal. Open Settings → API and copy your project ref and anon key.

Create a table

In the SQL editor, create a todos table with a couple of rows:

create table todos ( id bigint generated always as identity primary key, title text not null, is_complete boolean not null default false ); insert into todos (title) values ('Read the docs'), ('Ship an app');

Install the SDK

bash npm install @zxcv.build/sdk

Initialize the client

Add your keys to .env. Vite only exposes variables prefixed with VITE_ to the browser, which is exactly what the anon key needs:

.env
VITE_ZXCV_PROJECT=acme-store VITE_ZXCV_ANON_KEY=your-anon-key

Create a shared client. In Vite you read env vars off import.meta.env:

src/zxcv.ts
import { createClient } from '@zxcv.build/sdk' export const zxcv = createClient({ project: import.meta.env.VITE_ZXCV_PROJECT, anonKey: import.meta.env.VITE_ZXCV_ANON_KEY, })

Fetch and render

There’s no server component, so fetch on the client inside useEffect:

src/Todos.tsx
import { useEffect, useState } from 'react' import { zxcv } from './zxcv' type Todo = { id: number; title: string; is_complete: boolean } export function Todos() { const [todos, setTodos] = useState<Todo[]>([]) useEffect(() => { zxcv .from('todos') .select('id, title, is_complete') .order('id') .then(({ data, error }) => { if (error) console.error(error) else setTodos(data ?? []) }) }, []) return ( <ul> {todos.map((todo) => ( <li key={todo.id}>{todo.title}</li> ))} </ul> ) }

Render it from your app root:

src/App.tsx
import { Todos } from './Todos' export default function App() { return <Todos /> }

Want live updates instead of a one-time fetch? Swap the useEffect for a Realtime subscription — it’s the same client, and the feed is filtered by the same authorization rules as select.

Deploy

Ship the built SPA to the global edge from your terminal:

npm i -g zxcv zxcv link acme-store zxcv deploy --prod

Every push gets a preview URL; --prod promotes to your domain. See Deploy for custom domains and canaries.

Next steps