feat(docs): migrate resources and getting-started to ContentListings (#48517)

## 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 `<ContentListings id="…" />` 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.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## 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.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Danny White authored and GitHub committed 2026-08-03 00:52:44 +00:00
1 parent e7796315d2
commit c0f1ef51fb
11 files changed
+581 -557

No files matched your search

+1 -1
View File
@@ -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 `<ContentListings id="storage-get-started" />`. 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 `<ContentListings id="storage-get-started" />`. 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.
@@ -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 }) {
<div className="not-prose space-y-4">
{group.description && <p className="text-foreground-light">{group.description}</p>}
<ul className={listClassName}>
{group.items.map((item) => {
{items.map((item) => {
const external = isExternalContentListingHref(item.href)
const key = `${group.id}-${item.href}`
@@ -77,6 +81,7 @@ function ContentListingsGroup({ group }: { group: ContentListingGroup }) {
className="block h-full"
onClick={() => trackClick(item)}
target={external ? '_blank' : undefined}
rel={external ? 'noopener noreferrer' : undefined}
>
<GlassPanel
title={item.title}
@@ -106,6 +111,7 @@ function ContentListingsGroup({ group }: { group: ContentListingGroup }) {
href={item.href}
onClick={() => trackClick(item)}
target={external ? '_blank' : undefined}
rel={external ? 'noopener noreferrer' : undefined}
>
<strong>{item.title}</strong>: {item.description}
</Link>
@@ -120,7 +126,7 @@ function ContentListingsGroup({ group }: { group: ContentListingGroup }) {
export function ContentListings({ id }: { id: string }) {
const group = getContentListingById(id)
if (!group || !group.items.length) return null
if (!group || !filterContentListingItems(group.items).length) return null
return (
<div className="my-10 space-y-10">
+5 -367
View File
@@ -5,214 +5,13 @@ description: 'Resources for getting started with Supabase.'
hideToc: true
---
<div className="flex flex-col gap-12 my-12">
<ContentListings id="getting-started-overview" />
<div>
<div className="grid grid-cols-12 gap-6 not-prose">
<Link href="/guides/ai-tools" className="col-span-12 md:col-span-6 xl:col-span-4" passHref>
<GlassPanel title="Build with AI tools" hasLightIcon={true} showIconBg={true}>
Develop with Supabase AI-first using plugins, MCP, and skills.
</GlassPanel>
</Link>
<Link
href="/guides/getting-started/api-keys"
className="col-span-12 md:col-span-6 xl:col-span-4"
passHref
>
<GlassPanel title="API Keys" hasLightIcon={true} showIconBg={true}>
Learn about the different API keys in Supabase and how to use them.
</GlassPanel>
</Link>
<Link
href="/guides/local-development"
className="col-span-12 md:col-span-6 xl:col-span-4"
passHref
>
<GlassPanel title="Local Development" hasLightIcon={true} showIconBg={true}>
Use the Supabase CLI to develop locally and collaborate between teams.
</GlassPanel>
</Link>
</div>
</div>
</div>
## Use cases
<div className="grid lg:grid-cols-12 gap-6 not-prose">
<Link href="/guides/ai#examples" passHref className="col-span-12 md:col-span-6 xl:col-span-4">
<GlassPanel
title="AI, Vectors, and embeddings"
icon="/docs/img/icons/openai_logo"
hasLightIcon={true}
>
Build AI-enabled applications using our Vector toolkit.
</GlassPanel>
</Link>
<Link
href="https://github.com/vercel/nextjs-subscription-payments#nextjs-subscription-payments-starter"
passHref
className="col-span-12 md:col-span-6 xl:col-span-4"
>
<GlassPanel title="Subscription Payments (SaaS)" icon="/docs/img/icons/nextjs-icon">
Clone, deploy, and fully customize a SaaS subscription application with Next.js.
</GlassPanel>
</Link>
<Link
href="https://github.com/supabase-community/partner-gallery-example#supabase-partner-gallery-example"
passHref
className="col-span-12 md:col-span-6 xl:col-span-4"
>
<GlassPanel title="Partner Gallery" icon="/docs/img/icons/nextjs-icon">
Postgres full-text search, image storage, and more.
</GlassPanel>
</Link>
</div>
## Framework quickstarts
<ContentListings id="getting-started-use-cases" />
<$Show if="docs:framework_quickstarts">
<div className="grid lg:grid-cols-12 gap-6 not-prose">
<Link
href="/guides/getting-started/quickstarts/reactjs"
passHref
className="col-span-12 md:col-span-6 xl:col-span-4"
>
<GlassPanel title="React" icon="/docs/img/icons/react-icon">
Learn how to create a Supabase project, add some sample data to your database, and query the
data from a React app.
</GlassPanel>
</Link>
<Link
href="/guides/getting-started/quickstarts/nextjs"
passHref
className="col-span-12 md:col-span-6 xl:col-span-4"
>
<GlassPanel title="Next.js" icon="/docs/img/icons/nextjs-icon" hasLightIcon={true}>
Learn how to create a Supabase project, add some sample data to your database, and query the
data from a Next.js app.
</GlassPanel>
</Link>
<Link
href="/guides/getting-started/quickstarts/nuxtjs"
passHref
className="col-span-12 md:col-span-6 xl:col-span-4"
>
<GlassPanel title="Nuxt" icon="/docs/img/icons/nuxt-icon">
Learn how to create a Supabase project, add some sample data to your database, and query the
data from a Nuxt app.
</GlassPanel>
</Link>
<Link
href="/guides/getting-started/quickstarts/hono"
passHref
className="col-span-12 md:col-span-6 xl:col-span-4"
>
<GlassPanel title="Hono" icon="/docs/img/icons/hono-icon">
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.
</GlassPanel>
</Link>
<Link
href="/guides/getting-started/quickstarts/redwoodjs"
passHref
className="col-span-12 md:col-span-6 xl:col-span-4"
>
<GlassPanel title="RedwoodJS" icon="/docs/img/icons/redwood-icon">
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.
</GlassPanel>
</Link>
<$Show if="sdk:dart">
<Link
href="/guides/getting-started/quickstarts/flutter"
passHref
className="col-span-12 md:col-span-6 xl:col-span-4"
>
<GlassPanel title="Flutter" icon="/docs/img/icons/flutter-icon">
Learn how to create a Supabase project, add some sample data to your database, and query the
data from a Flutter app.
</GlassPanel>
</Link>
</$Show>
<$Show if="sdk:swift">
<Link
href="/guides/getting-started/quickstarts/ios-swiftui"
passHref
className="col-span-12 md:col-span-6 xl:col-span-4"
>
<GlassPanel title="iOS SwiftUI" icon="/docs/img/icons/swift-icon">
Learn how to create a Supabase project, add some sample data to your database, and query the
data from an iOS app.
</GlassPanel>
</Link>
</$Show>
<$Show if="sdk:kotlin">
<Link
href="/guides/getting-started/quickstarts/kotlin"
passHref
className="col-span-12 md:col-span-6 xl:col-span-4"
>
<GlassPanel title="Android Kotlin" icon="/docs/img/icons/kotlin-icon">
Learn how to create a Supabase project, add some sample data to your database, and query the
data from an Android Kotlin app.
</GlassPanel>
</Link>
</$Show>
<Link
href="/guides/getting-started/quickstarts/sveltekit"
passHref
className="col-span-12 md:col-span-6 xl:col-span-4"
>
<GlassPanel title="SvelteKit" icon="/docs/img/icons/svelte-icon">
Learn how to create a Supabase project, add some sample data to your database, and query the
data from a SvelteKit app.
</GlassPanel>
</Link>
<Link
href="/guides/getting-started/quickstarts/solidjs"
passHref
className="col-span-12 md:col-span-6 xl:col-span-4"
>
<GlassPanel title="SolidJS" icon="/docs/img/icons/solidjs-icon">
Learn how to create a Supabase project, add some sample data to your database, and query the
data from a SolidJS app.
</GlassPanel>
</Link>
<Link
href="/guides/getting-started/quickstarts/vue"
passHref
className="col-span-12 md:col-span-6 xl:col-span-4"
>
<GlassPanel title="Vue" icon="/docs/img/icons/vuejs-icon">
Learn how to create a Supabase project, add some sample data to your database, and query the
data from a Vue app.
</GlassPanel>
</Link>
<Link
href="/guides/getting-started/quickstarts/tanstack"
passHref
className="col-span-12 md:col-span-6 xl:col-span-4"
>
<GlassPanel title="TanStack Start" icon="/docs/img/icons/tanstack-icon" hasLightIcon={true}>
Learn how to create a Supabase project, add some sample data to your database, and query the
data from a TanStack Start app.
</GlassPanel>
</Link>
<Link
href="/guides/getting-started/quickstarts/refine"
passHref
className="col-span-12 md:col-span-6 xl:col-span-4"
>
<GlassPanel title="Refine" icon="/docs/img/icons/refine-icon">
Learn how to create a Supabase project, add some sample data to your database, and query the
data from a Refine app.
</GlassPanel>
</Link>
</div>
<ContentListings id="getting-started-framework-quickstarts" />
</$Show>
@@ -224,173 +23,12 @@ hideToc: true
<$Show if="docs:web_apps">
## Web app demos
<ContentListings id="getting-started-web-app-demos" />
<div className="grid lg:grid-cols-12 gap-6 not-prose">
<Link href="/guides/getting-started/tutorials/with-nextjs" passHref className="col-span-12 md:col-span-6 xl:col-span-4">
<GlassPanel
title="Next.js"
icon="/docs/img/icons/nextjs-icon"
hasLightIcon={true}
>
Learn how to build a user management app with Next.js and Supabase Database, Auth, and Storage functionality.
</GlassPanel>
</Link>
<Link href="/guides/getting-started/tutorials/with-react" passHref className="col-span-12 md:col-span-6 xl:col-span-4">
<GlassPanel
title="React"
icon="/docs/img/icons/react-icon"
>
Learn how to build a user management app with React and Supabase Database, Auth, and Storage functionality.
</GlassPanel>
</Link>
<Link href="/guides/getting-started/tutorials/with-vue-3" passHref className="col-span-12 md:col-span-6 xl:col-span-4">
<GlassPanel
title="Vue 3"
icon="/docs/img/icons/vuejs-icon"
>
Learn how to build a user management app with Vue 3 and Supabase Database, Auth, and Storage functionality.
</GlassPanel>
</Link>
<Link href="/guides/getting-started/tutorials/with-nuxt-3" passHref className="col-span-12 md:col-span-6 xl:col-span-4">
<GlassPanel
title="Nuxt 3"
icon="/docs/img/icons/nuxt-icon"
>
Learn how to build a user management app with Nuxt 3 and Supabase Database, Auth, and Storage functionality.
</GlassPanel>
</Link>
<Link href="/guides/getting-started/tutorials/with-angular" passHref className="col-span-12 md:col-span-6 xl:col-span-4">
<GlassPanel
title="Angular"
icon="/docs/img/icons/angular-icon"
>
Learn how to build a user management app with Angular and Supabase Database, Auth, and Storage functionality.
</GlassPanel>
</Link>
<Link href="/guides/getting-started/tutorials/with-redwoodjs" passHref className="col-span-12 md:col-span-6 xl:col-span-4">
<GlassPanel
title="RedwoodJS"
icon="/docs/img/icons/redwood-icon"
>
Learn how to build a user management app with RedwoodJS and Supabase Database, Auth, and Storage functionality.
</GlassPanel>
</Link>
<Link href="/guides/getting-started/tutorials/with-svelte" passHref className="col-span-12 md:col-span-6 xl:col-span-4">
<GlassPanel
title="Svelte"
icon="/docs/img/icons/svelte-icon"
>
Learn how to build a user management app with Svelte and Supabase Database, Auth, and Storage functionality.
</GlassPanel>
</Link>
<Link href="/guides/getting-started/tutorials/with-sveltekit" passHref className="col-span-12 md:col-span-6 xl:col-span-4">
<GlassPanel
title="SvelteKit"
icon="/docs/img/icons/svelte-icon"
>
Learn how to build a user management app with SvelteKit and Supabase Database, Auth, and Storage functionality.
</GlassPanel>
</Link>
<Link href="/guides/getting-started/tutorials/with-refine" passHref className="col-span-12 md:col-span-6 xl:col-span-4">
<GlassPanel
title="Refine"
icon="/docs/img/icons/refine-icon"
>
Learn how to build a user management app with Refine and Supabase Database, Auth, and Storage functionality.
</GlassPanel>
</Link>
</div>
</$Show>
<$Show if="docs:mobile_tutorials">
## Mobile tutorials
<ContentListings id="getting-started-mobile-tutorials" />
<div className="grid lg:grid-cols-12 gap-6 not-prose">
<$Show if="sdk:dart">
<Link href="/guides/getting-started/tutorials/with-flutter" passHref className="col-span-12 md:col-span-6 xl:col-span-4">
<GlassPanel
title="Flutter"
icon="/docs/img/icons/flutter-icon"
>
Learn how to build a user management app with Flutter and Supabase Database, Auth, and Storage functionality.
</GlassPanel>
</Link>
</$Show>
<Link
href="/guides/getting-started/tutorials/with-expo-react-native"
passHref
className="col-span-12 md:col-span-6 xl:col-span-4"
>
<GlassPanel
title="Expo React Native"
icon="/docs/img/icons/expo-icon"
hasLightIcon={true}
>
Learn how to build a user management app with Expo React Native and Supabase Database, Auth, and Storage functionality.
</GlassPanel>
</Link>
<Link
href="/guides/auth/quickstarts/with-expo-react-native-social-auth"
passHref
className="col-span-12 md:col-span-6 xl:col-span-4"
>
<GlassPanel
title="Expo React Native Social Auth"
icon="/docs/img/icons/expo-icon"
hasLightIcon={true}
>
Learn how to implement social authentication in an app with Expo React Native and Supabase Database and Auth functionality.
</GlassPanel>
</Link>
<$Show if="sdk:kotlin">
<Link href="/guides/getting-started/tutorials/with-kotlin" passHref className="col-span-12 md:col-span-6 xl:col-span-4">
<GlassPanel
title="Android Kotlin"
icon="/docs/img/icons/kotlin-icon"
>
Learn how to build a product management app with Android and Supabase Database, Auth, and Storage functionality.
</GlassPanel>
</Link>
</$Show>
<$Show if="sdk:swift">
<Link href="/guides/getting-started/tutorials/with-swift" passHref className="col-span-12 md:col-span-6 xl:col-span-4">
<GlassPanel
title="iOS Swift"
icon="/docs/img/icons/swift-icon"
>
Learn how to build a user management app with iOS and Supabase Database, Auth, and Storage functionality.
</GlassPanel>
</Link>
</$Show>
<Link href="/guides/getting-started/tutorials/with-ionic-react" passHref className="col-span-12 md:col-span-6 xl:col-span-4">
<GlassPanel
title="Ionic React"
icon="/docs/img/icons/ionic-icon"
>
Learn how to build a user management app with Ionic React and Supabase Database, Auth, and Storage functionality.
</GlassPanel>
</Link>
<Link href="/guides/getting-started/tutorials/with-ionic-vue" passHref className="col-span-12 md:col-span-6 xl:col-span-4">
<GlassPanel
title="Ionic Vue"
icon="/docs/img/icons/ionic-icon"
>
Learn how to build a user management app with Ionic Vue and Supabase Database, Auth, and Storage functionality.
</GlassPanel>
</Link>
<Link
href="/guides/getting-started/tutorials/with-ionic-angular"
passHref
className="col-span-12 md:col-span-6 xl:col-span-4"
>
<GlassPanel
title="Ionic Angular"
icon="/docs/img/icons/ionic-icon"
>
Learn how to build a user management app with Ionic Angular and Supabase Database, Auth, and Storage functionality.
</GlassPanel>
</Link>
</div>
</$Show>
+3 -183
View File
@@ -6,188 +6,8 @@ hideToc: true
{/* <!-- vale off --> */}
<div className="flex flex-col gap-12 my-12">
<ContentListings id="resources-overview" />
<div>
<ContentListings id="resources-migrate" />
<div className="grid grid-cols-12 gap-6 not-prose">
<Link href="/guides/getting-started" className="col-span-12 md:col-span-6 xl:col-span-4" passHref>
<GlassPanel title="Examples" hasLightIcon={true} showIconBg={true}>
Official GitHub examples, curated content from the community, and more.
</GlassPanel>
</Link>
<Link
href="/guides/resources/glossary"
className="col-span-12 md:col-span-6 xl:col-span-4"
passHref
>
<GlassPanel title="Glossary" hasLightIcon={true} showIconBg={true}>
Definitions for terminology and acronyms used in the Supabase documentation.
</GlassPanel>
</Link>
</div>
</div>
<div>
<div className="max-w-xl mb-6">
## Migrate to Supabase
</div>
<div className="grid grid-cols-12 gap-6 not-prose">
<Link
href="/guides/platform/migrating-to-supabase/auth0"
className="col-span-12 md:col-span-6 xl:col-span-4"
passHref
>
<GlassPanel title="Auth0" icon="/docs/img/icons/auth0-icon" hasLightIcon={true}>
Move your auth users from Auth0 to a Supabase project.
</GlassPanel>
</Link>
<Link
href="/guides/platform/migrating-to-supabase/firebase-auth"
className="col-span-12 md:col-span-6 xl:col-span-4"
passHref
>
<GlassPanel title="Firebase Auth" icon="/docs/img/icons/firebase-icon">
Move your auth users from a Firebase project to a Supabase project.
</GlassPanel>
</Link>
<Link
href="/guides/platform/migrating-to-supabase/firestore-data"
className="col-span-12 md:col-span-6 xl:col-span-4"
passHref
>
<GlassPanel title="Firestore Data" icon="/docs/img/icons/firebase-icon">
Migrate the contents of a Firestore collection to a single Postgres table.
</GlassPanel>
</Link>
<Link
href="/guides/platform/migrating-to-supabase/firebase-storage"
className="col-span-12 md:col-span-6 xl:col-span-4"
passHref
>
<GlassPanel title="Firebase Storage" icon="/docs/img/icons/firebase-icon">
Convert your Firebase Storage files to Supabase Storage.
</GlassPanel>
</Link>
<Link
href="/guides/platform/migrating-to-supabase/heroku"
className="col-span-12 md:col-span-6 xl:col-span-4"
passHref
>
<GlassPanel title="Heroku" icon="/docs/img/icons/heroku-icon">
Migrate your Heroku Postgres database to Supabase.
</GlassPanel>
</Link>
<Link
href="/guides/platform/migrating-to-supabase/render"
className="col-span-12 md:col-span-6 xl:col-span-4"
passHref
>
<GlassPanel title="Render" icon="/docs/img/icons/render-icon">
Migrate your Render Postgres database to Supabase.
</GlassPanel>
</Link>
<Link
href="/guides/platform/migrating-to-supabase/amazon-rds"
className="col-span-12 md:col-span-6 xl:col-span-4"
passHref
>
<GlassPanel title="Amazon RDS" icon="/docs/img/icons/aws-rds-icon">
Migrate your Amazon RDS database to Supabase.
</GlassPanel>
</Link>
<Link
href="/guides/platform/migrating-to-supabase/postgres"
className="col-span-12 md:col-span-6 xl:col-span-4"
passHref
>
<GlassPanel title="Postgres" icon="/docs/img/icons/postgres-icon">
Migrate your Postgres database to Supabase.
</GlassPanel>
</Link>
<Link
href="/guides/platform/migrating-to-supabase/mysql"
className="col-span-12 md:col-span-6 xl:col-span-4"
passHref
>
<GlassPanel title="MySQL" icon="/docs/img/icons/mysql-icon">
Migrate your MySQL database to Supabase.
</GlassPanel>
</Link>
<Link
href="/guides/platform/migrating-to-supabase/mssql"
className="col-span-12 md:col-span-6 xl:col-span-4"
passHref
>
<GlassPanel title="Microsoft SQL Server" icon="/docs/img/icons/mssql-icon">
Migrate your Microsoft SQL Server database to Supabase.
</GlassPanel>
</Link>
</div>
</div>
<div>
<div className="max-w-xl mb-6">
## Postgres resources
</div>
<div className="grid grid-cols-12 gap-6 not-prose">
<Link
href="/guides/database/postgres/indexes"
className="col-span-12 md:col-span-6 xl:col-span-4"
passHref
>
<GlassPanel title="Managing Indexes" hasLightIcon={true} showIconBg={true}>
Improve query performance using various index types in Postgres.
</GlassPanel>
</Link>
<Link
href="/guides/database/postgres/cascade-deletes"
className="col-span-12 md:col-span-6 xl:col-span-4"
passHref
>
<GlassPanel title="Cascade Deletes" hasLightIcon={true} showIconBg={true}>
Understand the types of foreign key constraint deletes.
</GlassPanel>
</Link>
<Link
href="/guides/database/postgres/dropping-all-tables-in-schema"
className="col-span-12 md:col-span-6 xl:col-span-4"
passHref
>
<GlassPanel title="Drop all tables in schema" hasLightIcon={true} showIconBg={true}>
Delete all tables in a given schema.
</GlassPanel>
</Link>
<Link
href="/guides/database/postgres/first-row-in-group"
className="col-span-12 md:col-span-6 xl:col-span-4"
passHref
>
<GlassPanel title="Select first row per group" hasLightIcon={true} showIconBg={true}>
Retrieve the first row in each distinct group.
</GlassPanel>
</Link>
<Link
href="/guides/database/postgres/which-version-of-postgres"
className="col-span-12 md:col-span-6 xl:col-span-4"
passHref
>
<GlassPanel title="Print Postgres version" hasLightIcon={true} showIconBg={true}>
Find out which version of Postgres you are running.
</GlassPanel>
</Link>
</div>
</div>
{/* end of container */}
</div>
<ContentListings id="resources-postgres" />
@@ -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.',
},
],
}
+16
View File
@@ -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,
@@ -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.',
},
],
}
@@ -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)}`
+68
View File
@@ -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', () => {
+7 -1
View File
@@ -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))
}
+5
View File
@@ -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'])