From c0f1ef51fb9c083ab3a2e6867def0c4c7b2fa521 Mon Sep 17 00:00:00 2001
From: Danny White <3104761+dnywh@users.noreply.github.com>
Date: Mon, 3 Aug 2026 10:52:44 +1000
Subject: [PATCH] feat(docs): migrate resources and getting-started to
ContentListings (#48517)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
## What kind of change does this PR introduce?
Docs update / follow-up to #48379.
## What is the current behavior?
`/guides/resources` and `/guides/getting-started` hand-roll `GlassPanel`
grids in MDX. They look like ContentListings cards after the chrome PR,
but they do not use the shared data files, so they miss PostHog
`docs_content_listing_clicked` telemetry and the CONTRIBUTING
contribution path.
## What is the new behavior?
Those pages use `` backed by
`resources.data.ts` and `getting-started.data.ts`, same pattern as
storage.
- Section-level `$Show` wrappers stay for framework / web / mobile
blocks
- Nimbus stays a `$Partial` behind `$Show`
- New optional per-item `feature` field gates SDK links (e.g. Flutter /
Swift / Kotlin) without splitting whole sections
- CONTRIBUTING notes when to use `feature` vs a partial-level `$Show`
## To test
Compare the following against `master`:
-
[Resources](https://docs-git-dnywh-docs-content-listings-resources-239158-supabase.vercel.app/docs/guides/resources):
overview, migrate, and postgres grids; icons in light/dark
- [Getting
started](https://docs-git-dnywh-docs-content-listings-resources-239158-supabase.vercel.app/docs/guides/getting-started):
overview, use cases, framework quickstarts, web demos, mobile tutorials;
nimbus partial when enabled
- Click a card and confirm `docs_content_listing_clicked` fires with the
expected `listingId`
Everything should look and feel the same. It’s just that we’re using
`ContentListings` instead of `GlassPanel` grids.
## Summary by CodeRabbit
- **New Features**
- Added centralized Getting Started and Resources content listings,
including quickstarts, demos, tutorials, migration guides, and Postgres
resources.
- Added feature-based visibility controls for individual content listing
items.
- **Improvements**
- Disabled content is now automatically hidden from documentation pages
and generated Markdown.
- Pages and sections with no available content are omitted entirely.
- External documentation links are more secure.
- Updated contribution guidance with instructions and examples for
feature flags.
---
apps/docs/CONTRIBUTING.md | 2 +-
.../ContentListings.client.tsx | 12 +-
apps/docs/content/guides/getting-started.mdx | 372 +-----------------
apps/docs/content/guides/resources.mdx | 186 +--------
.../content-listings/getting-started.data.ts | 327 +++++++++++++++
apps/docs/data/content-listings/index.ts | 16 +
.../data/content-listings/resources.data.ts | 131 ++++++
.../markdown-schema/ContentListings.ts | 11 +-
apps/docs/lib/content-listings.test.ts | 68 ++++
apps/docs/lib/content-listings.utils.ts | 8 +-
apps/docs/lib/content-listings.zod.mjs | 5 +
11 files changed, 581 insertions(+), 557 deletions(-)
create mode 100644 apps/docs/data/content-listings/getting-started.data.ts
create mode 100644 apps/docs/data/content-listings/resources.data.ts
diff --git a/apps/docs/CONTRIBUTING.md b/apps/docs/CONTRIBUTING.md
index 41cb00108b4..3f868c0feb1 100644
--- a/apps/docs/CONTRIBUTING.md
+++ b/apps/docs/CONTRIBUTING.md
@@ -260,7 +260,7 @@ Run `pnpm test:local lib/content-listings.test.ts` from apps/docs.
**Manually add content listings:**
1. Add or update a `ContentListingGroup` export in [`data/content-listings/[topic].data.ts`](data/content-listings/). The `id` field must be globally unique across all listing groups. For example, use `storage-get-started` rather than `get-started`. The ID is both the lookup key and the telemetry `listingId`.
-2. Place the component inline in guide MDX, for example ``. Use a partial only when the block is reused or gated with `$Show` at the partial level.
+2. Place the component inline in guide MDX, for example ``. Use a partial only when the block is reused or gated with `$Show` at the partial level. For individual items that depend on a feature flag (for example `sdk:dart`), set `feature` on the item instead of wrapping the whole listing.
3. Run `pnpm test:local lib/content-listings.test.ts` from `apps/docs`.
Code snippets for manually adding content listings are available in [`.vscode/content-listing.code-snippets`](../../.vscode/content-listing.code-snippets). Use `cl-data` for a data export with a namespaced ID. Use `cl-inline` for an MDX component.
diff --git a/apps/docs/components/ContentListings/ContentListings.client.tsx b/apps/docs/components/ContentListings/ContentListings.client.tsx
index f71fd0236b1..548bde14d50 100644
--- a/apps/docs/components/ContentListings/ContentListings.client.tsx
+++ b/apps/docs/components/ContentListings/ContentListings.client.tsx
@@ -2,13 +2,14 @@
import type { ContentListingGroup, ContentListingItem } from '~/lib/content-listings.schema'
import {
+ filterContentListingItems,
getContentListingById,
getContentListingGroupLabel,
isExternalContentListingHref,
} from '~/lib/content-listings.utils'
import { useSendTelemetryEvent } from '~/lib/telemetry'
import Link from 'next/link'
-import { useCallback } from 'react'
+import { useCallback, useMemo } from 'react'
import { Badge } from 'ui'
import { GlassPanel } from 'ui-patterns/GlassPanel'
import { Heading } from 'ui/src/components/CustomHTMLElements'
@@ -52,10 +53,13 @@ function ContentListingGroupHeading({ group }: { group: ContentListingGroup }) {
function ContentListingsGroup({ group }: { group: ContentListingGroup }) {
const { trackClick } = useContentListingClickHandler(group)
+ const items = useMemo(() => filterContentListingItems(group.items), [group.items])
const isGrid = group.type === 'grid'
const listClassName = isGrid ? 'grid md:grid-cols-12 gap-4' : 'list-disc pl-6 space-y-2'
const gridItemClassName = isGrid ? GRID_ITEM_CLASS[group.columns ?? 3] : undefined
+ if (!items.length) return null
+
// Heading stays outside `not-prose` so it inherits the surrounding MDX prose
// typography. The list itself opts out so its explicit Tailwind layout wins.
return (
@@ -64,7 +68,7 @@ function ContentListingsGroup({ group }: { group: ContentListingGroup }) {
diff --git a/apps/docs/content/guides/getting-started.mdx b/apps/docs/content/guides/getting-started.mdx
index 9ec3e750fd9..ede5583753d 100644
--- a/apps/docs/content/guides/getting-started.mdx
+++ b/apps/docs/content/guides/getting-started.mdx
@@ -5,214 +5,13 @@ description: 'Resources for getting started with Supabase.'
hideToc: true
---
-
+
-
-
-
-
-
- Develop with Supabase AI-first using plugins, MCP, and skills.
-
-
-
-
- Learn about the different API keys in Supabase and how to use them.
-
-
-
-
- Use the Supabase CLI to develop locally and collaborate between teams.
-
-
-
-
-
-
-
-
-## Use cases
-
-
-
-
- Build AI-enabled applications using our Vector toolkit.
-
-
-
-
- Clone, deploy, and fully customize a SaaS subscription application with Next.js.
-
-
-
-
- Postgres full-text search, image storage, and more.
-
-
-
-
-
- Learn how to create a Supabase project, add some sample data to your database, and query the
- data from a React app.
-
-
-
-
- Learn how to create a Supabase project, add some sample data to your database, and query the
- data from a Next.js app.
-
-
-
-
- Learn how to create a Supabase project, add some sample data to your database, and query the
- data from a Nuxt app.
-
-
-
-
- Learn how to create a Supabase project, add some sample data to your database, secure it with
- auth, and query the data from a Hono app.
-
-
-
-
- Learn how to create a Supabase project, add some sample data to your database using Prisma
- migration and seeds, and query the data from a RedwoodJS app.
-
-
- <$Show if="sdk:dart">
-
-
- Learn how to create a Supabase project, add some sample data to your database, and query the
- data from a Flutter app.
-
-
- $Show>
- <$Show if="sdk:swift">
-
-
- Learn how to create a Supabase project, add some sample data to your database, and query the
- data from an iOS app.
-
-
- $Show>
- <$Show if="sdk:kotlin">
-
-
- Learn how to create a Supabase project, add some sample data to your database, and query the
- data from an Android Kotlin app.
-
-
- $Show>
-
-
- Learn how to create a Supabase project, add some sample data to your database, and query the
- data from a SvelteKit app.
-
-
-
-
- Learn how to create a Supabase project, add some sample data to your database, and query the
- data from a SolidJS app.
-
-
-
-
- Learn how to create a Supabase project, add some sample data to your database, and query the
- data from a Vue app.
-
-
-
-
- Learn how to create a Supabase project, add some sample data to your database, and query the
- data from a TanStack Start app.
-
-
-
-
- Learn how to create a Supabase project, add some sample data to your database, and query the
- data from a Refine app.
-
-
-
-
-
- Learn how to build a user management app with Next.js and Supabase Database, Auth, and Storage functionality.
-
-
-
-
- Learn how to build a user management app with React and Supabase Database, Auth, and Storage functionality.
-
-
-
-
- Learn how to build a user management app with Vue 3 and Supabase Database, Auth, and Storage functionality.
-
-
-
-
- Learn how to build a user management app with Nuxt 3 and Supabase Database, Auth, and Storage functionality.
-
-
-
-
- Learn how to build a user management app with Angular and Supabase Database, Auth, and Storage functionality.
-
-
-
-
- Learn how to build a user management app with RedwoodJS and Supabase Database, Auth, and Storage functionality.
-
-
-
-
- Learn how to build a user management app with Svelte and Supabase Database, Auth, and Storage functionality.
-
-
-
-
- Learn how to build a user management app with SvelteKit and Supabase Database, Auth, and Storage functionality.
-
-
-
-
- Learn how to build a user management app with Refine and Supabase Database, Auth, and Storage functionality.
-
-
-
$Show>
<$Show if="docs:mobile_tutorials">
-## Mobile tutorials
+
-
- <$Show if="sdk:dart">
-
-
- Learn how to build a user management app with Flutter and Supabase Database, Auth, and Storage functionality.
-
-
- $Show>
-
-
- Learn how to build a user management app with Expo React Native and Supabase Database, Auth, and Storage functionality.
-
-
-
-
- Learn how to implement social authentication in an app with Expo React Native and Supabase Database and Auth functionality.
-
-
- <$Show if="sdk:kotlin">
-
-
- Learn how to build a product management app with Android and Supabase Database, Auth, and Storage functionality.
-
-
- $Show>
- <$Show if="sdk:swift">
-
-
- Learn how to build a user management app with iOS and Supabase Database, Auth, and Storage functionality.
-
-
- $Show>
-
-
- Learn how to build a user management app with Ionic React and Supabase Database, Auth, and Storage functionality.
-
-
-
-
- Learn how to build a user management app with Ionic Vue and Supabase Database, Auth, and Storage functionality.
-
-
-
-
- Learn how to build a user management app with Ionic Angular and Supabase Database, Auth, and Storage functionality.
-
-
-
-
-
- Official GitHub examples, curated content from the community, and more.
-
-
-
-
- Definitions for terminology and acronyms used in the Supabase documentation.
-
-
-
-
-
-
-
-
-
-## Migrate to Supabase
-
-
-
-
-
-
- Move your auth users from Auth0 to a Supabase project.
-
-
-
-
- Move your auth users from a Firebase project to a Supabase project.
-
-
-
-
- Migrate the contents of a Firestore collection to a single Postgres table.
-
-
-
-
- Convert your Firebase Storage files to Supabase Storage.
-
-
-
-
- Migrate your Heroku Postgres database to Supabase.
-
-
-
-
- Migrate your Render Postgres database to Supabase.
-
-
-
-
- Migrate your Amazon RDS database to Supabase.
-
-
-
-
- Migrate your Postgres database to Supabase.
-
-
-
-
- Migrate your MySQL database to Supabase.
-
-
-
-
- Migrate your Microsoft SQL Server database to Supabase.
-
-
-
-
-
-
-
-
-
-## Postgres resources
-
-
-
-
-
-
- Improve query performance using various index types in Postgres.
-
-
-
-
- Understand the types of foreign key constraint deletes.
-
-
-
-
- Delete all tables in a given schema.
-
-
-
-
- Retrieve the first row in each distinct group.
-
-
-
-
- Find out which version of Postgres you are running.
-
-
-
-
-
-
-{/* end of container */}
-
-
+
diff --git a/apps/docs/data/content-listings/getting-started.data.ts b/apps/docs/data/content-listings/getting-started.data.ts
new file mode 100644
index 00000000000..2d5ae192dce
--- /dev/null
+++ b/apps/docs/data/content-listings/getting-started.data.ts
@@ -0,0 +1,327 @@
+import type { ContentListingGroup } from '~/lib/content-listings.schema'
+
+export const gettingStartedOverview: ContentListingGroup = {
+ id: 'getting-started-overview',
+ type: 'grid',
+ items: [
+ {
+ title: 'Build with AI tools',
+ href: '/guides/ai-tools',
+ description: 'Develop with Supabase AI-first using plugins, MCP, and skills.',
+ },
+ {
+ title: 'API Keys',
+ href: '/guides/getting-started/api-keys',
+ description: 'Learn about the different API keys in Supabase and how to use them.',
+ },
+ {
+ title: 'Local Development',
+ href: '/guides/local-development',
+ description: 'Use the Supabase CLI to develop locally and collaborate between teams.',
+ },
+ ],
+}
+
+export const gettingStartedUseCases: ContentListingGroup = {
+ id: 'getting-started-use-cases',
+ heading: 'Use cases',
+ headingLevel: 'h3',
+ type: 'grid',
+ items: [
+ {
+ title: 'AI, Vectors, and embeddings',
+ href: '/guides/ai#examples',
+ icon: '/docs/img/icons/openai_logo',
+ hasLightIcon: true,
+ description: 'Build AI-enabled applications using our Vector toolkit.',
+ },
+ {
+ title: 'Subscription Payments (SaaS)',
+ href: 'https://github.com/vercel/nextjs-subscription-payments#nextjs-subscription-payments-starter',
+ icon: '/docs/img/icons/nextjs-icon',
+ hasLightIcon: false,
+ description:
+ 'Clone, deploy, and fully customize a SaaS subscription application with Next.js.',
+ },
+ {
+ title: 'Partner Gallery',
+ href: 'https://github.com/supabase-community/partner-gallery-example#supabase-partner-gallery-example',
+ icon: '/docs/img/icons/nextjs-icon',
+ hasLightIcon: false,
+ description: 'Postgres full-text search, image storage, and more.',
+ },
+ ],
+}
+
+export const gettingStartedFrameworkQuickstarts: ContentListingGroup = {
+ id: 'getting-started-framework-quickstarts',
+ heading: 'Framework quickstarts',
+ headingLevel: 'h3',
+ type: 'grid',
+ items: [
+ {
+ title: 'React',
+ href: '/guides/getting-started/quickstarts/reactjs',
+ icon: '/docs/img/icons/react-icon',
+ hasLightIcon: false,
+ description:
+ 'Learn how to create a Supabase project, add some sample data to your database, and query the data from a React app.',
+ },
+ {
+ title: 'Next.js',
+ href: '/guides/getting-started/quickstarts/nextjs',
+ icon: '/docs/img/icons/nextjs-icon',
+ hasLightIcon: true,
+ description:
+ 'Learn how to create a Supabase project, add some sample data to your database, and query the data from a Next.js app.',
+ },
+ {
+ title: 'Nuxt',
+ href: '/guides/getting-started/quickstarts/nuxtjs',
+ icon: '/docs/img/icons/nuxt-icon',
+ hasLightIcon: false,
+ description:
+ 'Learn how to create a Supabase project, add some sample data to your database, and query the data from a Nuxt app.',
+ },
+ {
+ title: 'Hono',
+ href: '/guides/getting-started/quickstarts/hono',
+ icon: '/docs/img/icons/hono-icon',
+ hasLightIcon: false,
+ description:
+ 'Learn how to create a Supabase project, add some sample data to your database, secure it with auth, and query the data from a Hono app.',
+ },
+ {
+ title: 'RedwoodJS',
+ href: '/guides/getting-started/quickstarts/redwoodjs',
+ icon: '/docs/img/icons/redwood-icon',
+ hasLightIcon: false,
+ description:
+ 'Learn how to create a Supabase project, add some sample data to your database using Prisma migration and seeds, and query the data from a RedwoodJS app.',
+ },
+ {
+ title: 'Flutter',
+ href: '/guides/getting-started/quickstarts/flutter',
+ icon: '/docs/img/icons/flutter-icon',
+ hasLightIcon: false,
+ feature: 'sdk:dart',
+ description:
+ 'Learn how to create a Supabase project, add some sample data to your database, and query the data from a Flutter app.',
+ },
+ {
+ title: 'iOS SwiftUI',
+ href: '/guides/getting-started/quickstarts/ios-swiftui',
+ icon: '/docs/img/icons/swift-icon',
+ hasLightIcon: false,
+ feature: 'sdk:swift',
+ description:
+ 'Learn how to create a Supabase project, add some sample data to your database, and query the data from an iOS app.',
+ },
+ {
+ title: 'Android Kotlin',
+ href: '/guides/getting-started/quickstarts/kotlin',
+ icon: '/docs/img/icons/kotlin-icon',
+ hasLightIcon: false,
+ feature: 'sdk:kotlin',
+ description:
+ 'Learn how to create a Supabase project, add some sample data to your database, and query the data from an Android Kotlin app.',
+ },
+ {
+ title: 'SvelteKit',
+ href: '/guides/getting-started/quickstarts/sveltekit',
+ icon: '/docs/img/icons/svelte-icon',
+ hasLightIcon: false,
+ description:
+ 'Learn how to create a Supabase project, add some sample data to your database, and query the data from a SvelteKit app.',
+ },
+ {
+ title: 'SolidJS',
+ href: '/guides/getting-started/quickstarts/solidjs',
+ icon: '/docs/img/icons/solidjs-icon',
+ hasLightIcon: false,
+ description:
+ 'Learn how to create a Supabase project, add some sample data to your database, and query the data from a SolidJS app.',
+ },
+ {
+ title: 'Vue',
+ href: '/guides/getting-started/quickstarts/vue',
+ icon: '/docs/img/icons/vuejs-icon',
+ hasLightIcon: false,
+ description:
+ 'Learn how to create a Supabase project, add some sample data to your database, and query the data from a Vue app.',
+ },
+ {
+ title: 'TanStack Start',
+ href: '/guides/getting-started/quickstarts/tanstack',
+ icon: '/docs/img/icons/tanstack-icon',
+ hasLightIcon: true,
+ description:
+ 'Learn how to create a Supabase project, add some sample data to your database, and query the data from a TanStack Start app.',
+ },
+ {
+ title: 'Refine',
+ href: '/guides/getting-started/quickstarts/refine',
+ icon: '/docs/img/icons/refine-icon',
+ hasLightIcon: false,
+ description:
+ 'Learn how to create a Supabase project, add some sample data to your database, and query the data from a Refine app.',
+ },
+ ],
+}
+
+export const gettingStartedWebAppDemos: ContentListingGroup = {
+ id: 'getting-started-web-app-demos',
+ heading: 'Web app demos',
+ headingLevel: 'h3',
+ type: 'grid',
+ items: [
+ {
+ title: 'Next.js',
+ href: '/guides/getting-started/tutorials/with-nextjs',
+ icon: '/docs/img/icons/nextjs-icon',
+ hasLightIcon: true,
+ description:
+ 'Learn how to build a user management app with Next.js and Supabase Database, Auth, and Storage functionality.',
+ },
+ {
+ title: 'React',
+ href: '/guides/getting-started/tutorials/with-react',
+ icon: '/docs/img/icons/react-icon',
+ hasLightIcon: false,
+ description:
+ 'Learn how to build a user management app with React and Supabase Database, Auth, and Storage functionality.',
+ },
+ {
+ title: 'Vue 3',
+ href: '/guides/getting-started/tutorials/with-vue-3',
+ icon: '/docs/img/icons/vuejs-icon',
+ hasLightIcon: false,
+ description:
+ 'Learn how to build a user management app with Vue 3 and Supabase Database, Auth, and Storage functionality.',
+ },
+ {
+ title: 'Nuxt 3',
+ href: '/guides/getting-started/tutorials/with-nuxt-3',
+ icon: '/docs/img/icons/nuxt-icon',
+ hasLightIcon: false,
+ description:
+ 'Learn how to build a user management app with Nuxt 3 and Supabase Database, Auth, and Storage functionality.',
+ },
+ {
+ title: 'Angular',
+ href: '/guides/getting-started/tutorials/with-angular',
+ icon: '/docs/img/icons/angular-icon',
+ hasLightIcon: false,
+ description:
+ 'Learn how to build a user management app with Angular and Supabase Database, Auth, and Storage functionality.',
+ },
+ {
+ title: 'RedwoodJS',
+ href: '/guides/getting-started/tutorials/with-redwoodjs',
+ icon: '/docs/img/icons/redwood-icon',
+ hasLightIcon: false,
+ description:
+ 'Learn how to build a user management app with RedwoodJS and Supabase Database, Auth, and Storage functionality.',
+ },
+ {
+ title: 'Svelte',
+ href: '/guides/getting-started/tutorials/with-svelte',
+ icon: '/docs/img/icons/svelte-icon',
+ hasLightIcon: false,
+ description:
+ 'Learn how to build a user management app with Svelte and Supabase Database, Auth, and Storage functionality.',
+ },
+ {
+ title: 'SvelteKit',
+ href: '/guides/getting-started/tutorials/with-sveltekit',
+ icon: '/docs/img/icons/svelte-icon',
+ hasLightIcon: false,
+ description:
+ 'Learn how to build a user management app with SvelteKit and Supabase Database, Auth, and Storage functionality.',
+ },
+ {
+ title: 'Refine',
+ href: '/guides/getting-started/tutorials/with-refine',
+ icon: '/docs/img/icons/refine-icon',
+ hasLightIcon: false,
+ description:
+ 'Learn how to build a user management app with Refine and Supabase Database, Auth, and Storage functionality.',
+ },
+ ],
+}
+
+export const gettingStartedMobileTutorials: ContentListingGroup = {
+ id: 'getting-started-mobile-tutorials',
+ heading: 'Mobile tutorials',
+ headingLevel: 'h3',
+ type: 'grid',
+ items: [
+ {
+ title: 'Flutter',
+ href: '/guides/getting-started/tutorials/with-flutter',
+ icon: '/docs/img/icons/flutter-icon',
+ hasLightIcon: false,
+ feature: 'sdk:dart',
+ description:
+ 'Learn how to build a user management app with Flutter and Supabase Database, Auth, and Storage functionality.',
+ },
+ {
+ title: 'Expo React Native',
+ href: '/guides/getting-started/tutorials/with-expo-react-native',
+ icon: '/docs/img/icons/expo-icon',
+ hasLightIcon: true,
+ description:
+ 'Learn how to build a user management app with Expo React Native and Supabase Database, Auth, and Storage functionality.',
+ },
+ {
+ title: 'Expo React Native Social Auth',
+ href: '/guides/auth/quickstarts/with-expo-react-native-social-auth',
+ icon: '/docs/img/icons/expo-icon',
+ hasLightIcon: true,
+ description:
+ 'Learn how to implement social authentication in an app with Expo React Native and Supabase Database and Auth functionality.',
+ },
+ {
+ title: 'Android Kotlin',
+ href: '/guides/getting-started/tutorials/with-kotlin',
+ icon: '/docs/img/icons/kotlin-icon',
+ hasLightIcon: false,
+ feature: 'sdk:kotlin',
+ description:
+ 'Learn how to build a product management app with Android and Supabase Database, Auth, and Storage functionality.',
+ },
+ {
+ title: 'iOS Swift',
+ href: '/guides/getting-started/tutorials/with-swift',
+ icon: '/docs/img/icons/swift-icon',
+ hasLightIcon: false,
+ feature: 'sdk:swift',
+ description:
+ 'Learn how to build a user management app with iOS and Supabase Database, Auth, and Storage functionality.',
+ },
+ {
+ title: 'Ionic React',
+ href: '/guides/getting-started/tutorials/with-ionic-react',
+ icon: '/docs/img/icons/ionic-icon',
+ hasLightIcon: false,
+ description:
+ 'Learn how to build a user management app with Ionic React and Supabase Database, Auth, and Storage functionality.',
+ },
+ {
+ title: 'Ionic Vue',
+ href: '/guides/getting-started/tutorials/with-ionic-vue',
+ icon: '/docs/img/icons/ionic-icon',
+ hasLightIcon: false,
+ description:
+ 'Learn how to build a user management app with Ionic Vue and Supabase Database, Auth, and Storage functionality.',
+ },
+ {
+ title: 'Ionic Angular',
+ href: '/guides/getting-started/tutorials/with-ionic-angular',
+ icon: '/docs/img/icons/ionic-icon',
+ hasLightIcon: false,
+ description:
+ 'Learn how to build a user management app with Ionic Angular and Supabase Database, Auth, and Storage functionality.',
+ },
+ ],
+}
diff --git a/apps/docs/data/content-listings/index.ts b/apps/docs/data/content-listings/index.ts
index bf0e5f93537..ef18cdc48ae 100644
--- a/apps/docs/data/content-listings/index.ts
+++ b/apps/docs/data/content-listings/index.ts
@@ -11,8 +11,16 @@ import {
functionsExamplesWebhooksPayments,
functionsGetStarted,
} from './functions.data'
+import {
+ gettingStartedFrameworkQuickstarts,
+ gettingStartedMobileTutorials,
+ gettingStartedOverview,
+ gettingStartedUseCases,
+ gettingStartedWebAppDemos,
+} from './getting-started.data'
import { logDrainsDestinations } from './log-drains.data'
import { realtimeExamples, realtimeGetStarted, realtimeResources } from './realtime.data'
+import { resourcesMigrate, resourcesOverview, resourcesPostgres } from './resources.data'
import {
selfHostingCommunity,
selfHostingGetHelp,
@@ -37,10 +45,18 @@ const ALL_GROUPS: readonly ContentListingGroup[] = [
functionsExamplesAiMedia,
functionsExamplesMessaging,
functionsExamplesOperations,
+ gettingStartedOverview,
+ gettingStartedUseCases,
+ gettingStartedFrameworkQuickstarts,
+ gettingStartedWebAppDemos,
+ gettingStartedMobileTutorials,
logDrainsDestinations,
realtimeGetStarted,
realtimeExamples,
realtimeResources,
+ resourcesOverview,
+ resourcesMigrate,
+ resourcesPostgres,
selfHostingGetStarted,
selfHostingCommunity,
selfHostingResolveIssues,
diff --git a/apps/docs/data/content-listings/resources.data.ts b/apps/docs/data/content-listings/resources.data.ts
new file mode 100644
index 00000000000..bca9a8af826
--- /dev/null
+++ b/apps/docs/data/content-listings/resources.data.ts
@@ -0,0 +1,131 @@
+import type { ContentListingGroup } from '~/lib/content-listings.schema'
+
+export const resourcesOverview: ContentListingGroup = {
+ id: 'resources-overview',
+ type: 'grid',
+ items: [
+ {
+ title: 'Examples',
+ href: '/guides/getting-started',
+ description: 'Official GitHub examples, curated content from the community, and more.',
+ },
+ {
+ title: 'Glossary',
+ href: '/guides/resources/glossary',
+ description: 'Definitions for terminology and acronyms used in the Supabase documentation.',
+ },
+ ],
+}
+
+export const resourcesMigrate: ContentListingGroup = {
+ id: 'resources-migrate',
+ heading: 'Migrate to Supabase',
+ headingLevel: 'h3',
+ type: 'grid',
+ items: [
+ {
+ title: 'Auth0',
+ href: '/guides/platform/migrating-to-supabase/auth0',
+ icon: '/docs/img/icons/auth0-icon',
+ hasLightIcon: true,
+ description: 'Move your auth users from Auth0 to a Supabase project.',
+ },
+ {
+ title: 'Firebase Auth',
+ href: '/guides/platform/migrating-to-supabase/firebase-auth',
+ icon: '/docs/img/icons/firebase-icon',
+ hasLightIcon: false,
+ description: 'Move your auth users from a Firebase project to a Supabase project.',
+ },
+ {
+ title: 'Firestore Data',
+ href: '/guides/platform/migrating-to-supabase/firestore-data',
+ icon: '/docs/img/icons/firebase-icon',
+ hasLightIcon: false,
+ description: 'Migrate the contents of a Firestore collection to a single Postgres table.',
+ },
+ {
+ title: 'Firebase Storage',
+ href: '/guides/platform/migrating-to-supabase/firebase-storage',
+ icon: '/docs/img/icons/firebase-icon',
+ hasLightIcon: false,
+ description: 'Convert your Firebase Storage files to Supabase Storage.',
+ },
+ {
+ title: 'Heroku',
+ href: '/guides/platform/migrating-to-supabase/heroku',
+ icon: '/docs/img/icons/heroku-icon',
+ hasLightIcon: false,
+ description: 'Migrate your Heroku Postgres database to Supabase.',
+ },
+ {
+ title: 'Render',
+ href: '/guides/platform/migrating-to-supabase/render',
+ icon: '/docs/img/icons/render-icon',
+ hasLightIcon: false,
+ description: 'Migrate your Render Postgres database to Supabase.',
+ },
+ {
+ title: 'Amazon RDS',
+ href: '/guides/platform/migrating-to-supabase/amazon-rds',
+ icon: '/docs/img/icons/aws-rds-icon',
+ hasLightIcon: false,
+ description: 'Migrate your Amazon RDS database to Supabase.',
+ },
+ {
+ title: 'Postgres',
+ href: '/guides/platform/migrating-to-supabase/postgres',
+ icon: '/docs/img/icons/postgres-icon',
+ hasLightIcon: false,
+ description: 'Migrate your Postgres database to Supabase.',
+ },
+ {
+ title: 'MySQL',
+ href: '/guides/platform/migrating-to-supabase/mysql',
+ icon: '/docs/img/icons/mysql-icon',
+ hasLightIcon: false,
+ description: 'Migrate your MySQL database to Supabase.',
+ },
+ {
+ title: 'Microsoft SQL Server',
+ href: '/guides/platform/migrating-to-supabase/mssql',
+ icon: '/docs/img/icons/mssql-icon',
+ hasLightIcon: false,
+ description: 'Migrate your Microsoft SQL Server database to Supabase.',
+ },
+ ],
+}
+
+export const resourcesPostgres: ContentListingGroup = {
+ id: 'resources-postgres',
+ heading: 'Postgres resources',
+ headingLevel: 'h3',
+ type: 'grid',
+ items: [
+ {
+ title: 'Managing Indexes',
+ href: '/guides/database/postgres/indexes',
+ description: 'Improve query performance using various index types in Postgres.',
+ },
+ {
+ title: 'Cascade Deletes',
+ href: '/guides/database/postgres/cascade-deletes',
+ description: 'Understand the types of foreign key constraint deletes.',
+ },
+ {
+ title: 'Drop all tables in schema',
+ href: '/guides/database/postgres/dropping-all-tables-in-schema',
+ description: 'Delete all tables in a given schema.',
+ },
+ {
+ title: 'Select first row per group',
+ href: '/guides/database/postgres/first-row-in-group',
+ description: 'Retrieve the first row in each distinct group.',
+ },
+ {
+ title: 'Print Postgres version',
+ href: '/guides/database/postgres/which-version-of-postgres',
+ description: 'Find out which version of Postgres you are running.',
+ },
+ ],
+}
diff --git a/apps/docs/internals/markdown-schema/ContentListings.ts b/apps/docs/internals/markdown-schema/ContentListings.ts
index 299b23c06ec..ce78463b70a 100644
--- a/apps/docs/internals/markdown-schema/ContentListings.ts
+++ b/apps/docs/internals/markdown-schema/ContentListings.ts
@@ -1,6 +1,10 @@
import { withDocsBasePath } from '~/internals/internal-links'
import type { ContentListingGroup } from '~/lib/content-listings.schema'
-import { getContentListingById, isExternalContentListingHref } from '~/lib/content-listings.utils'
+import {
+ filterContentListingItems,
+ getContentListingById,
+ isExternalContentListingHref,
+} from '~/lib/content-listings.utils'
import { getInternalLinkBaseUrl } from '../internal-links'
@@ -14,6 +18,9 @@ export function serializeContentListingGroupToMarkdown(
group: ContentListingGroup,
linkBaseUrl: string
): string {
+ const items = filterContentListingItems(group.items)
+ if (!items.length) return ''
+
const lines: string[] = []
if (group.heading) {
const level = group.headingLevel ?? 'h2'
@@ -26,7 +33,7 @@ export function serializeContentListingGroupToMarkdown(
lines.push('')
}
- for (const item of group.items) {
+ for (const item of items) {
const href = isExternalContentListingHref(item.href)
? item.href
: `${linkBaseUrl}${withDocsBasePath(item.href)}`
diff --git a/apps/docs/lib/content-listings.test.ts b/apps/docs/lib/content-listings.test.ts
index 0c609a60699..87b0d5d82fe 100644
--- a/apps/docs/lib/content-listings.test.ts
+++ b/apps/docs/lib/content-listings.test.ts
@@ -144,6 +144,74 @@ describe('serializeContentListingGroupToMarkdown', () => {
expect(markdown).not.toMatch(/^#+\s/m)
expect(markdown).toContain('**[Connect]')
})
+
+ it('omits feature-gated items when those features are disabled', () => {
+ const previous = process.env.ENABLED_FEATURES_OVERRIDE_DISABLE_ALL
+ process.env.ENABLED_FEATURES_OVERRIDE_DISABLE_ALL = 'true'
+
+ try {
+ const markdown = serializeContentListingGroupToMarkdown(
+ {
+ id: 'frameworks',
+ heading: 'Frameworks',
+ items: [
+ {
+ title: 'React',
+ href: '/guides/getting-started/quickstarts/reactjs',
+ description: 'Web framework.',
+ },
+ {
+ title: 'Flutter',
+ href: '/guides/getting-started/quickstarts/flutter',
+ description: 'Mobile framework.',
+ feature: 'sdk:dart',
+ },
+ ],
+ },
+ ''
+ )
+
+ expect(markdown).toContain('**[React]')
+ expect(markdown).not.toContain('Flutter')
+ } finally {
+ if (previous === undefined) {
+ delete process.env.ENABLED_FEATURES_OVERRIDE_DISABLE_ALL
+ } else {
+ process.env.ENABLED_FEATURES_OVERRIDE_DISABLE_ALL = previous
+ }
+ }
+ })
+
+ it('returns empty string when every item is feature-gated off', () => {
+ const previous = process.env.ENABLED_FEATURES_OVERRIDE_DISABLE_ALL
+ process.env.ENABLED_FEATURES_OVERRIDE_DISABLE_ALL = 'true'
+
+ try {
+ const markdown = serializeContentListingGroupToMarkdown(
+ {
+ id: 'sdk-only',
+ heading: 'SDKs',
+ items: [
+ {
+ title: 'Flutter',
+ href: '/guides/getting-started/quickstarts/flutter',
+ description: 'Mobile framework.',
+ feature: 'sdk:dart',
+ },
+ ],
+ },
+ ''
+ )
+
+ expect(markdown).toBe('')
+ } finally {
+ if (previous === undefined) {
+ delete process.env.ENABLED_FEATURES_OVERRIDE_DISABLE_ALL
+ } else {
+ process.env.ENABLED_FEATURES_OVERRIDE_DISABLE_ALL = previous
+ }
+ }
+ })
})
describe('ContentListings markdown handler', () => {
diff --git a/apps/docs/lib/content-listings.utils.ts b/apps/docs/lib/content-listings.utils.ts
index 10c73b4d4f1..93c12304399 100644
--- a/apps/docs/lib/content-listings.utils.ts
+++ b/apps/docs/lib/content-listings.utils.ts
@@ -1,6 +1,7 @@
import { CONTENT_LISTINGS } from '~/data/content-listings'
+import { isFeatureEnabled, type Feature } from 'common/enabled-features'
-import type { ContentListingGroup } from './content-listings.schema'
+import type { ContentListingGroup, ContentListingItem } from './content-listings.schema'
/** Label for telemetry — prefers heading, falls back to id. */
export function getContentListingGroupLabel(group: ContentListingGroup): string {
@@ -14,3 +15,8 @@ export function isExternalContentListingHref(href: string): boolean {
export function getContentListingById(id: string): ContentListingGroup | undefined {
return CONTENT_LISTINGS[id]
}
+
+/** Omits items whose `feature` flag is disabled. Shared by UI and markdown export. */
+export function filterContentListingItems(items: ContentListingItem[]): ContentListingItem[] {
+ return items.filter((item) => !item.feature || isFeatureEnabled(item.feature as Feature))
+}
diff --git a/apps/docs/lib/content-listings.zod.mjs b/apps/docs/lib/content-listings.zod.mjs
index 3f7060ef8cb..a678cb6b578 100644
--- a/apps/docs/lib/content-listings.zod.mjs
+++ b/apps/docs/lib/content-listings.zod.mjs
@@ -29,6 +29,11 @@ export const contentListingItemSchema = z.object({
badge: z.string().min(1).optional(),
/** Grid cards only. Defaults to inline (next to the title), matching existing usage. */
badgePosition: z.enum(['inline', 'below']).optional(),
+ /**
+ * When set, the item is omitted unless `isFeatureEnabled(feature)` is true.
+ * Use for SDK- or product-gated links (e.g. `sdk:dart`) inside a shared listing.
+ */
+ feature: z.string().min(1).optional(),
})
export const contentListingGroupTypeSchema = z.enum(['list', 'grid'])