This package integrates [Supabase](https://supabase.io/) with [react-admin](https://marmelab.com/react-admin). It provides a dataProvider, an authProvider, specialized hooks and components to get the most out of Supabase in your react-admin application.
npm install ra-supabaseThis package integrates Supabase with react-admin. It provides a dataProvider, an authProvider, specialized hooks and components to get the most out of Supabase in your react-admin application.

Use the create-react-admin command and the supabase template to create a new React-Admin app that uses Supabase as a backend, both for the data and the authentication.
``bash`
npx create-react-admin my-admin --data-provider supabase
Edit .env and populate it with your Supabase connection variables:
`env`
VITE_SUPABASE_URL=
VITE_SUPABASE_API_KEY=
Make the data in your database readable by authenticated users by adding an RLS policy:
`sql`
create policy "Authenticated can read and write data"
on "public"."*"
as PERMISSIVE
for ALL
to authenticated
using (true);
Add a new user in your project's Users list with an email and password.
Run the development server, then go to http://localhost:5173/ in a browser.
`bash`
npm run dev
You should see a login screen: Log in using the credentials of the user you created earlier.
Now the app is ready to use.
!Demo
The generated admin is fully functional:
- All public tables are listed in the sidebar
- Lists are paginated, sortable, and filterable
- Creating, editing, and deleting records is possible
- Forms use the correct input component based on the field type
- Relationships are displayed as links in show views and as autocomplete inputs in edit views
- Authentication is handled by Supabase
You can also install ra-supabase in an existing project:
`sh`
yarn add ra-supabaseor
npm install ra-supabase
ra-supabase leverages the Supabase authentication. If you don't need to support the invitations workflow and you only enabled third party OAuth authentication, you're done with the installation.
If you do want to support the invitations workflow or use the default email/password authentication, you must do one of the following:
- Configure Supabase with a custom SMTP provider
- Set up an authentication hook to send the emails yourself
ra-supabase provides an component that takes advantage of Supabase's OpenAPI schema to guess the resources and their fields. The initial project root for a react-admin app with Supabase typically looks like this:
`jsx
// in src/App.tsx
import { AdminGuesser } from 'ra-supabase';
export const App = () => (
apiKey={YOUR_SUPABASE_API_KEY}
/>
);
`
To start customizing the app, open the browser console, and copy the guessed admin code. You can then paste it into your own app and start customizing it.
The generated code will look like this:
`jsx
import { Admin, Resource, CustomRoutes } from 'react-admin';
import { BrowserRouter, Route } from 'react-router-dom';
import { createClient } from '@supabase/supabase-js';
import {
CreateGuesser,
EditGuesser,
ForgotPasswordPage,
ListGuesser,
LoginPage,
SetPasswordPage,
ShowGuesser,
defaultI18nProvider,
supabaseDataProvider,
supabaseAuthProvider
} from 'ra-supabase';
const instanceUrl = YOUR_SUPABASE_URL;
const apiKey = YOUR_SUPABASE_API_KEY;
const supabaseClient = createClient(instanceUrl, apiKey);
const dataProvider = supabaseDataProvider({ instanceUrl, apiKey, supabaseClient });
const authProvider = supabaseAuthProvider(supabaseClient, {});
export const App = () => (
authProvider={authProvider}
i18nProvider={defaultI18nProvider}
loginPage={LoginPage}
>
);
`
Note: By default, uses a because Supabase email links require it. If you want to use a , check out the Using Hash Router section.
When specifying the source prop of filter inputs, you can either set it to the field name for simple equality checks or add an operator suffix for more control. For instance, the gte (Greater Than or Equal) or the ilike (Case insensitive like) operators:
`jsx
const postFilters = [
];
export const PostList = () => ...
;
`
These operators are available:
| Abbreviation | In PostgreSQL | Meaning |
|--------------|------------------------|-------------------------------------------------------------------------------------------------------------|
| eq | = | equals |>
| gt | | greater than |>=
| gte | | greater than or equal |<
| lt | | less than |<=
| lte | | less than or equal |<>
| neq | or != | not equal |LIKE
| like | | LIKE operator (to avoid URL encoding you can use * as an alias of the percent sign % for the pattern) |ILIKE
| ilike | | ILIKE operator (to avoid URL encoding you can use * as an alias of the percent sign % for the pattern) |~
| match | | ~ operator |~
| imatch | | ~ operator |IN
| in | | one of a list of values, e.g. ?a=in.(1,2,3) – also supports commas in quoted strings like ?a=in.("hi,there","yes,you") |IS
| is | | checking for exact equality (null, true, false, unknown) |IS DISTINCT FROM
| isdistinct | | not equal, treating NULL as a comparable value |@@
| fts | | full-text search using to_tsquery |@@
| plfts | | full-text search using plainto_tsquery |@@
| phfts | | full-text search using phraseto_tsquery |@@
| wfts | | full-text search using websearch_to_tsquery |@>
| cs | | contains e.g. ?tags=cs.{example, new} |<@
| cd | | contained in e.g. ?values=cd.{1,2,3} |&&
| ov | | overlap (have points in common), e.g. ?period=ov.[2017-01-01,2017-06-30] – also supports array types, use curly braces instead of square brackets e.g. ?arr=ov.{1,3} |<<
| sl | | strictly left of, e.g. ?range=sl.(1,10) |>>
| sr | | strictly right of |&<
| nxr | | does not extend to the right of, e.g. ?range=nxr.(1,10) |&>
| nxl | | does not extend to the left of |-|-
| adj | | is adjacent to, e.g. ?range=adj.(1,10) |NOT
| not | | negates another operator logical operators |OR
| or | | logical OR, see logical operators |AND
| and | | logical AND, see logical operators |ALL
| all | | comparison matches all the values in the list |ANY
| any | | comparison matches any value in the list |
See the PostgREST documentation for more details.
Use the meta.columns parameter to restrict the columns that you want to fetch:
`jsx`
const { data, total, isLoading, error } = useGetList(
'contact',
{
pagination: { page: 1, perPage: 10 },
sort: { field: 'created_at', order: 'DESC' },
meta: { columns: ['id', 'first_name', 'last_name'] }
}
);
You can leverage this feature to:
- rename columns: columns: ['id', 'firstName:first_name', 'lastName:last_name']columns: ['id::text', 'first_name', 'last_name']
- cast columns: columns: ['', 'company:companies()']
- embed relationships:
The last example will return all columns from the contact table and all columns from the companies table related to the contact table.
`jsx`
{
id: 123,
first_name: 'John',
last_name: 'Doe',
company_id: 456,
company: {
id: 456,
name: 'ACME'
}
}
You can leverage this feature to access related data in Field component:
`jsx`
const ContactShow = () => (
);
When ordering by a column that contains null values, you can specify whether the null values should be sorted first or last:
`jsx`
const { data, total, isLoading, error } = useGetList(
'posts',
{
pagination: { page: 1, perPage: 10 },
sort: { field: 'published_at', order: 'DESC' },
meta: { nullslast: true }
}
);
You can also set this option globally in the dataProvider:
`jsx
import { PostgRestSortOrder } from '@raphiniert/ra-data-postgrest';
const config = {
...
sortOrder: PostgRestSortOrder.AscendingNullsLastDescendingNullsLast
}
const dataProvider = supabaseDataProvider(config);
`
As users authenticate through supabase, you can leverage Row Level Security. Users identity will be propagated through the dataProvider if you provided the public API (anon) key. Keep in mind that passing the service_role key will bypass Row Level Security. This is not recommended.
ra-supabase provides alternative guessers for all CRUD pages, leveraging the OpenAPI schema provided by Supabase. Use these guessers instead of react-admin's default guessers for a better first experience.
`jsx
import { Admin, Resource } from 'react-admin';
import { ListGuesser, ShowGuesser, EditGuesser, CreateGuesser } from 'ra-supabase';
export const MyAdmin = () => (
list={ListGuesser}
show={ShowGuesser}
edit={EditGuesser}
create={CreateGuesser}
/>
);
`
By default, React Admin adds an component inside all and it will query the dataProvider with a filter on the q field. This doesn't work with Supabase. This is also true for and its child.
You must set the filterToQuery prop so that it filters the references correctly. For instance, for a list of contacts having a company that has a name prop:
`tsx
import { AutocompleteInput, Datagrid, List, ReferenceField, ReferenceInput, TextField } from 'react-admin';
const filters = [
})} />
];
export const ContactList = () => (
);
`
The guessers will try their best to infer commonly used fields for filtering. If the referenced resource contains any of these fields, it will be targeted for filtering:
- nametitle
- label
- reference
-
Supabase uses URL hash links for its redirections. This can cause conflicts if you use a HashRouter. For this reason, we recommend using the BrowserRouter.
If you want to use the HashRouter, you'll need to modify the code.
1. Create a custom auth-callback.html file inside your public folder. This file will intercept the supabase redirect and rewrite the URL to prevent conflicts with the HashRouter. For example, see packages/demo/public/auth-callback.html.BrowserRouter
2. Remove from your App.ts
3. Go to your Supabase dashboard Authentication section
4. In URL Configuration, add the following URL in the Redirect URLs section: YOUR_APPLICATION_URL/auth-callback.html"{{ .ConfirmationURL }}"
5. In Email Templates, change the to "{{ .ConfirmationURL }}auth-callback.html"
3. Go to your config.toml file[auth]
4. In section set site_url to your application URL[auth]
5. In , add the following URL in the additional_redirect_urls = [{APPLICATION_URL}}/auth-callback.html"][auth.email.template.{TYPE}]
6. Add an section with the following option :
``
[auth.email.template.TYPE]
subject = {TYPE_MESSAGE}
content_path = "./supabase/templates/{TYPE}.html"
In {TYPE}.html set the auth-callback redirection
`HTML`
{TYPE_MESSAGE}
supabaseDataProvider also accepts the same options as the ra-data-postgrest dataProvider (except apiUrl), like primaryKeys or schema.
`jsx
// in dataProvider.js
import { supabaseDataProvider } from 'ra-supabase-core';
import { supabaseClient } from './supabase';
export const dataProvider = supabaseDataProvider({
instanceUrl: 'YOUR_SUPABASE_URL',
apiKey: 'YOUR_SUPABASE_ANON_KEY',
supabaseClient,
primaryKeys: new Map([
['some_table', ['custom_id']],
['another_table', ['first_column', 'second_column']],
]),
schema: () => localStorage.getItem('schema') || 'api',
});
`
See the ra-data-postgrest` documentation for more details.
To setup only the email/password authentication, just pass the LoginPage to the loginPage prop of the component:
`jsx
import { Admin, Resource, ListGuesser } from 'react-admin';
import { LoginPage } from 'ra-supabase';
import { dataProvider } from './dataProvider';
import { authProvider } from './authProvider';
export const MyAdmin = () => (
authProvider={authProvider}
loginPage={LoginPage}
>
);
`
If you only want to support third party OAuth authentication, you can disable email & password authentication by providing a LoginPage component:
`jsx
import { Admin, Resource, ListGuesser } from 'react-admin';
import { LoginPage } from 'ra-supabase';
import { dataProvider } from './dataProvider';
import { authProvider } from './authProvider';
export const MyAdmin = () => (
authProvider={authProvider}
loginPage={
>
);
`
ra-supabase supports the invitation workflow. If a user is invited to use the application (by sending an invitation through Supabase dashboard for instance), you can configure the /set-password custom route to allow them to set their password.
This requires you to configure your supabase instance:
#### Configuring a local Supabase instance
1. Go to your config.toml file[auth]
2. In section set site_url to your application URL[auth]
3. In , add the following URL in the additional_redirect_urls = [{{APPLICATION_URL}}/auth-callback"][auth.email.template.invite]
4. Add an section with the following option
``
[auth.email.template.invite]
subject = "You have been invited"
content_path = "./supabase/templates/invite.html"
In invite.html set the auth-callback redirection
` You have been invited to create a user on {{ .SiteURL }}. Follow this link to accept the invite:HTML`
You have been invited
#### Configuring an hosted Supabase instance
1. Go to your dashboard Authentication section
1. In URL Configuration, set Site URL to your application URL
1. In URL Configuration, add the following URL in the Redirect URLs section: YOUR_APPLICATION_URL/auth-callback"{{ .ConfirmationURL }}"
1. In Email Templates, change the to "{{ .ConfirmationURL }}auth-callback"
You can now add the /set-password custom route:
`jsx
// in App.js
import { Admin, CustomRoutes, Resource, ListGuesser } from 'react-admin';
import { LoginPage, SetPasswordPage } from 'ra-supabase';
import { BrowserRouter, Route } from 'react-router-dom';
import { dataProvider } from './dataProvider';
import { authProvider } from './authProvider';
export const MyAdmin = () => (
authProvider={authProvider}
loginPage={LoginPage}
>
element={
/>
);
`
For HashRouter see Using Hash Router.
If users forgot their password, they can request for a reset if you add the /forgot-password custom route. You should also set up the /set-password custom route to allow them to choose their new password.
This requires you to configure your supabase instance:
#### Configuring a local Supabase instance
1. Go to your config.toml file[auth]
2. In section set site_url to your application URL[auth]
3. In , add the following URL in the additional_redirect_urls = [{{APPLICATION_URL}}/auth-callback"][auth.email.template.recovery]
4. Add an section with the following option
``
[auth.email.template.recovery]
subject = "Reset Password"
content_path = "./supabase/templates/recovery.html"
In recovery.html set the auth-callback redirection
`HTML`
Reset Password
#### Configuring an hosted Supabase instance
1. Go to your dashboard Authentication section
1. In URL Configuration, set Site URL to your application URL
1. In URL Configuration, add the following URL in the Redirect URLs section: YOUR_APPLICATION_URL/auth-callback"{{ .ConfirmationURL }}"
1. In Email Templates, change the to "{{ .ConfirmationURL }}/auth-callback"
You can now add the /forgot-password and /set-password custom routes:
`jsx
// in App.js
import { Admin, CustomRoutes, Resource, ListGuesser } from 'react-admin';
import { LoginPage, SetPasswordPage, ForgotPasswordPage } from 'ra-supabase';
import { BrowserRouter, Route } from 'react-router-dom';
import { dataProvider } from './dataProvider';
import { authProvider } from './authProvider';
export const MyAdmin = () => (
authProvider={authProvider}
loginPage={LoginPage}
>
element={
/>
element={
/>
);
`
For HashRouter see Using Hash Router.
To setup OAuth authentication, you can pass a LoginPage element:
`jsx
import { Admin, Resource, ListGuesser } from 'react-admin';
import { LoginPage } from 'ra-supabase';
import { dataProvider } from './dataProvider';
import { authProvider } from './authProvider';
export const MyAdmin = () => (
authProvider={authProvider}
loginPage={
// You can also disable email & password authentication
loginPage={
>
);
`
Make sure you enabled the specified providers in your Supabase instance:
- Hosted instance
- Local instance
This also requires you to configure the redirect URLS on your supabase instance:
#### Configuring a local Supabase instance
1. Go to your config.toml file[auth]
2. In section set site_url to your application URL[auth]
3. In , add the following URL in the additional_redirect_urls = [{{APPLICATION_URL}}/auth-callback"]
#### Configuring an hosted Supabase instance
1. Go to your dashboard Authentication section
1. In URL Configuration, set Site URL to your application URL
1. In URL Configuration, add the following URL in the Redirect URLs section: YOUR_APPLICATION_URL/auth-callback
To disable email/password authentication, set the disableEmailPassword prop:
`jsx
import { Admin, Resource, ListGuesser } from 'react-admin';
import { LoginPage } from 'ra-supabase';
import { dataProvider } from './dataProvider';
import { authProvider } from './authProvider';
export const MyAdmin = () => (
authProvider={authProvider}
loginPage={
}
>
);
`
For HashRouter see Using Hash Router.
We provide two language packages:
- ra-supabase-language-english
- ra-supabase-language-french
`js
// in i18nProvider.js
import { mergeTranslations } from 'ra-core';
import polyglotI18nProvider from 'ra-i18n-polyglot';
import englishMessages from 'ra-language-english';
import frenchMessages from 'ra-language-french';
import { raSupabaseEnglishMessages } from 'ra-supabase-language-english';
import { raSupabaseFrenchMessages } from 'ra-supabase-language-french';
const allEnglishMessages = mergeTranslations(
englishMessages,
raSupabaseEnglishMessages
);
const allFrenchMessages = mergeTranslations(
frenchMessages,
raSupabaseFrenchMessages
);
export const i18nProvider = polyglotI18nProvider(
locale => (locale === 'fr' ? allFrenchMessages : allEnglishMessages),
'en'
);
// in App.js
import { Admin, Resource, ListGuesser } from 'react-admin';
import { LoginPage } from 'ra-supabase';
import { dataProvider } from './dataProvider';
import { authProvider } from './authProvider';
import { i18nProvider } from './i18nProvider';
export const MyAdmin = () => (
authProvider={authProvider}
i18nProvider={i18nProvider}
loginPage={LoginPage}
>
);
``
- Add support for magic link authentication
- Add support for uploading files to Supabase storage