diff --git a/apps/docs/components/ContentListings/iconChip.tsx b/apps/docs/components/ContentListings/iconChip.tsx index 8fe440b18bf..e3af1bed8e9 100644 --- a/apps/docs/components/ContentListings/iconChip.tsx +++ b/apps/docs/components/ContentListings/iconChip.tsx @@ -1,11 +1,10 @@ 'use client' +import type { ContentListingIcon } from '~/lib/content-listings.schema' import { Axiom, Datadog, Grafana, Last9, Otlp, Sentry } from 'icons' import { Braces, Cloud, Server } from 'lucide-react' import type { ReactNode } from 'react' -import type { ContentListingIcon } from '~/lib/content-listings.schema' - type IconKind = Extract['kind'] const ICON_KIND_COMPONENTS: Record = { @@ -33,5 +32,15 @@ export function resolveContentListingIcon( ) } + // GlassPanel picks -light via next-themes; class-based dark: is more reliable for + // Reflex (near-black light mark vanishes on dark cards if theme resolution lags). + if (typeof icon === 'string' && icon.endsWith('/reflex-icon')) { + return ( + <> + + + + ) + } return icon } diff --git a/apps/docs/components/FrameworkQuickstarts.tsx b/apps/docs/components/FrameworkQuickstarts.tsx index 398b099f5de..66760243aab 100644 --- a/apps/docs/components/FrameworkQuickstarts.tsx +++ b/apps/docs/components/FrameworkQuickstarts.tsx @@ -96,6 +96,12 @@ const frameworks = [ icon: '/docs/img/icons/python-icon', href: '/guides/getting-started/quickstarts/flask', }, + { + name: 'Reflex', + icon: '/docs/img/icons/reflex-icon', + href: '/guides/getting-started/quickstarts/reflex', + hasLightIcon: true, + }, { name: 'Laravel', icon: '/docs/img/icons/laravel-icon', @@ -106,6 +112,11 @@ const frameworks = [ icon: '/docs/img/icons/rails-icon', href: '/guides/getting-started/quickstarts/ruby-on-rails', }, + { + name: 'Spring Boot', + icon: '/docs/img/icons/spring-boot-icon', + href: '/guides/getting-started/quickstarts/spring-boot', + }, ] export function FrameworkQuickstarts({ labelledBy }: { labelledBy?: string }) { diff --git a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts index 6a97bf6aa02..613c942a50f 100644 --- a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts +++ b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts @@ -366,38 +366,50 @@ export const gettingstarted: NavMenuConstant = { name: 'Framework Quickstarts', enabled: frameworkQuickstartsEnabled, items: [ + { + name: 'React', + url: '/guides/getting-started/quickstarts/reactjs', + }, { name: 'Next.js', url: '/guides/getting-started/quickstarts/nextjs', }, { - name: 'React', - url: '/guides/getting-started/quickstarts/reactjs', + name: 'TanStack Start', + url: '/guides/getting-started/quickstarts/tanstack' as `/${string}`, }, { name: 'Astro', url: '/guides/getting-started/quickstarts/astrojs', }, - { - name: 'Nuxt', - url: '/guides/getting-started/quickstarts/nuxtjs', - }, { name: 'Vue', url: '/guides/getting-started/quickstarts/vue', }, + { + name: 'Nuxt', + url: '/guides/getting-started/quickstarts/nuxtjs', + }, + { + name: 'SvelteKit', + url: '/guides/getting-started/quickstarts/sveltekit' as `/${string}`, + }, + { + name: 'SolidJS', + url: '/guides/getting-started/quickstarts/solidjs', + }, + { + name: 'RedwoodJS', + url: '/guides/getting-started/quickstarts/redwoodjs' as `/${string}`, + }, + { + name: 'Refine', + url: '/guides/getting-started/quickstarts/refine', + }, { name: 'Hono', url: '/guides/getting-started/quickstarts/hono', }, - { - name: 'Expo React Native', - url: '/guides/getting-started/quickstarts/expo-react-native', - }, - { - name: 'Flutter', - url: '/guides/getting-started/quickstarts/flutter', - }, { name: 'iOS SwiftUI', url: '/guides/getting-started/quickstarts/ios-swiftui', @@ -407,8 +419,12 @@ export const gettingstarted: NavMenuConstant = { url: '/guides/getting-started/quickstarts/kotlin' as `/${string}`, }, { - name: 'SvelteKit', - url: '/guides/getting-started/quickstarts/sveltekit' as `/${string}`, + name: 'Expo React Native', + url: '/guides/getting-started/quickstarts/expo-react-native', + }, + { + name: 'Flutter', + url: '/guides/getting-started/quickstarts/flutter', }, { name: 'Flask (Python)', @@ -416,8 +432,9 @@ export const gettingstarted: NavMenuConstant = { enabled: !jsOnly, }, { - name: 'TanStack Start', - url: '/guides/getting-started/quickstarts/tanstack' as `/${string}`, + name: 'Reflex (Python)', + url: '/guides/getting-started/quickstarts/reflex' as `/${string}`, + enabled: !jsOnly, }, { name: 'Laravel PHP', @@ -434,18 +451,6 @@ export const gettingstarted: NavMenuConstant = { url: '/guides/getting-started/quickstarts/spring-boot' as `/${string}`, enabled: !jsOnly, }, - { - name: 'SolidJS', - url: '/guides/getting-started/quickstarts/solidjs', - }, - { - name: 'RedwoodJS', - url: '/guides/getting-started/quickstarts/redwoodjs' as `/${string}`, - }, - { - name: 'Refine', - url: '/guides/getting-started/quickstarts/refine', - }, ], }, { diff --git a/apps/docs/content/guides/getting-started/quickstarts/reflex.mdx b/apps/docs/content/guides/getting-started/quickstarts/reflex.mdx new file mode 100644 index 00000000000..efbf157d086 --- /dev/null +++ b/apps/docs/content/guides/getting-started/quickstarts/reflex.mdx @@ -0,0 +1,130 @@ +--- +title: 'Use Supabase with Reflex' +subtitle: 'Learn how to create a Supabase project, add some sample data to your database, and query the data from a Reflex app.' +breadcrumb: 'Framework Quickstarts' +--- + + + +<$Partial path="quickstart_db_setup.mdx" /> + +## 3. Create a Reflex app + +Create a new directory for your Reflex app, initialize a project with `uv`, add Reflex as a dependency, and scaffold a blank app with `--template blank`. + +```bash +mkdir my_app && cd my_app +uv init +uv add reflex +uv run reflex init --template blank +``` + +## 4. Set up AI tooling (optional) + +<$Partial path="quickstart_ai_tooling.mdx" /> + +## 5. Install the Supabase client library + +The fastest way to get started is to use the `supabase-py` client library, which provides a convenient interface for working with Supabase from a Reflex app. + +Install `supabase-py` and `python-dotenv` to load environment variables. + +```bash +uv add supabase python-dotenv +``` + +## 6. Declare Supabase environment variables + +Create a `.env` file in your project root and populate it with your Supabase connection variables that you can get from the helper below, or [from the project **Connect** panel](/dashboard/project/_?showConnect=true&connectTab=frameworks): + + + +```text name=.env +SUPABASE_URL= +SUPABASE_PUBLISHABLE_KEY= +``` + +<$Partial path="api_settings.mdx" variables={{ "framework": "", "tab": "" }} /> + +## 7. Create the Supabase client + +Open `my_app/my_app.py` and add the following code to create an async Supabase client from your environment variables. Reflex's event handlers run inside an async event loop, so the client is created once with `acreate_client` and reused across requests, rather than recreated per page load. + +```python name=my_app/my_app.py +import os +import reflex as rx +from supabase import acreate_client, AsyncClient +from dotenv import load_dotenv + +load_dotenv() + +_supabase: AsyncClient | None = None + + +async def get_supabase() -> AsyncClient: + global _supabase + if _supabase is None: + _supabase = await acreate_client( + os.environ.get("SUPABASE_URL"), + os.environ.get("SUPABASE_PUBLISHABLE_KEY"), + ) + return _supabase +``` + +## 8. Query data from the app + +Add a `State` class that holds the query result, an async event handler that fetches data from your `instruments` table, and a page that renders the result. `on_load` triggers the handler when the page is opened. + +```python name=my_app/my_app.py +from postgrest import APIError + + +class State(rx.State): + instruments: list[dict] = [] + error: str = "" + + async def load_instruments(self): + supabase = await get_supabase() + try: + response = await supabase.table("instruments").select("*").execute() + except APIError as error: + self.error = f"Error loading instruments: {error.message}" + return + self.instruments = response.data + + +def index() -> rx.Component: + return rx.container( + rx.heading("Instruments"), + rx.cond( + State.error, + rx.text(State.error), + rx.foreach( + State.instruments, + lambda instrument: rx.text(instrument["name"]), + ), + ), + ) + + +app = rx.App() +app.add_page(index, on_load=State.load_instruments) +``` + +## 9. Start the app + +Run the Reflex development server, go to http://localhost:3000 in a browser, and you should see the list of instruments. + +```bash +uv run reflex run +``` + +<$Partial path="quickstart_going_to_production.mdx" /> + +## Next steps + +- Set up [Auth](/docs/guides/auth) for your app +- [Insert more data](/docs/guides/database/import-data) into your database +- Upload and serve static files using [Storage](/docs/guides/storage) diff --git a/apps/docs/data/ai-prompts.data.ts b/apps/docs/data/ai-prompts.data.ts index 66081d5bcb5..6700d988420 100644 --- a/apps/docs/data/ai-prompts.data.ts +++ b/apps/docs/data/ai-prompts.data.ts @@ -179,6 +179,20 @@ database.new and run the instruments table SQL. Then: REFERENCE https://supabase.com/docs/guides/getting-started/quickstarts/refine.md`, + reflex: `Help me add Supabase to my Reflex project. Create a Supabase project at +database.new and run the instruments table SQL. Then: +1. Run \`uv init\` and \`uv add reflex\`, then \`uv run reflex init --template blank\` + to scaffold the app. +2. Run \`uv add supabase python-dotenv\`. +3. Create \`.env\` and set \`SUPABASE_URL\` and \`SUPABASE_PUBLISHABLE_KEY\`. +4. In \`my_app/my_app.py\`, create a single async Supabase client with + \`acreate_client\` (one client per process, not recreated per request) and an + \`rx.State\` event handler that queries and renders the instruments table, + handling \`postgrest.APIError\`. +5. Run \`uv run reflex run\` and open http://localhost:3000. + +REFERENCE +https://supabase.com/docs/guides/getting-started/quickstarts/reflex.md`, 'ruby-on-rails': `Help me add Supabase to my Ruby on Rails project. Create a Supabase project at database.new. Then: 1. Run \`rails new blog -d=postgresql\` to scaffold a new Rails project. diff --git a/apps/docs/data/content-listings/getting-started.data.ts b/apps/docs/data/content-listings/getting-started.data.ts index 07e7320a1df..a450b2186b7 100644 --- a/apps/docs/data/content-listings/getting-started.data.ts +++ b/apps/docs/data/content-listings/getting-started.data.ts @@ -76,12 +76,11 @@ export const gettingStartedFrameworkQuickstarts: ContentListingGroup = { 'Full-stack React with server rendering, wired to Supabase Postgres and cookie-based auth.', }, { - title: 'Nuxt', - href: '/guides/getting-started/quickstarts/nuxtjs', - icon: '/docs/img/icons/nuxt-icon', - hasLightIcon: false, - description: - 'Full-stack Vue with server rendering, reading Postgres through a Supabase composable.', + title: 'TanStack Start', + href: '/guides/getting-started/quickstarts/tanstack', + icon: '/docs/img/icons/tanstack-icon', + hasLightIcon: true, + description: 'Type-safe full-stack React that queries Supabase Postgres in server functions.', }, { title: 'Astro', @@ -92,12 +91,35 @@ export const gettingStartedFrameworkQuickstarts: ContentListingGroup = { 'Content-driven sites that render on the server and pull Supabase Postgres data per request.', }, { - title: 'Hono', - href: '/guides/getting-started/quickstarts/hono', - icon: '/docs/img/icons/hono-icon', + title: 'Vue', + href: '/guides/getting-started/quickstarts/vue', + icon: '/docs/img/icons/vuejs-icon', hasLightIcon: false, description: - 'Lightweight web APIs with Supabase Auth anonymous sign-in and RLS-protected reads.', + 'Build single-page apps with the Vue composition API, backed by Supabase Postgres.', + }, + { + title: 'Nuxt', + href: '/guides/getting-started/quickstarts/nuxtjs', + icon: '/docs/img/icons/nuxt-icon', + hasLightIcon: false, + description: + 'Full-stack Vue with server rendering, reading Postgres through a Supabase composable.', + }, + { + title: 'SvelteKit', + href: '/guides/getting-started/quickstarts/sveltekit', + icon: '/docs/img/icons/svelte-icon', + hasLightIcon: false, + description: 'Full-stack Svelte that loads Supabase Postgres data in server load functions.', + }, + { + title: 'SolidJS', + href: '/guides/getting-started/quickstarts/solidjs', + icon: '/docs/img/icons/solidjs-icon', + hasLightIcon: false, + description: + 'Fine-grained reactive UIs that load Supabase Postgres data with Solid resources.', }, { title: 'RedwoodJS', @@ -108,20 +130,20 @@ export const gettingStartedFrameworkQuickstarts: ContentListingGroup = { 'Full-stack React and GraphQL, with Prisma migrations against your Supabase Postgres database.', }, { - title: 'Expo React Native', - href: '/guides/getting-started/quickstarts/expo-react-native', - icon: '/docs/img/icons/expo-icon', - hasLightIcon: true, + title: 'Refine', + href: '/guides/getting-started/quickstarts/refine', + icon: '/docs/img/icons/refine-icon', + hasLightIcon: false, description: - 'Ship iOS and Android from one React Native codebase, backed by Supabase Postgres.', + 'Scaffold CRUD dashboards and admin panels straight from your Supabase Postgres tables.', }, { - title: 'Flutter', - href: '/guides/getting-started/quickstarts/flutter', - icon: '/docs/img/icons/flutter-icon', + title: 'Hono', + href: '/guides/getting-started/quickstarts/hono', + icon: '/docs/img/icons/hono-icon', hasLightIcon: false, - feature: 'sdk:dart', - description: 'Ship iOS and Android from one Dart codebase, backed by Supabase Postgres.', + description: + 'Lightweight web APIs with Supabase Auth anonymous sign-in and RLS-protected reads.', }, { title: 'iOS SwiftUI', @@ -141,42 +163,20 @@ export const gettingStartedFrameworkQuickstarts: ContentListingGroup = { 'Native Android apps in Kotlin and Jetpack Compose, using the Supabase Kotlin SDK.', }, { - title: 'SvelteKit', - href: '/guides/getting-started/quickstarts/sveltekit', - icon: '/docs/img/icons/svelte-icon', - hasLightIcon: false, - description: 'Full-stack Svelte that loads Supabase Postgres data in server load functions.', - }, - { - title: 'SolidJS', - href: '/guides/getting-started/quickstarts/solidjs', - icon: '/docs/img/icons/solidjs-icon', - hasLightIcon: false, - description: - 'Fine-grained reactive UIs that load Supabase Postgres data with Solid resources.', - }, - { - title: 'Vue', - href: '/guides/getting-started/quickstarts/vue', - icon: '/docs/img/icons/vuejs-icon', - hasLightIcon: false, - description: - 'Build single-page apps with the Vue composition API, backed by Supabase Postgres.', - }, - { - title: 'TanStack Start', - href: '/guides/getting-started/quickstarts/tanstack', - icon: '/docs/img/icons/tanstack-icon', + title: 'Expo React Native', + href: '/guides/getting-started/quickstarts/expo-react-native', + icon: '/docs/img/icons/expo-icon', hasLightIcon: true, - description: 'Type-safe full-stack React that queries Supabase Postgres in server functions.', + description: + 'Ship iOS and Android from one React Native codebase, backed by Supabase Postgres.', }, { - title: 'Refine', - href: '/guides/getting-started/quickstarts/refine', - icon: '/docs/img/icons/refine-icon', + title: 'Flutter', + href: '/guides/getting-started/quickstarts/flutter', + icon: '/docs/img/icons/flutter-icon', hasLightIcon: false, - description: - 'Scaffold CRUD dashboards and admin panels straight from your Supabase Postgres tables.', + feature: 'sdk:dart', + description: 'Ship iOS and Android from one Dart codebase, backed by Supabase Postgres.', }, { title: 'Python', @@ -185,6 +185,14 @@ export const gettingStartedFrameworkQuickstarts: ContentListingGroup = { hasLightIcon: false, description: 'Serve Flask web apps that query Postgres with the Supabase Python client.', }, + { + title: 'Reflex', + href: '/guides/getting-started/quickstarts/reflex', + icon: '/docs/img/icons/reflex-icon', + hasLightIcon: true, + description: + 'Full-stack Python web apps that query Supabase Postgres with the async Python client.', + }, { title: 'Laravel', href: '/guides/getting-started/quickstarts/laravel', @@ -201,6 +209,14 @@ export const gettingStartedFrameworkQuickstarts: ContentListingGroup = { description: 'Convention-driven Ruby apps with Active Record connected directly to your Supabase Postgres database.', }, + { + title: 'Spring Boot', + href: '/guides/getting-started/quickstarts/spring-boot', + icon: '/docs/img/icons/spring-boot-icon', + hasLightIcon: false, + description: + 'Java APIs with Spring Data JPA connected directly to your Supabase Postgres database.', + }, ], } diff --git a/apps/docs/public/img/icons/reflex-icon-light.svg b/apps/docs/public/img/icons/reflex-icon-light.svg new file mode 100644 index 00000000000..545d4780962 --- /dev/null +++ b/apps/docs/public/img/icons/reflex-icon-light.svg @@ -0,0 +1,8 @@ + + + + + + diff --git a/apps/docs/public/img/icons/reflex-icon.svg b/apps/docs/public/img/icons/reflex-icon.svg new file mode 100644 index 00000000000..89ce248b543 --- /dev/null +++ b/apps/docs/public/img/icons/reflex-icon.svg @@ -0,0 +1,8 @@ + + + + + + diff --git a/apps/docs/public/img/icons/spring-boot-icon.svg b/apps/docs/public/img/icons/spring-boot-icon.svg new file mode 100644 index 00000000000..a87092cb99b --- /dev/null +++ b/apps/docs/public/img/icons/spring-boot-icon.svg @@ -0,0 +1,6 @@ + + + + diff --git a/supa-mdx-lint/Rule001HeadingCase.toml b/supa-mdx-lint/Rule001HeadingCase.toml index 34bbda84489..189b854f1fd 100644 --- a/supa-mdx-lint/Rule001HeadingCase.toml +++ b/supa-mdx-lint/Rule001HeadingCase.toml @@ -197,6 +197,7 @@ may_uppercase = [ "Query Performance", "Rails", "React", + "Reflex", "Rollup", "React Email", "React Native",