Skip to main content

πŸš€ Getting Started with Supastash

Supastash helps you build offline-first apps by syncing local SQLite with Supabase β€” all in the background. Whether you're building a point-of-sale, chat, delivery, or CRM app, Supastash gives you control, performance, and reliability even when users are offline.

This guide walks you through setting it up from scratch.


πŸ“¦ Installation​

1. Install Supastash​

npm install supastash

2. Install Required Peer Dependencies​

These must be installed manually:

npm install @supabase/supabase-js \
@react-native-community/netinfo \
react \
react-native

3. Choose ONE SQLite Adapter​

Pick the adapter based on your project setup:

# For Expo projects
npm install expo-sqlite

# For bare React Native (better performance)
npm install react-native-nitro-sqlite

# Classic RN SQLite adapter
npm install react-native-sqlite-storage

βš™οΈ Project Setup​

1. Configure Supastash​

Set this up early β€” e.g., lib/supastash.ts

import { configureSupastash, defineLocalSchema } from "supastash";
import { supabase } from "./supabase";
import { openDatabaseAsync } from "expo-sqlite"; // or your adapter

configureSupastash({
supabaseClient: supabase,
dbName: "supastash_db",
sqliteClient: { openDatabaseAsync },
sqliteClientType: "expo", // "rn-nitro" or "rn-storage"

onSchemaInit: () => {
defineLocalSchema("users", {
id: "TEXT PRIMARY KEY",
name: "TEXT",
email: "TEXT",
created_at: "TIMESTAMP DEFAULT CURRENT_TIMESTAMP",
updated_at: "TIMESTAMP DEFAULT CURRENT_TIMESTAMP",
__indices: ["email", "user_id"],
});
},

debugMode: true,
syncEngine: {
push: true,
pull: false, // Enable if you're using RLS and filters
},
excludeTables: {
push: ["daily_reminders"],
pull: ["daily_reminders"],
},
});

2. Initialize Once in Your App​

// App.tsx or _layout.tsx
import "@/lib/supastash"; // Triggers initialization

export default function App() {
return <Stack />; // or your app shell
}

πŸ›‘οΈ Server-Side Setup (for Filtered Pulls)​

To enable safe, filtered data pulling from Supabase, run this SQL function:

create or replace function get_table_schema(table_name text)
returns table(column_name text, data_type text, is_nullable text)
security definer
as $$
select column_name, data_type, is_nullable
from information_schema.columns
where table_schema = 'public' and table_name = $1;
$$ language sql;

grant execute on function get_table_schema(text) to anon, authenticated;

βœ… Required if using Row Level Security (RLS) ⚠️ Make sure all sync-related timestamps (created_at, updated_at, deleted_at) use timestamptz β€” not plain timestamp.


βš™οΈ Wait for Initialization Before Rendering​

import { useSupatash } from "supastash";

const { dbReady } = useSupatash();
if (!dbReady) return null;

return <AppRoutes />;

πŸ“‘ Data Fetching Options​

Supastash gives you two main hooks for fetching and syncing local data:


🧠 useSupastashData – Full Sync, Realtime-Aware​

const { data, dataMap, groupedBy } = useSupastashData("orders", {
filter: { column: "user_id", operator: "eq", value: userId },
extraMapKeys: ["status"],
});
  • Syncs with Supabase in realtime
  • Uses global cache
  • Automatically keeps state updated across screens
  • Best for dashboards, shared state, live data

🧩 Dynamic Filtering (All Hooks)​

Supastash lets you filter synced data based on user, shop, etc.

const { data } = useSupastashData("orders", {
filter: {
column: "user_id",
operator: "eq",
value: currentUserId,
}, // RealtimeFilter
sqlFilter: [{ column: "user_id", operator: "eq", value: currentUserId }], //sql (optional)
});

πŸ’‘ filter = for Supabase realtime πŸ’‘ sqlFilter = for actual query filtering


πŸ›‘οΈ Registering Table Filters: useSupastashFilters​

If you’re pulling data (i.e., using pull: true in configureSupastash), always call this hook at startup:

useSupastashFilters({
orders: [{ column: "shop_id", operator: "eq", value: activeShopId }],
inventory: [
{ column: "location_id", operator: "eq", value: selectedLocation },
],
});
  • Ensures only scoped rows are pulled from Supabase
  • Prevents unnecessary or insecure full-table syncs
  • Validates your filters and warns you if anything’s wrong

πŸ” One-Off Queries with Supabase​

Use Supastash's built-in wrapper for direct Supabase access:

import { supastash } from "supastash";

const { data, error } = await supastash.from("orders").select("*").run();

It works just like supabase.from(...), but ensures it respects your Supastash config.


πŸ”§ Debugging​

Enable debugMode: true to log sync events, retries, or failures:

configureSupastash({
...,
debugMode: true,
});

βœ… Next Steps​


That’s it β€” you're now ready to build offline-first, scalable, and Supabase-powered apps using Supastash. Whether you're going full realtime or keeping it lite, you’re in control.