PicoBase SDK Integration Guide
A step-by-step guide for adding PicoBase to your project. No backend experience required.
What You Get
PicoBase gives your app a complete backend with four building blocks:
| Building block | What it does |
|---|---|
| Auth | Sign up, sign in, sign out, password reset, Google/GitHub login |
| Database | Store, read, update, and delete data (called "collections") |
| Storage | Upload and serve files (images, PDFs, etc.) |
| Realtime | Get instant updates when data changes (no refreshing) |
Before You Start
You need two things from the PicoBase dashboard:
- Your instance URL — looks like
https://myapp.picobase.com - Your API key — starts with
pbk_(e.g.pbk_abc12345_xxxxxxxxxxxxxxxx)
To get these:
1. Sign in at the PicoBase dashboard
2. Click Create Instance and give it a name
3. Once created, click into your instance
4. Click Create API Key and copy it somewhere safe — it is only shown once
You also need Node.js installed (version 18 or newer).
Step 1 — Install the SDK
Open your terminal, navigate to your project folder, and run:
npm install @picobase_app/clientIf you're using React, also install the React helpers:
npm install @picobase_app/client @picobase_app/reactStep 2 — Create Your Client
The "client" is the object you use to talk to PicoBase. You create it once and use it everywhere.
For any JavaScript/TypeScript project
Create a file called picobase.ts (or picobase.js) wherever you keep your utility files:
import { createClient } from '@picobase_app/client'
const pb = createClient(
'https://myapp.picobase.com', // <-- replace with your URL
'pbk_abc12345_xxxxxxxxxxxxxxxx' // <-- replace with your API key
)
export default pbFor React projects (recommended)
Instead of importing the client everywhere, wrap your app with the PicoBaseProvider. This gives every component access to PicoBase through hooks.
import { PicoBaseProvider } from '@picobase_app/react'
function App() {
return (
<PicoBaseProvider
url="https://myapp.picobase.com"
apiKey="pbk_abc12345_xxxxxxxxxxxxxxxx"
>
{/* Everything inside here can use PicoBase hooks */}
<YourApp />
</PicoBaseProvider>
)
}Keep your API key out of your code
Optional but recommended
Store your URL and key in a .env file so they don't end up in git:
VITE_PICOBASE_URL=https://myapp.picobase.com
VITE_PICOBASE_API_KEY=pbk_abc12345_xxxxxxxxxxxxxxxxThen reference them in your code:
// Vite projects
const pb = createClient(
import.meta.env.VITE_PICOBASE_URL,
import.meta.env.VITE_PICOBASE_API_KEY
)
// Next.js projects
const pb = createClient(
process.env.NEXT_PUBLIC_PICOBASE_URL!,
process.env.NEXT_PUBLIC_PICOBASE_API_KEY!
)Next.js TIP: Name the variables with a NEXT_PUBLIC_ prefix to make them available in the browser.
Step 3 — Add User Authentication
Option A: Drop-in Auth Form (fastest)
Use the AuthForm component for a full login/signup form with zero config.
import { AuthForm } from '@picobase_app/react'
function LoginPage() {
return (
<AuthForm
providers={['google', 'github']}
onSuccess={(user) => {
console.log('Logged in as', user.email)
window.location.href = '/dashboard'
}}
/>
)
}Option B: Build Your Own Login Form
Use the useAuth hook for full control.
Sign-up form
import { useState } from 'react'
import { useAuth } from '@picobase_app/react'
function SignUpForm() {
const { signUp } = useAuth()
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
async function handleSubmit(e) {
e.preventDefault()
await signUp(email, password)
// Redirect or update UI
}
return (
<form onSubmit={handleSubmit}>
<input type="email" value={email} onChange={e => setEmail(e.target.value)} required />
<input type="password" value={password} onChange={e => setPassword(e.target.value)} required />
<button type="submit">Sign Up</button>
</form>
)
}Step 4 — Protect Pages
Show different content depending on whether the user is logged in.
import { useAuth } from '@picobase_app/react'
function App() {
const { user, loading } = useAuth()
// Still checking if user is logged in
if (loading) return <p>Loading...</p>
// Not logged in
if (!user) return <LoginPage />
// Logged in
return (
<div>
<p>Welcome, {user.email}!</p>
<Dashboard />
</div>
)
}Step 5 — Read and Write Data
Collections are like database tables. Create them in the dashboard, then use them in code.
React shortcut: useCollection hook
Fetches data and handles loading/error states automatically.
import { useCollection } from '@picobase_app/react'
function PostList() {
const { items, loading, error } = useCollection('posts', {
sort: '-created',
filter: 'published = true',
})
if (loading) return <p>Loading posts...</p>
if (error) return <p>Error: {error.message}</p>
return (
<ul>
{items.map(post => (
<li key={post.id}>{post.title}</li>
))}
</ul>
)
}CRUD Operations (Vanilla JS / Handlers)
// Create
const newPost = await pb.collection('posts').create({
title: 'My First Post',
published: true,
})
// Read (List)
const result = await pb.collection('posts').getList(1, 20, {
sort: '-created',
})
// Update
await pb.collection('posts').update('RECORD_ID', {
title: 'Updated Title',
})
// Delete
await pb.collection('posts').delete('RECORD_ID')Step 6 — Upload Files
Files are attached to records. Add a file field to your collection in the dashboard first.
const formData = new FormData()
formData.append('title', 'My photo')
formData.append('image', fileInput.files[0])
const record = await pb.collection('posts').create(formData)Get the file URL
const url = pb.storage.getFileUrl(record, 'photo.jpg')
// With thumbnail transform
const thumbUrl = pb.storage.getFileUrl(record, 'photo.jpg', {
thumb: '100x100',
})Step 7 — Listen for Live Updates
Realtime subscriptions let your app update instantly when data changes.
// Watch for any changes
const unsubscribe = await pb.collection('messages').subscribe((event) => {
console.log(event.action) // 'create', 'update', or 'delete'
console.log(event.record) // the data that changed
})
// Stop listening
await unsubscribe()Full Working Example
A complete React app with auth and data — ready to copy.
import { PicoBaseProvider, useAuth, useCollection, AuthForm } from '@picobase_app/react'
const URL = import.meta.env.VITE_PICOBASE_URL
const KEY = import.meta.env.VITE_PICOBASE_API_KEY
export default function App() {
return (
<PicoBaseProvider url={URL} apiKey={KEY}>
<Main />
</PicoBaseProvider>
)
}
function Main() {
const { user, loading, signOut } = useAuth()
if (loading) return <p>Loading...</p>
if (!user) {
return (
<div style={{ maxWidth: 400, margin: '100px auto' }}>
<AuthForm
providers={['google']}
onSuccess={() => window.location.reload()}
/>
</div>
)
}
return (
<div style={{ maxWidth: 600, margin: '40px auto' }}>
<h1>Welcome, {user.email}</h1>
<button onClick={signOut}>Sign Out</button>
<hr />
<PostsList />
</div>
)
}
function PostsList() {
const { items, loading, error } = useCollection('posts', {
sort: '-created',
filter: 'published = true',
})
if (loading) return <p>Loading posts...</p>
if (items.length === 0) return <p>No posts yet.</p>
return (
<ul>
{items.map(post => (
<li key={post.id}>
<strong>{post.title}</strong>
<p>{post.content}</p>
</li>
))}
</ul>
)
}Quick Reference
import { createClient } from '@picobase_app/client'
const pb = createClient(url, apiKey)
// Auth
await pb.auth.signUp({ email, password })
await pb.auth.signIn({ email, password })
await pb.auth.signInWithOAuth({ provider: 'google' })
await pb.auth.requestPasswordReset(email)
pb.auth.signOut()
pb.auth.user // current user or null
pb.auth.isValid // true if logged in
pb.auth.onStateChange((event, record) => { ... })
// Database
await pb.collection('x').getList(page, perPage, { filter, sort, expand })
await pb.collection('x').getOne(id)
await pb.collection('x').getFirstListItem(filter)
await pb.collection('x').getFullList({ filter, sort })
await pb.collection('x').create(data)
await pb.collection('x').update(id, data)
await pb.collection('x').delete(id)
// Realtime
const unsub = await pb.collection('x').subscribe(callback)
await unsub()
// Storage
pb.storage.getFileUrl(record, filename)
pb.storage.getFileUrl(record, filename, { thumb: '100x100' })Common Mistakes
"usePicoBase* hooks must be used within a PicoBaseProvider"
Make sure <PicoBaseProvider> wraps your entire app or at least the part that uses the hooks.
"Instance unavailable after 3 retries"
Your PicoBase instance might be stopped. Check the dashboard. The SDK retries automatically, but if it wakes up too slowly, you might see this error.
Forgetting "await"
Most SDK calls are asynchronous. If you forget await, you get a Promise instead of data.
Next Steps
- Create collections in the dashboard to define your data structure
- Set up collection rules to control who can read/write data
- Enable OAuth providers in your instance's auth settings
- Real the full API reference in the @picobase_app/client README