Learn how to integrate your Supabase database with a React application to read and write data from your frontend.
Prerequisites:
Time Required: 20-30 minutes
By the end of this guide, you’ll be able to:
Supabase provides starter code to help you connect quickly!
Go to your Supabase project dashboard and click on the “Connect” tab in the left sidebar.
What you’ll find:
In the Connect tab, select “React” or “JavaScript” as your framework.
What happens:
Note: You can choose TypeScript (.ts/.tsx) or JavaScript (.js/.jsx) - both work the same way!
Supabase provides three files to get you started. Copy these into your React project.
The three files you’ll need to modify:
.env.local - Environment variables file
.env.localsrc/utils/supabase.js - Supabase client configuration
src/utils/supabase.jssrc/App.jsx - Main React component
File Structure:
project-root/
├── .env.local # Environment variables
├── src/
│ ├── utils/
│ │ └── supabase.js # Supabase client config
│ └── App.jsx # Main component (modified)
└── package.json
💡 Tip: Create the utils folder first: mkdir src/utils
How to set up:
.env.local file in project rootsrc/utils/supabase.js with client configApp.jsx to fetch dataThe starter code uses a generic “todos” table. Change it to match your table name and columns. We’ll use “employees” as an example.
What to change:
src/App.jsx:
'employees', 'products', 'students').select() queryitem.task → item.first_name for different column namesNote: The src/utils/supabase.js file only contains the client configuration and doesn’t need table-specific changes.
Example:
Before (todos):
const { data, error } = await supabase
.from('todos')
.select('id, task, completed');
After (employees):
const { data, error } = await supabase
.from('employees')
.select('id, first_name, last_name, department');
⚠️ Common Bug Alert! There’s a known issue in the Supabase starter code with the import statement.
The Problem: The starter code uses a named import when it should use a default import, and has the wrong path.
Wrong (from starter code):
import { supabase } from '../utils/supabase'
Correct:
import supabase from './utils/supabase'
Where to fix:
src/utils/supabase.jssrc/App.jsx (or wherever you’re importing supabase){ } around supabase💡 Why? The supabase.js file uses export default, so you need a default import, not a named import.
async to Functions Using awaitThe Problem:
Functions that use await must be declared as async. The starter code is missing the async keyword.
Wrong (from starter code):
function getTodos() {
const { data: todos } = await supabase.from('todos').select()
// ... rest of function
}
Correct:
async function getEmployees() {
const { data: employees } = await supabase.from('employees').select()
// ... rest of function
}
Where to fix:
await must be asyncApp.jsx and any utility functions.env.local for Better SecurityThe Problem:
The starter code creates a .env file, but .env.local is more secure.
Recommended change:
.env to .env.localCorrect .env.local format:
VITE_SUPABASE_URL=https://your-project.supabase.co
VITE_SUPABASE_ANON_KEY=your-anon-key-here
Important:
VITE_ prefix is correct and required for Vite projects.env.local instead of .env (automatically ignored by git).env or .env.local files to version controlBefore your code will work, you need to install the Supabase JavaScript client.
In your React project terminal:
npm install @supabase/supabase-js
If using JavaScript (not TypeScript), also install:
npm install tslib
What this does:
package.json dependenciestslib is needed for JavaScript projects (not just TypeScript)Verify installation:
package.json for "@supabase/supabase-js" in dependenciesNow test that everything is working!
Steps:
npm run dev
import { useState, useEffect } from 'react';
import supabase from './utils/supabase';
function App() {
const [data, setData] = useState([]);
useEffect(() => {
async function fetchData() {
const { data, error } = await supabase
.from('employees')
.select('*');
if (error) {
console.error('Error:', error);
} else {
setData(data);
}
}
fetchData();
}, []);
return (
<div>
<h1>Employee Directory</h1>
{data.map((employee) => (
<div key={employee.id}>
<p>{employee.first_name} {employee.last_name}</p>
</div>
))}
</div>
);
}
export default App;
Debugging:
supabase.js// In your React component or utils file
async function getAllItems() {
const { data, error } = await supabase
.from('your_table_name')
.select('*'); // * means all columns
if (error) {
console.error('Error fetching data:', error);
return [];
}
return data;
}
// Get items matching criteria
async function getEmployeesByDepartment() {
const { data, error } = await supabase
.from('employees')
.select('*')
.eq('department', 'Engineering'); // Only Engineering employees
if (error) {
console.error('Error:', error);
return [];
}
return data;
}
// Create a new row
async function createEmployee(firstName, lastName, department) {
const { data, error } = await supabase
.from('employees')
.insert([
{ first_name: firstName, last_name: lastName, department: department }
])
.select();
if (error) {
console.error('Error creating employee:', error);
return null;
}
return data;
}
function EmployeeForm() {
const [employees, setEmployees] = useState([]);
async function handleSubmit(e) {
e.preventDefault();
// Get form data using event.target
const formData = new FormData(e.target);
const firstName = formData.get('firstName');
const lastName = formData.get('lastName');
const department = formData.get('department');
// Create new employee
const newEmployee = await createEmployee(firstName, lastName, department);
// Refresh the list
const { data } = await supabase
.from('employees')
.select('*');
setEmployees(data);
// Clear form
e.target.reset();
}
return (
<form onSubmit={handleSubmit}>
<input
name="firstName"
placeholder="First Name..."
/>
<input
name="lastName"
placeholder="Last Name..."
/>
<input
name="department"
placeholder="Department..."
/>
<button type="submit">Add Employee</button>
</form>
);
}
Solution:
tslib is missing from your dependenciesnpm install tslibnpm run devExample Error:
✘ [ERROR] Could not resolve "tslib"
node_modules/@supabase/storage-js/dist/module/lib/fetch.js:1:26:
1 │ import { __awaiter } from "tslib";
╵ ~~~~~~~
Solution:
npm run devExample Error:
GET http://localhost:5173/node_modules/.vite/deps/@supabase... net::ERR_ABORTED 504 (Outdated Optimize Dep)
Solution:
npm install @supabase/supabase-jsutils/supabase.js file existsSolution:
supabase.jsSolution:
Solution:
utils/supabase.jsSolution:
Supabase Client:
utils/supabase.jsAsync/Await:
async functions and await for resultsQuery Methods:
.from('table') - Select which table.select('*') - Get all columns.select('id, name') - Get specific columns.insert([{ }]) - Create new row.eq('column', value) - Filter where column equals value.order('column') - Sort resultsError Handling:
error in the responseOfficial Documentation:
Video Tutorials:
React Patterns:
Once you have data displaying:
.insert() to save data.update() to modify rows.delete() to remove rows.on('INSERT')You’ve successfully connected React to Supabase if you can:
Organization:
src/utils/supabase.js or similar fileError Handling:
Performance:
*)Security:
service_role key (only use anon key)Congratulations! You can now build full-stack React applications with a real database! 🎉
Attribution: This guide was created with assistance from Claude AI (Anthropic) to provide clear, step-by-step instructions for connecting React to Supabase.