# React Supabase CRUD

## Supabase Database Basics

### What is Supabase?

<https://supabase.io>

An open source alternative to Google Firebased

* PostgreSQL database with real-time capabilities
* Authentication with multiple methods/services
* File storage
* Serverless Functions&#x20;

### PostgreSQL?

Built on top of Postgres, a very scalable relational database. You can manage your database from the Supabase interface.

* Create tables, relationships, etc
* Write SQL quries
* Enable & disable extensions
* Real-time engine on top of Postgres
* PostgREST API

### User Authentication

Create and manage users from Supabase Through the Providesrs

* Email/Password
* \<agic Link
* Google
* GitHub
* Facebok
* More...

### Access Control

Security and access are managed via Postgres RLS (Row Level Security)

How It Works

1. A user signs up. Supabase creates a new user in the auth.users table
2. Supabase returns a new JWT(Jason Web Token), which contains the user's UUID
3. Every request to your database also sends the JWT
4. Postgres inspects the JWT to determine the user making the request
5. The user's UID can be used in policies to restrict access to rows

Client Libraries

The supabase client makes it easy to access data, authenticate, use storage, etc

### Prerequisites

* Node.js installed
* A Supabase account
* Basic knowledge of React

### Step 1: Initialize the Project

#### 1.1 Create a Vite Project

Run the following command to create a new Vite project:

```bash
npm create vite@latest supabase-todo --template react
```

or

```bash
// Some code
npx create-vite
>Project name: supabase-todo (if you are alredy inside vs code terminal, then just use .)
>Current directory is not empty > Ignore files and continue
>Select  a framework: React
>Variants: JavaScript
```

Navigate into the project directory:

```
cd supabase-todo
```

Install dependencies:

```
npm install
```

#### 1.2 Install Required Packages

```bash
npm install @supabase/supabase-js 
npm install @supabase/auth-ui-react @supabase/auth-ui-share
npm install react-router-dom tailwindcss
```

#### 1.3 Configure Tailwind CSS

First, install Tailwind CSS using npm. Open your terminal and run the following commands in your project directory:

Initialize Tailwind CSS: npx tailwindcss init creates a default tailwind. config. js file in your project

```bash
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
```

Modify `tailwind.config.js` to include: Set Up Content Paths in Configuration

Tailwind CSS needs to know where your templates are located to scan them for class names. Edit the tailwind.config.js file to include the paths to all of your HTML and JavaScript files

```javascript
// tailwind.config.js
/** @type {import('tailwindcss').Config} */
module.exports = {
  content: ["./index.html", "./src/**/*.{js,jsx,ts,tsx}"],
  theme: {
    extend: {},
  },
  plugins: [],
};
```

Add Tailwind to `src/index.css`:

```css
@tailwind base;
@tailwind components;
@tailwind utilities;
```

***

#### Compile Your Tailwind CSS (optional) <a href="#step-4-compile-your-tailwind-css" id="step-4-compile-your-tailwind-css"></a>

Now, use the Tailwind CLI to transform the directives into concrete styles based on your configuration and the classes used in your templates.

Execute this command to compile your CSS and watch for any changes:

```bash
// Some code
npx tailwindcss -i ./src/index.css -o ./src/output.css --watch
```

#### Reference the Compiled CSS in Your HTML <a href="#step-5-reference-the-compiled-css-in-your-html" id="step-5-reference-the-compiled-css-in-your-html"></a>

The final step is to link the generated CSS file in your HTML documents. This enables you to start using Tailwind’s utility classes to style your application.

Link to the compiled CSS in your main HTML file (e.g., index.html):

```html
// index.html
...
<head>
   <link rel="stylesheet" href="./src/output.css">
</head>
...
```

### Step 2: Set Up Supabase

#### 2.1 Create a Supabase Project

* Go to [Supabase](https://supabase.io/)
* Sign in and create a new project : Fill in Organization name, project name, generate Database password (Keep it securely), choose region
* Copy the **Project URL** and **anon key** from the Supabase dashboard

#### 2.2 Configure Supabase in React

Create a file `src/supabaseClient.js`:

```jsx
import { createClient } from "@supabase/supabase-js";

const supabaseUrl = "YOUR_SUPABASE_URL";
const supabaseKey = "YOUR_SUPABASE_ANON_KEY";
export const supabase = createClient(supabaseUrl, supabaseKey);
```

* ```
  Go to Supabase site > Dashboard>Home
  Get the following API endpoint from Supabse Dashboard> Project
  YOUR_SUPABASE_URL
  YOUR_SUPABASE_ANON_KEY
  ```
* Use prefix : VITE\_SUPABASE\_URL, VITE\_SUPABASE\_ANON\_KEY

Create <mark style="color:orange;">.env</mark> (in root project folder)

```
// .env file
VITE_SUPABASE_ANON_KEY = 
VITE_SUPABSE_URL = 
```

```jsx
// updated supabaseClient.js
import { createClient } from "@supabase/supabase-js";

// Initialize Supabase client with your credentials
const supabaseUrl = import.meta.env.VITE_SUPABASE_URL;
const supabaseKey = import.meta.env.VITE_ANON_KEY;
const supabase = createClient(supabaseUrl, supabaseKey);

export default supabase
```

***

### Step 3: Create the Database Table

Navigate to the <mark style="color:orange;">**Database**</mark> section in Supabase and create a new table <mark style="color:orange;">**ToDoList**</mark> with the following columns at Table Editor

* `id`: UUID (Primary Key, default `gen_random_uuid()`)
* `name`: Text
* `isCompleted`: Boolean (default `false`)
* `user_id`: UUID (Foreign Key to `auth.users.id`)

Enable Row-Level Security (RLS) and add the following policies:

* **Insert Policy**: Allow authenticated users to insert their own todos.
* **Select Policy**: Allow authenticated users to view only their own todos.
* **Update Policy**: Allow authenticated users to update their own todos.
* **Delete Policy**: Allow authenticated users to delete their own todos.

***

### Step 4: Implement Authentication in Google Cloud Console

#### 4.1 Create Login Component  with SignInGoogle Method

Setup Google Cloud Project&#x20;

* open [https://console.cloud.google.com ](https://console.cloud.google.com/)
* Google Cloud > APIs & Services > Credential
* Create a new project
* Google Cloud > APIs & Services > OAuth consent screen
  * User Type : Internal
  * Create - Provide App Information, user support email, Developer contact information
  * Save & Continue
* Back to Dashboard : APIs & Services > Credential > Create Credentials - select OAuth client ID

  * Application type: Web application
  * Name: Web client 1 (default)
  * Authorized redirect URIs : URIs 1 - copy and paste here from supabase >Authendication>Providers > Google and then Create
  * In popup modal, copy <mark style="color:orange;">Client ID</mark>, <mark style="color:orange;">Client secret</mark>
  * enter the callback URL from the [Supabase dashboard](https://supabase.com/dashboard/project/_/auth/providers) > Project URL

In Supabase Dashboard, go to Authentication > Providers

* Click Google icon to expand : Enable Sign in with Google and provide the following info.
  * Client ID (for OAuth) :: from Google APIs & Services > Credential
  * Client Secret (for OAuth) ::
  * Save&#x20;

<figure><img src="/files/nTpyG2kFdde7R4VpfI24" alt=""><figcaption></figcaption></figure>

Create `src/components/Login.jsx`:

```jsx
//Login.jsx component
import { supabase } from "../supabaseClient.js";

const Login = () => {
  const signInWithGoogle = async () => {
    const { user, error } = await supabase.auth.signInWithOAuth({ provider: "google" });
    if (error) console.error("Error logging in", error);
  };

  return (
    <button className="bg-blue-500 text-white p-2 rounded" onClick={signInWithGoogle}>
      Sign in with Google
    </button>
  );
};

export default Login;
```

Create `src/components/Logout.jsx`:

```jsx
// Logout.jsx component
import {supabase} from '../supabaseClient'

const Logout = () => {
  const signOut = async () => {
    const { error } = await supabase.auth.signOut()

    if (error) console.error('Error logging out', error)
  }

  return (
    <button
      className="bg-red-500 text-white p-2 rounded"
      onClick={signOut}>
      Sign out with Google
    </button>
  )
}

export default Logout

```

#### 4.2 Handle Authentication in App

Modify `src/App.js`:

<pre class="language-jsx"><code class="lang-jsx"><strong>import { useState, useEffect } from "react";
</strong>import { supabase } from "./supabaseClient";
import Login from "./components/Login";
import TodoList from "./components/TodoList";

function App() {
  const [session, setSession] = useState(null);

  useEffect(() => {
    supabase.auth.getSession().then(({ data }) => {
      setSession(data.session);
    });
    supabase.auth.onAuthStateChange((_event, session) => {
      setSession(session);
    });
  }, []);

  return &#x3C;div>{session ? &#x3C;TodoList /> : &#x3C;Login />}&#x3C;/div>;
}

export default App;
</code></pre>

***

### Step 5: Implement CRUD Operations

#### 5.1 Create Todo Component

Create `src/components/TodoList.js`:

```jsx
import { useState, useEffect } from "react";
import { supabase } from "../supabaseClient";

const TodoList = () => {
  const [todos, setTodos] = useState([]);
  const [newTodo, setNewTodo] = useState("");

  useEffect(() => {
    fetchTodos();
  }, []);

  const fetchTodos = async () => {
    const { data , error} = await supabase.from("ToDoList").select("*");
    if (error) {
      console.log("Error fetching: ", error)
    } else {
      setTodos(data);
    }
  };

  const addTodo = async () => {
    const newTodoData = {
      name:newTodo,
      isCompleted: false,
      };
    const {data, error} = await supabase.from("ToDoList")
                                        .insert([newTodoData])
                                        .single();
    if(error) {
       console.log("Error adding newToDo: ", error);
     } else {
        fetchTodos();
        setNewTodo("");
    }
  };

  const toggleCompletion = async (id, isCompleted) => {
    await supabase.from("ToDoList").update({ isCompleted: !isCmpleted }).eq("id", id);
    fetchTodos();
  };

  const deleteTodo = async (id) => {
    await supabase.from("ToDoList").delete().eq("id", id);
    fetchTodos();
  };

  return (
    <div>
      <input type="text" value={newTodo} onChange={(e) => setNewTodo(e.target.value)} className="border p-2" />
      <button onClick={addTodo} className="bg-green-500 text-white p-2 ml-2">Add Todo Item</button>
      <ul>
        {todos.map((todo) => (
          <li key={todo.id} className="flex justify-between p-2">
            <span className={todo.completed ? "line-through" : ""}>{todo.name}</span>
            <div>
              <button onClick={() => toggleCompletion(todo.id, todo.completed)} className="bg-yellow-500 text-white p-1">Complete Task</button>
              <button onClick={() => deleteTodo(todo.id)} className="bg-red-500 text-white p-1 ml-2">Delete Task</button>
            </div>
          </li>
        ))}
      </ul>
    </div>
  );
};

export default TodoList;
```

This concludes our guide on setting up a **Supabase CRUD Todo List** application using React and Vite with Tailwind CSS. 🎉

### Result


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://reactjs.koida.tech/database-in-application/react-supabase-crud.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
