+
+ )
+}
+
+export function ContentListings({ id }: { id: string }) {
+ const group = getContentListingById(id)
+ if (!group || !group.items.length) return null
+
+ return (
+
+
+
+ )
+}
diff --git a/apps/docs/components/ContentListings/index.tsx b/apps/docs/components/ContentListings/index.tsx
new file mode 100644
index 00000000000..461a4ab891b
--- /dev/null
+++ b/apps/docs/components/ContentListings/index.tsx
@@ -0,0 +1 @@
+export { ContentListings } from './ContentListings.client'
diff --git a/apps/docs/content/guides/auth.mdx b/apps/docs/content/guides/auth.mdx
index a500f15609c..fd4071f7e65 100644
--- a/apps/docs/content/guides/auth.mdx
+++ b/apps/docs/content/guides/auth.mdx
@@ -1,7 +1,7 @@
---
id: 'auth'
title: 'Auth'
-description: 'Use Supabase to Authenticate and Authorize your users.'
+description: 'Use Supabase to authenticate and authorize your users.'
subtitle: 'Use Supabase to authenticate and authorize your users.'
tocVideo: '6ow_jW4epf8'
---
@@ -27,19 +27,15 @@ Auth uses your project's Postgres database under the hood, storing user data and
Auth also enables access control to your database's automatically generated [REST API](/docs/guides/api). When using Supabase SDKs, your data requests are automatically sent with the user's Auth Token. The Auth Token scopes database access on a row-by-row level when used along with [RLS policies](/docs/guides/database/postgres/row-level-security).
+
+
<$Show if="authentication:show_providers">
<$Partial path="providers.mdx" />
$Show>
<$Show if="billing:all">
-## Pricing
-
-Charges apply to Monthly Active Users (MAU), Monthly Active Third-Party Users (Third-Party MAU), and Monthly Active SSO Users (SSO MAU) and Advanced MFA Add-ons. For a detailed breakdown of how these charges are calculated, refer to the following pages:
-
-- [Pricing MAU](/docs/guides/platform/manage-your-usage/monthly-active-users)
-- [Pricing Third-Party MAU](/docs/guides/platform/manage-your-usage/monthly-active-users-third-party)
-- [Pricing SSO MAU](/docs/guides/platform/manage-your-usage/monthly-active-users-sso)
-- [Advanced MFA - Phone](/docs/guides/platform/manage-your-usage/advanced-mfa-phone)
-
+
$Show>
+
+
diff --git a/apps/docs/content/guides/database/overview.mdx b/apps/docs/content/guides/database/overview.mdx
index 6cc01e11976..953c3bfe296 100644
--- a/apps/docs/content/guides/database/overview.mdx
+++ b/apps/docs/content/guides/database/overview.mdx
@@ -1,44 +1,19 @@
---
id: 'database'
title: 'Database'
-description: 'Every Supabase project is a full Postgres database. Learn how to connect, manage, and secure your data.'
+subtitle: 'Use Supabase to connect, manage, and secure your Postgres database.'
+description: 'Use Supabase to connect, manage, and secure your Postgres database.'
sidebar_label: 'Overview'
---
-Every Supabase project has a full [Postgres](https://www.postgresql.org/) database — not a Postgres abstraction.
+Every Supabase project gets a full [Postgres](https://www.postgresql.org/) database, not a Postgres abstraction. This database is the foundation that Auth, Storage, Realtime, and Edge Functions are built on, and Supabase manages daily database backups and offers point-in-time recovery on paid plans.
-
-
-You can work with a project's database in several ways:
+Work with your project's database in the following ways:
- Visually using the [**Table Editor**](/dashboard/project/_/editor) section of the Dashboard.
- With query syntax using the [**SQL Editor**](/dashboard/project/_/sql) section of the Dashboard.
-- Programmatically using a variety of methods.
+- Programmatically using a variety of different methods.
-Read the [Connect to your database](/docs/guides/database/connecting-to-postgres#how-to-connect-to-your-postgres-databases) guide for details on connection strings and options.
+
-
-
-The database is the foundation that Auth, Storage, Realtime, and Edge Functions are built on, and Supabase manages daily database backups and offers point-in-time recovery on paid plans.
-
-## Get started
-
-If you're new to the database section, these are the pages to read first:
-
-- **[Connect to your database](/docs/guides/database/connecting-to-postgres)**: Connection strings, the Supavisor connection pooler, and when to use direct, transaction, or session mode.
-- **[Tables and data](/docs/guides/database/tables)**: Create tables and relationships, and edit rows from the Dashboard.
-- **[Import data](/docs/guides/database/import-data)**: Load existing data from CSV files, `pg_dump`, or another Postgres database.
-- **[Secure your data](/docs/guides/database/secure-data)**: Row Level Security (RLS) is how Supabase makes the database safe to query directly from the client. Read this before exposing any table to your app.
-- **[Extensions](/docs/guides/database/extensions)**: Enable Postgres extensions — including `pgvector` for embeddings, `PostGIS` for geospatial data, and `pg_cron` for scheduled jobs — from the Dashboard.
-- **[Run SQL commands](/dashboard/project/_/sql)**: Use the Dashboard's SQL Editor for ad-hoc queries and saved snippets.
-
-## Going further
-
-Once you've covered the basics, these guides help with other use cases and features:
-
-- **[Database functions](/docs/guides/database/functions)** and **[triggers](/docs/guides/database/postgres/triggers)**: Run logic inside the database in response to inserts, updates, or deletes.
-- **[Database webhooks](/docs/guides/database/webhooks)**: Send row changes to an external HTTP endpoint.
-- **[Replication and read replicas](/docs/guides/database/replication)**: Stream changes to other systems or read from a geographically closer replica.
-- **[Backups](/docs/guides/platform/backups)**: Daily backups on every project, with point-in-time recovery on paid plans. Backups cover the database itself; objects stored through the Storage API are not included.
-- **[Query performance and optimization](/docs/guides/database/query-optimization)**: Indexes, the query planner, and tools for finding slow queries.
-- **[Roles and permissions](/docs/guides/database/postgres/roles)**: The Postgres roles Supabase ships with and how to add your own.
+
diff --git a/apps/docs/content/guides/functions.mdx b/apps/docs/content/guides/functions.mdx
index 87d1b7f42e4..2982243fa10 100644
--- a/apps/docs/content/guides/functions.mdx
+++ b/apps/docs/content/guides/functions.mdx
@@ -1,8 +1,8 @@
---
id: 'functions'
title: 'Edge Functions'
-description: 'Globally distributed TypeScript functions.'
-subtitle: 'Globally distributed TypeScript functions.'
+description: 'Run TypeScript functions globally at the edge.'
+subtitle: 'Run TypeScript functions globally at the edge.'
sidebar_label: 'Overview'
tocVideo: 'za_loEtS4gs'
---
@@ -41,295 +41,18 @@ Edge Functions are server-side TypeScript functions, distributed globally at the
- Sending transactional emails.
- Building messaging bots for Slack, Discord, etc.
-
-
-
+
## Examples
-Check out the [Edge Function Examples](https://github.com/supabase/supabase/tree/master/examples/edge-functions) in our GitHub repository.
+Check out [Supabase Edge Function Examples](https://github.com/supabase/supabase/tree/master/examples/edge-functions) in GitHub or try these examples:
-
-
-
-
- Use the Supabase client inside your Edge Function.
-
-
-
-
-
-
- Combining Kysely with Deno Postgres gives you a convenient developer experience for
- interacting directly with your Postgres database.
-
-
-
-
-
-
- Monitor Edge Functions with the Sentry Deno SDK.
-
-
-
-
-
-
- Send CORS headers for invoking from the browser.
-
-
-
-
-
-
- Full example for using Supabase and Stripe, with Expo.
-
-
-
-
-
-
- Full example for using Supabase and Stripe, with Flutter.
-
-
-
-
-
-
- Learn how to use HTTP methods and paths to build a RESTful service for managing tasks.
-
-
-
-
-
-
- An example on reading a file from Supabase Storage.
-
-
-
-
-
-
- Generate Open Graph images with Deno and Supabase Edge Functions.
-
-
-
-
-
- Slack Bot handling Slack mentions in Edge Function
-
-
-
-
+
+
+
+
+
+
+
+
+
diff --git a/apps/docs/content/guides/getting-started.mdx b/apps/docs/content/guides/getting-started.mdx
index 45a0a7892cc..f0ff033351a 100644
--- a/apps/docs/content/guides/getting-started.mdx
+++ b/apps/docs/content/guides/getting-started.mdx
@@ -5,36 +5,7 @@ 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
diff --git a/apps/docs/content/guides/realtime.mdx b/apps/docs/content/guides/realtime.mdx
index cd79fccadff..8352d90659b 100644
--- a/apps/docs/content/guides/realtime.mdx
+++ b/apps/docs/content/guides/realtime.mdx
@@ -20,60 +20,8 @@ Supabase provides a globally distributed [Realtime](https://github.com/supabase/
- **Multiplayer games** - Synchronized game state and player interactions
- **Social features** - Live notifications, reactions, and user activity feeds
-Check the [Getting Started](/docs/guides/realtime/getting_started) guide to get started.
+
-## Examples
+
-
-
-
-
- Showcase application displaying cursor movements and chat messages using Broadcast.
-
-
-
-
-
-
- Supabase UI chat component using Broadcast to send message between users.
-
-
-
-
-
-
- Supabase UI avatar stack component using Presence to track connected users.
-
-
-
-
-
-
- Supabase UI realtime cursor component using Broadcast to share users' cursors to build
- collaborative applications.
-
-
-
-
-
-## Resources
-
-Find the source code and documentation in the Supabase GitHub repository.
-
-
-
-
- View the source code.
-
-
-
-
-
- Read more about Supabase Realtime.
-
-
-
-
+
diff --git a/apps/docs/content/guides/storage.mdx b/apps/docs/content/guides/storage.mdx
index 81643f11439..a918edf83a1 100644
--- a/apps/docs/content/guides/storage.mdx
+++ b/apps/docs/content/guides/storage.mdx
@@ -17,91 +17,8 @@ Supabase Storage is a robust, scalable solution for managing files of any size w
- **Fine-grained Access Control** - Manage file permissions with row-level security and custom policies
- **Multiple Bucket Types** - Specialized storage solutions for different use cases
-## Storage bucket types
+
-Supabase Storage offers different bucket types optimized for specific use cases:
+
-### Files buckets
-
-Store and serve traditional files including images, videos, documents, and general-purpose content. Ideal for user-generated content, media libraries, and asset management.
-
-**Use cases:** Images, videos, documents, PDFs, archives
-
-**Features:**
-
-- Global CDN delivery
-- Image optimization and transformation
-- Row-level security integration
-- Direct URL access for files
-
-[Learn more about Files Buckets](/docs/guides/storage/quickstart)
-
-### Analytics buckets
-
-Purpose-built for storing and analyzing data in open table formats like Apache Iceberg. Perfect for time-series data, logs, and large-scale analytical workloads.
-
-**Use cases:** Data lakes, analytics pipelines, ETL operations, historical data analysis
-
-**Features:**
-
-- Apache Iceberg table format support
-- SQL-accessible via Postgres foreign tables
-- Partitioned data organization
-- Efficient data querying and transformation
-
-[Learn more about Analytics Buckets](/docs/guides/storage/analytics/introduction)
-
-### Vector buckets
-
-Specialized storage for vector embeddings and similarity search operations. Designed for AI and ML applications requiring semantic search capabilities.
-
-**Use cases:** AI-powered search, semantic similarity matching, embedding storage, RAG systems
-
-**Features:**
-
-- Optimized vector indexing (HNSW, Flat)
-- Multiple distance metrics (cosine, euclidean, L2)
-- Metadata filtering for vectors
-- Similarity search queries
-
-[Learn more about Vector Buckets](/docs/guides/storage/vector/introduction)
-
-## Examples
-
-Check out all of the Storage [templates and examples](https://github.com/supabase/supabase/tree/master/examples/storage) in our GitHub repository.
-
-
-
-
-
- Use Uppy to upload files to Supabase Storage using the TUS protocol (resumable uploads).
-
-
-
-
-
-## Resources
-
-Find the source code and documentation in the Supabase GitHub repository.
-
-
-
-
- View the source code.
-
-
-
-
-
- See the Swagger Documentation for Supabase Storage.
-
-
-
-
+
diff --git a/apps/docs/data/content-listings/auth.data.ts b/apps/docs/data/content-listings/auth.data.ts
new file mode 100644
index 00000000000..e70d1cd5836
--- /dev/null
+++ b/apps/docs/data/content-listings/auth.data.ts
@@ -0,0 +1,102 @@
+import type { ContentListingGroup } from '~/lib/content-listings.schema'
+
+export const authGetStarted: ContentListingGroup = {
+ id: 'auth-get-started',
+ heading: 'Get started',
+ description: "Start here if you're new to Supabase Auth:",
+ type: 'grid',
+ items: [
+ {
+ title: 'Auth with email and password',
+ href: '/guides/auth/passwords',
+ description: 'Sign up and sign in users with email and password.',
+ },
+ {
+ title: 'Server-side rendering',
+ href: '/guides/auth/server-side',
+ description: 'Create a Supabase client for SSR frameworks like Next.js and SvelteKit.',
+ },
+ {
+ title: 'Row Level Security',
+ href: '/guides/database/postgres/row-level-security',
+ description: 'Use RLS policies to authorize data access from the client.',
+ },
+ ],
+}
+
+export const authPricing: ContentListingGroup = {
+ id: 'auth-pricing',
+ heading: 'Pricing',
+ description:
+ 'Charges apply to Monthly Active Users (MAU), Monthly Active Third-Party Users (Third-Party MAU), and Monthly Active SSO Users (SSO MAU) and Advanced MFA Add-ons. For a detailed breakdown of how these charges are calculated, refer to the following pages.',
+ type: 'list',
+ items: [
+ {
+ title: 'Pricing MAU',
+ href: '/guides/platform/manage-your-usage/monthly-active-users',
+ description: 'How MAU usage is measured and billed.',
+ },
+ {
+ title: 'Pricing Third-Party MAU',
+ href: '/guides/platform/manage-your-usage/monthly-active-users-third-party',
+ description: 'How third-party auth MAU is measured and billed.',
+ },
+ {
+ title: 'Pricing SSO MAU',
+ href: '/guides/platform/manage-your-usage/monthly-active-users-sso',
+ description: 'How SSO MAU usage is measured and billed.',
+ },
+ {
+ title: 'Advanced MFA - Phone',
+ href: '/guides/platform/manage-your-usage/advanced-mfa-phone',
+ description: 'How Advanced MFA Phone add-on usage is measured and billed.',
+ },
+ ],
+}
+
+export const authNextSteps: ContentListingGroup = {
+ id: 'auth-next-steps',
+ heading: 'Next steps',
+ description:
+ "Once you've covered the basics, these guides help with other use cases and features:",
+ type: 'grid',
+ columns: 4,
+ items: [
+ {
+ title: 'Email (Magic link or OTP)',
+ href: '/guides/auth/auth-email-passwordless',
+ description:
+ 'Sign up and sign in users with a Magic Link or email OTP instead of a password.',
+ },
+ {
+ title: 'Enterprise SSO',
+ href: '/guides/auth/enterprise-sso',
+ description: 'Add Single Sign-On for enterprise applications with SAML 2.0.',
+ },
+ {
+ title: 'User sessions',
+ href: '/guides/auth/sessions',
+ description: 'Control session lifetime, refresh tokens, and multi-device sign-in behavior.',
+ },
+ {
+ title: 'Third-party auth',
+ href: '/guides/auth/third-party/overview',
+ description: 'Use Clerk, Auth0, Firebase Auth, Cognito, or WorkOS JWTs with Supabase APIs.',
+ },
+ {
+ title: 'Multi-factor authentication',
+ href: '/guides/auth/auth-mfa',
+ description: 'Add a second factor to user sign-in with TOTP or phone.',
+ },
+ {
+ title: 'JWTs',
+ href: '/guides/auth/jwts',
+ description: 'Understand how Supabase Auth issues and validates JWTs.',
+ },
+ {
+ title: 'Auth Hooks',
+ href: '/guides/auth/auth-hooks',
+ description: 'Customize Auth behavior with Postgres functions at key lifecycle points.',
+ },
+ ],
+}
diff --git a/apps/docs/data/content-listings/database.data.ts b/apps/docs/data/content-listings/database.data.ts
new file mode 100644
index 00000000000..32961aed8fb
--- /dev/null
+++ b/apps/docs/data/content-listings/database.data.ts
@@ -0,0 +1,90 @@
+import type { ContentListingGroup } from '~/lib/content-listings.schema'
+
+export const databaseGetStarted: ContentListingGroup = {
+ id: 'database-get-started',
+ heading: 'Get started',
+ description: "If you're new to the database section, these are the pages to read first:",
+ type: 'grid',
+ columns: 2,
+ items: [
+ {
+ title: 'Connect to your database',
+ href: '/guides/database/connecting-to-postgres',
+ description:
+ 'Connection strings, the Supavisor connection pooler, and when to use direct, transaction, or session mode.',
+ },
+ {
+ title: 'Tables and data',
+ href: '/guides/database/tables',
+ description: 'Create tables and relationships, and edit rows from the Dashboard.',
+ },
+ {
+ title: 'Import data',
+ href: '/guides/database/import-data',
+ description: 'Load existing data from CSV files, `pg_dump`, or another Postgres database.',
+ },
+ {
+ title: 'Secure your data',
+ href: '/guides/database/secure-data',
+ description:
+ 'Row Level Security (RLS) is how Supabase makes the database safe to query directly from the client. Read this before exposing any table to your app.',
+ },
+ {
+ title: 'Extensions',
+ href: '/guides/database/extensions',
+ description:
+ 'Add Postgres extensions from the Dashboard, including `pgvector` for embeddings, `PostGIS` for geospatial data, and `pg_cron` for scheduled jobs.',
+ },
+ {
+ title: 'Run SQL commands',
+ href: '/dashboard/project/_/sql',
+ description: "Use the Dashboard's SQL Editor for ad-hoc queries and saved snippets.",
+ },
+ ],
+}
+
+export const databaseNextSteps: ContentListingGroup = {
+ id: 'database-next-steps',
+ heading: 'Next steps',
+ description:
+ "Once you've covered the basics, these guides help with other use cases and features:",
+ type: 'grid',
+ items: [
+ {
+ title: 'Database functions',
+ href: '/guides/database/functions',
+ description: 'Run logic inside the database in response to inserts, updates, or deletes.',
+ },
+ {
+ title: 'Triggers',
+ href: '/guides/database/postgres/triggers',
+ description: 'Run logic inside the database in response to inserts, updates, or deletes.',
+ },
+ {
+ title: 'Database webhooks',
+ href: '/guides/database/webhooks',
+ description: 'Send row changes to an external HTTP endpoint.',
+ },
+ {
+ title: 'Replication and read replicas',
+ href: '/guides/database/replication',
+ description: 'Stream changes to other systems or read from a geographically closer replica.',
+ },
+ {
+ title: 'Backups',
+ href: '/guides/platform/backups',
+ description:
+ 'Daily backups on every project, with point-in-time recovery on paid plans. Backups cover the database itself; objects stored through the Storage API are not included.',
+ },
+ {
+ title: 'Query performance and optimization',
+ href: '/guides/database/query-optimization',
+ description: 'Indexes, the query planner, and tools for finding slow queries.',
+ },
+ {
+ title: 'Roles and permissions',
+ href: '/guides/database/postgres/roles',
+ description: 'The Postgres roles Supabase ships with and how to add your own.',
+ },
+ ],
+}
diff --git a/apps/docs/data/content-listings/functions.data.ts b/apps/docs/data/content-listings/functions.data.ts
new file mode 100644
index 00000000000..bb69d870704
--- /dev/null
+++ b/apps/docs/data/content-listings/functions.data.ts
@@ -0,0 +1,211 @@
+import type { ContentListingGroup } from '~/lib/content-listings.schema'
+
+export const functionsGetStarted: ContentListingGroup = {
+ id: 'functions-get-started',
+ heading: 'Get started',
+ type: 'grid',
+ columns: 2,
+ items: [
+ {
+ title: 'Edge Functions quickstart',
+ href: '/guides/functions/quickstart',
+ description: 'Create, test, and deploy your first Edge Function with the Supabase CLI.',
+ },
+ ],
+}
+
+export const functionsExamplesSupabase: ContentListingGroup = {
+ id: 'functions-examples-supabase',
+ heading: 'Supabase integration',
+ headingLevel: 'h3',
+ type: 'grid',
+ items: [
+ {
+ title: 'With supabase-js',
+ href: '/guides/functions/auth',
+ description: 'Use the Supabase client inside your Edge Function.',
+ },
+ {
+ title: 'Connect to Postgres',
+ href: '/guides/functions/connect-to-postgres',
+ description: 'Connect to Postgres from Edge Functions.',
+ },
+ {
+ title: 'Type-Safe SQL with Kysely',
+ href: '/guides/functions/kysely-postgres',
+ description:
+ 'Combine Kysely with Deno Postgres for a convenient developer experience when interacting directly with your Postgres database.',
+ },
+ {
+ title: 'With CORS headers',
+ href: '/guides/functions/cors',
+ description: 'Send CORS headers for invoking from the browser.',
+ },
+ {
+ title: 'Building a RESTful Service API',
+ href: 'https://github.com/supabase/supabase/blob/master/examples/edge-functions/supabase/functions/restful-tasks/index.ts',
+ icon: '/docs/img/icons/github-icon',
+ description:
+ 'Learn how to use HTTP methods and paths to build a RESTful service for managing tasks.',
+ },
+ {
+ title: 'Oak Server Middleware',
+ href: 'https://github.com/supabase/supabase/tree/master/examples/edge-functions/supabase/functions/oak-server',
+ icon: '/docs/img/icons/github-icon',
+ description: 'Route requests with Oak server middleware.',
+ },
+ {
+ title: 'Web Stream',
+ href: 'https://github.com/supabase/supabase/tree/master/examples/edge-functions/supabase/functions/streams',
+ icon: '/docs/img/icons/github-icon',
+ description: 'Stream Server-Sent Events from Edge Functions.',
+ },
+ {
+ title: 'Get User Location',
+ href: 'https://github.com/supabase/supabase/tree/master/examples/edge-functions/supabase/functions/location',
+ icon: '/docs/img/icons/github-icon',
+ description: "Get user location data from user's IP address.",
+ },
+ {
+ title: 'Working with Supabase Storage',
+ href: 'https://github.com/supabase/supabase/blob/master/examples/edge-functions/supabase/functions/read-storage/index.ts',
+ icon: '/docs/img/icons/github-icon',
+ description: 'Read a file from Supabase Storage.',
+ },
+ {
+ title: 'Upload File',
+ href: 'https://github.com/supabase/supabase/tree/master/examples/edge-functions/supabase/functions/file-upload-storage',
+ icon: '/docs/img/icons/github-icon',
+ description: 'Process multipart/form-data.',
+ },
+ ],
+}
+
+export const functionsExamplesWebhooksPayments: ContentListingGroup = {
+ id: 'functions-examples-webhooks-payments',
+ heading: 'Webhooks & payments',
+ headingLevel: 'h3',
+ type: 'grid',
+ items: [
+ {
+ title: 'Stripe Webhooks',
+ href: '/guides/functions/examples/stripe-webhooks',
+ description: 'Handle signed Stripe webhooks with Edge Functions.',
+ },
+ {
+ title: 'React Native with Stripe',
+ href: 'https://github.com/supabase-community/expo-stripe-payments-with-supabase-functions',
+ icon: '/docs/img/icons/github-icon',
+ description: 'Use Supabase and Stripe in a React Native app with Expo.',
+ },
+ {
+ title: 'Flutter with Stripe',
+ href: 'https://github.com/supabase-community/flutter-stripe-payments-with-supabase-functions',
+ icon: '/docs/img/icons/github-icon',
+ description: 'Use Supabase and Stripe in a Flutter app.',
+ },
+ ],
+}
+
+export const functionsExamplesAiMedia: ContentListingGroup = {
+ id: 'functions-examples-ai-media',
+ heading: 'AI & media',
+ headingLevel: 'h3',
+ type: 'grid',
+ items: [
+ {
+ title: 'Hugging Face',
+ href: '/guides/ai/examples/huggingface-image-captioning',
+ description: 'Access 100,000+ Machine Learning models.',
+ },
+ {
+ title: 'OpenAI',
+ href: '/guides/ai/examples/openai',
+ description: 'Use OpenAI in Edge Functions.',
+ },
+ {
+ title: 'Amazon Bedrock',
+ href: '/guides/functions/examples/amazon-bedrock-image-generator',
+ description: 'Generate images with Amazon Bedrock in Edge Functions.',
+ },
+ {
+ title: 'Open Graph Image Generation',
+ href: '/guides/functions/examples/og-image',
+ description: 'Generate Open Graph images with Deno and Supabase Edge Functions.',
+ },
+ {
+ title: 'OG Image Generation & Storage CDN Caching',
+ href: 'https://github.com/supabase/supabase/tree/master/examples/edge-functions/supabase/functions/og-image-with-storage-cdn',
+ icon: '/docs/img/icons/github-icon',
+ description: 'Cache generated images with Supabase Storage CDN.',
+ },
+ {
+ title: 'Puppeteer',
+ href: '/guides/functions/examples/screenshots',
+ description: 'Generate screenshots with Puppeteer.',
+ },
+ ],
+}
+
+export const functionsExamplesMessaging: ContentListingGroup = {
+ id: 'functions-examples-messaging',
+ heading: 'Bots & email',
+ headingLevel: 'h3',
+ type: 'grid',
+ items: [
+ {
+ title: 'Send emails',
+ href: '/guides/functions/examples/send-emails',
+ description: 'Send emails in Edge Functions with Resend.',
+ },
+ {
+ title: 'Discord Bot',
+ href: '/guides/functions/examples/discord-bot',
+ description: 'Build a slash command Discord bot with Edge Functions.',
+ },
+ {
+ title: 'Telegram Bot',
+ href: '/guides/functions/examples/telegram-bot',
+ description: 'Build a Telegram bot with Edge Functions.',
+ },
+ {
+ title: 'Slack Bot Mention Edge Function',
+ href: '/guides/functions/examples/slack-bot-mention',
+ description: 'Handle Slack mentions in a Slack bot Edge Function.',
+ },
+ ],
+}
+
+export const functionsExamplesOperations: ContentListingGroup = {
+ id: 'functions-examples-operations',
+ heading: 'Operations & security',
+ headingLevel: 'h3',
+ type: 'grid',
+ items: [
+ {
+ title: 'Monitoring with Sentry',
+ href: '/guides/functions/examples/sentry-monitoring',
+ description: 'Monitor Edge Functions with the Sentry Deno SDK.',
+ },
+ {
+ title: 'GitHub Actions',
+ href: '/guides/functions/examples/github-actions',
+ description: 'Deploy Edge Functions with GitHub Actions.',
+ },
+ {
+ title: 'Upstash Redis',
+ href: '/guides/functions/examples/upstash-redis',
+ description: 'Build an Edge Functions Counter with Upstash Redis.',
+ },
+ {
+ title: 'Rate Limiting',
+ href: '/guides/functions/examples/rate-limiting',
+ description: 'Rate-limit Edge Functions with Upstash Redis.',
+ },
+ {
+ title: 'Cloudflare Turnstile',
+ href: '/guides/functions/examples/cloudflare-turnstile',
+ description: 'Protect forms with Cloudflare Turnstile.',
+ },
+ ],
+}
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..33d36e08e8b
--- /dev/null
+++ b/apps/docs/data/content-listings/getting-started.data.ts
@@ -0,0 +1,23 @@
+import type { ContentListingGroup } from '~/lib/content-listings.schema'
+
+export const gettingStartedGetStarted: 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.',
+ },
+ ],
+}
diff --git a/apps/docs/data/content-listings/index.ts b/apps/docs/data/content-listings/index.ts
new file mode 100644
index 00000000000..b5ac8a84d09
--- /dev/null
+++ b/apps/docs/data/content-listings/index.ts
@@ -0,0 +1,40 @@
+import type { ContentListingGroup } from '~/lib/content-listings.schema'
+
+import { authGetStarted, authNextSteps, authPricing } from './auth.data'
+import { databaseGetStarted, databaseNextSteps } from './database.data'
+import {
+ functionsExamplesAiMedia,
+ functionsExamplesMessaging,
+ functionsExamplesOperations,
+ functionsExamplesSupabase,
+ functionsExamplesWebhooksPayments,
+ functionsGetStarted,
+} from './functions.data'
+import { gettingStartedGetStarted } from './getting-started.data'
+import { realtimeExamples, realtimeGetStarted, realtimeResources } from './realtime.data'
+import { storageExamples, storageGetStarted, storageResources } from './storage.data'
+
+const ALL_GROUPS: readonly ContentListingGroup[] = [
+ authGetStarted,
+ authPricing,
+ authNextSteps,
+ databaseGetStarted,
+ databaseNextSteps,
+ functionsGetStarted,
+ functionsExamplesSupabase,
+ functionsExamplesWebhooksPayments,
+ functionsExamplesAiMedia,
+ functionsExamplesMessaging,
+ functionsExamplesOperations,
+ gettingStartedGetStarted,
+ realtimeGetStarted,
+ realtimeExamples,
+ realtimeResources,
+ storageGetStarted,
+ storageExamples,
+ storageResources,
+]
+
+export const CONTENT_LISTINGS: Readonly> = Object.fromEntries(
+ ALL_GROUPS.map((group) => [group.id, group])
+)
diff --git a/apps/docs/data/content-listings/realtime.data.ts b/apps/docs/data/content-listings/realtime.data.ts
new file mode 100644
index 00000000000..0e0e85b2270
--- /dev/null
+++ b/apps/docs/data/content-listings/realtime.data.ts
@@ -0,0 +1,66 @@
+import type { ContentListingGroup } from '~/lib/content-listings.schema'
+
+export const realtimeGetStarted: ContentListingGroup = {
+ id: 'realtime-get-started',
+ heading: 'Get started',
+ type: 'grid',
+ columns: 2,
+ items: [
+ {
+ title: 'Getting Started',
+ href: '/guides/realtime/getting_started',
+ description: 'Set up Realtime in your project and send your first message.',
+ },
+ ],
+}
+
+export const realtimeExamples: ContentListingGroup = {
+ id: 'realtime-examples',
+ heading: 'Examples',
+ type: 'grid',
+ columns: 2,
+ items: [
+ {
+ title: 'Multiplayer.dev',
+ href: 'https://multiplayer.dev',
+ description:
+ 'Showcase application displaying cursor movements and chat messages using Broadcast.',
+ },
+ {
+ title: 'Chat',
+ href: 'https://supabase.com/ui/docs/nextjs/realtime-chat',
+ description: 'Supabase UI chat component using Broadcast to send message between users.',
+ },
+ {
+ title: 'Avatar Stack',
+ href: 'https://supabase.com/ui/docs/nextjs/realtime-avatar-stack',
+ description: 'Supabase UI avatar stack component using Presence to track connected users.',
+ },
+ {
+ title: 'Realtime Cursor',
+ href: 'https://supabase.com/ui/docs/nextjs/realtime-cursor',
+ description:
+ "Supabase UI realtime cursor component using Broadcast to share users' cursors to build collaborative applications.",
+ },
+ ],
+}
+
+export const realtimeResources: ContentListingGroup = {
+ id: 'realtime-resources',
+ heading: 'Resources',
+ description: 'Find the source code and documentation in the Supabase GitHub repository:',
+ type: 'grid',
+ columns: 2,
+ items: [
+ {
+ title: 'Supabase Realtime',
+ href: 'https://github.com/supabase/realtime',
+ description: 'View the source code.',
+ },
+ {
+ title: 'Realtime: Multiplayer Edition',
+ href: 'https://supabase.com/blog/supabase-realtime-multiplayer-general-availability',
+ description: 'Read more about Supabase Realtime.',
+ },
+ ],
+}
diff --git a/apps/docs/data/content-listings/storage.data.ts b/apps/docs/data/content-listings/storage.data.ts
new file mode 100644
index 00000000000..9180ea08d49
--- /dev/null
+++ b/apps/docs/data/content-listings/storage.data.ts
@@ -0,0 +1,72 @@
+import type { ContentListingGroup } from '~/lib/content-listings.schema'
+
+export const storageGetStarted: ContentListingGroup = {
+ id: 'storage-get-started',
+ heading: 'Get started',
+ description: 'Choose the bucket type that fits your use case:',
+ type: 'grid',
+ items: [
+ {
+ title: 'Files buckets',
+ href: '/guides/storage/quickstart',
+ description:
+ 'Store and serve images, videos, documents, and general-purpose files with direct URL access and row-level security.',
+ },
+ {
+ title: 'Analytics buckets',
+ href: '/guides/storage/analytics/introduction',
+ description:
+ 'Store data in Apache Iceberg tables for data lakes, logs, and Supabase Pipelines. Query from Postgres via foreign tables with partitioning.',
+ },
+ {
+ title: 'Vector buckets',
+ href: '/guides/storage/vector/introduction',
+ description:
+ 'Store embeddings and run similarity search for semantic matching, AI, and RAG. Use HNSW indexing, distance metrics, and metadata filtering.',
+ },
+ ],
+}
+
+export const storageExamples: ContentListingGroup = {
+ id: 'storage-examples',
+ heading: 'Examples',
+ description: 'Working sample projects for common Storage integration patterns:',
+ type: 'grid',
+ columns: 2,
+ items: [
+ {
+ title: 'Storage templates and examples',
+ href: 'https://github.com/supabase/supabase/tree/master/examples/storage',
+ description:
+ 'Sample projects for resumable uploads, signed upload URLs, and serving map tiles from private buckets.',
+ },
+ {
+ title: 'Resumable Uploads with Uppy',
+ href: 'https://github.com/supabase/supabase/tree/master/examples/storage/resumable-upload-uppy',
+ icon: '/docs/img/icons/github-icon',
+ description:
+ 'Upload large files with pause-and-resume support using Uppy and the TUS protocol.',
+ },
+ ],
+}
+
+export const storageResources: ContentListingGroup = {
+ id: 'storage-resources',
+ heading: 'Resources',
+ description: 'Source code and REST API reference for the Storage service:',
+ type: 'grid',
+ columns: 2,
+ items: [
+ {
+ title: 'Supabase Storage API',
+ href: 'https://github.com/supabase/storage-api',
+ description: 'Amazon S3-compatible object storage service that stores metadata in Postgres.',
+ },
+ {
+ title: 'OpenAPI Spec',
+ href: 'https://supabase.github.io/storage/',
+ description:
+ 'Interactive reference for Storage REST endpoints, request parameters, and response schemas.',
+ },
+ ],
+}
diff --git a/apps/docs/features/docs/MdxBase.shared.tsx b/apps/docs/features/docs/MdxBase.shared.tsx
index b47ebf114e8..9df8413a927 100644
--- a/apps/docs/features/docs/MdxBase.shared.tsx
+++ b/apps/docs/features/docs/MdxBase.shared.tsx
@@ -5,6 +5,7 @@ import AuthProviders from '~/components/AuthProviders'
import { AuthSmsProviderConfig } from '~/components/AuthSmsProviderConfig'
import ButtonCard from '~/components/ButtonCard'
import { ComputeDiskLimitsTable } from '~/components/ComputeDiskLimitsTable'
+import { ContentListings } from '~/components/ContentListings'
import { Extensions } from '~/components/Extensions'
import Image, { type ImageProps } from '~/components/Image'
import { Mermaid } from '~/components/Mermaid'
@@ -26,6 +27,7 @@ import { ShowUntil } from '~/features/ui/ShowUntil'
import { TabPanel, Tabs } from '~/features/ui/Tabs'
import { ArrowDown, Check, X } from 'lucide-react'
import Link from 'next/link'
+import { type ComponentPropsWithoutRef } from 'react'
import { Badge, Button } from 'ui'
import { Admonition, type AdmonitionProps } from 'ui-patterns/admonition'
import { GlassPanel } from 'ui-patterns/GlassPanel'
@@ -74,6 +76,7 @@ const components = {
CodeSampleDummy,
CodeSampleWrapper,
ComputeDiskLimitsTable,
+ ContentListings,
ErrorCodes,
Extensions,
GlassPanel,
@@ -100,17 +103,17 @@ const components = {
TabPanel,
InfoTooltip,
a: MdxAnchor,
- h2: (props: any) => (
+ h2: (props: ComponentPropsWithoutRef<'h2'>) => (
{props.children}
),
- h3: (props: any) => (
+ h3: (props: ComponentPropsWithoutRef<'h3'>) => (
{props.children}
),
- h4: (props: any) => (
+ h4: (props: ComponentPropsWithoutRef<'h4'>) => (
{props.children}
diff --git a/apps/docs/internals/generate-guides-markdown.ts b/apps/docs/internals/generate-guides-markdown.ts
index f2a378915b0..462c0c3d969 100644
--- a/apps/docs/internals/generate-guides-markdown.ts
+++ b/apps/docs/internals/generate-guides-markdown.ts
@@ -17,6 +17,7 @@ import { AuthProviders } from './markdown-schema/AuthProviders'
import { ComputeDiskLimitsTable } from './markdown-schema/ComputeDiskLimitsTable'
import { ErrorCodes } from './markdown-schema/ErrorCodes'
import { Link } from './markdown-schema/Link'
+import { ContentListings } from './markdown-schema/ContentListings'
import { MetricsStackCards } from './markdown-schema/MetricsStackCards'
import { NavData } from './markdown-schema/NavData'
import { Panel } from './markdown-schema/Panel'
@@ -152,6 +153,7 @@ const SCHEMA: ComponentSchema = {
...StepHike,
TabPanel,
MetricsStackCards,
+ ContentListings,
NavData,
SharedData,
}
@@ -178,7 +180,9 @@ async function transformBody(content: string, data: Record): Pr
const header = headerParts.join('\n\n')
- return header ? `${header}\n\n${body}` : body
+ let output = header ? `${header}\n\n${body}` : body
+
+ return output
}
async function generateOne(sourceFile: string, frontmatter: FrontmatterFormat): Promise {
diff --git a/apps/docs/internals/markdown-schema/ContentListings.ts b/apps/docs/internals/markdown-schema/ContentListings.ts
new file mode 100644
index 00000000000..299b23c06ec
--- /dev/null
+++ b/apps/docs/internals/markdown-schema/ContentListings.ts
@@ -0,0 +1,51 @@
+import { withDocsBasePath } from '~/internals/internal-links'
+import type { ContentListingGroup } from '~/lib/content-listings.schema'
+import { getContentListingById, isExternalContentListingHref } from '~/lib/content-listings.utils'
+
+import { getInternalLinkBaseUrl } from '../internal-links'
+
+const HEADING_MARKDOWN: Record<'h2' | 'h3' | 'h4', string> = {
+ h2: '##',
+ h3: '###',
+ h4: '####',
+}
+
+export function serializeContentListingGroupToMarkdown(
+ group: ContentListingGroup,
+ linkBaseUrl: string
+): string {
+ const lines: string[] = []
+ if (group.heading) {
+ const level = group.headingLevel ?? 'h2'
+ lines.push(`${HEADING_MARKDOWN[level]} ${group.heading}`)
+ lines.push('')
+ }
+
+ if (group.description) {
+ lines.push(group.description)
+ lines.push('')
+ }
+
+ for (const item of group.items) {
+ const href = isExternalContentListingHref(item.href)
+ ? item.href
+ : `${linkBaseUrl}${withDocsBasePath(item.href)}`
+ lines.push(`- **[${item.title}](${href}):** ${item.description}`)
+ }
+
+ return lines.join('\n')
+}
+
+/**
+ * Markdown export handler for ``. Looks up the
+ * group by id in the same data registry the React component uses.
+ */
+export const ContentListings = ({ props }: { props: Record }): string => {
+ const id = typeof props.id === 'string' ? props.id : ''
+ if (!id) return ''
+
+ const group = getContentListingById(id)
+ if (!group) return ''
+
+ return serializeContentListingGroupToMarkdown(group, getInternalLinkBaseUrl())
+}
diff --git a/apps/docs/lib/content-listings.schema.ts b/apps/docs/lib/content-listings.schema.ts
new file mode 100644
index 00000000000..df48028bf00
--- /dev/null
+++ b/apps/docs/lib/content-listings.schema.ts
@@ -0,0 +1,20 @@
+import { z } from 'zod'
+
+import {
+ contentListingGridColumnsSchema,
+ contentListingGroupSchema,
+ contentListingGroupTypeSchema,
+ contentListingHeadingLevelSchema,
+ contentListingItemSchema,
+} from './content-listings.zod.mjs'
+
+export {
+ contentListingGridColumnsSchema,
+ contentListingGroupSchema,
+ contentListingGroupTypeSchema,
+ contentListingHeadingLevelSchema,
+ contentListingItemSchema,
+}
+
+export type ContentListingItem = z.infer
+export type ContentListingGroup = z.infer
diff --git a/apps/docs/lib/content-listings.test.ts b/apps/docs/lib/content-listings.test.ts
new file mode 100644
index 00000000000..61a067ad726
--- /dev/null
+++ b/apps/docs/lib/content-listings.test.ts
@@ -0,0 +1,166 @@
+import { storageGetStarted } from '~/data/content-listings/storage.data'
+import {
+ ContentListings as ContentListingsMarkdownHandler,
+ serializeContentListingGroupToMarkdown,
+} from '~/internals/markdown-schema/ContentListings'
+import { isExternalContentListingHref } from '~/lib/content-listings.utils'
+import { describe, expect, it } from 'vitest'
+
+describe('isExternalContentListingHref', () => {
+ it('treats protocol-relative URLs as external', () => {
+ expect(isExternalContentListingHref('//example.com/path')).toBe(true)
+ })
+
+ it('treats absolute http(s) URLs as external', () => {
+ expect(isExternalContentListingHref('https://github.com/supabase/storage-api')).toBe(true)
+ })
+
+ it('treats internal guide paths as not external', () => {
+ expect(isExternalContentListingHref('/guides/storage/quickstart')).toBe(false)
+ })
+})
+
+describe('serializeContentListingGroupToMarkdown', () => {
+ it('renders grouped items with absolute URLs and descriptions', () => {
+ const markdown = serializeContentListingGroupToMarkdown(
+ {
+ id: 'get-started',
+ heading: 'Get started',
+ description: 'Read these first.',
+ items: [
+ {
+ title: 'Connect to your database',
+ href: '/guides/database/connecting-to-postgres',
+ description: 'Connection strings and pooler modes.',
+ },
+ ],
+ },
+ 'https://supabase.com'
+ )
+
+ expect(markdown).toContain('## Get started')
+ expect(markdown).toContain('Read these first.')
+ expect(markdown).toContain(
+ '**[Connect to your database](https://supabase.com/docs/guides/database/connecting-to-postgres):** Connection strings and pooler modes.'
+ )
+ })
+
+ it('preserves external hrefs in markdown export', () => {
+ const markdown = serializeContentListingGroupToMarkdown(
+ {
+ id: 'resources',
+ items: [
+ {
+ title: 'Storage API',
+ href: 'https://github.com/supabase/storage-api',
+ description: 'View the source code.',
+ },
+ ],
+ },
+ 'https://supabase.com'
+ )
+
+ expect(markdown).toContain(
+ '**[Storage API](https://github.com/supabase/storage-api):** View the source code.'
+ )
+ })
+
+ it('respects heading-level in markdown export', () => {
+ const markdown = serializeContentListingGroupToMarkdown(
+ {
+ id: 'get-started',
+ heading: 'Get started',
+ headingLevel: 'h3',
+ items: [
+ {
+ title: 'Connect',
+ href: '/guides/database/connecting-to-postgres',
+ description: 'Connection strings.',
+ },
+ ],
+ },
+ ''
+ )
+
+ expect(markdown).toContain('### Get started')
+ })
+
+ it('renders heading and description only once', () => {
+ const markdown = serializeContentListingGroupToMarkdown(
+ {
+ id: 'get-started',
+ heading: 'Get started',
+ description: 'Read these first.',
+ items: [
+ {
+ title: 'Connect',
+ href: '/guides/database/connecting-to-postgres',
+ description: 'Connection strings.',
+ },
+ ],
+ },
+ ''
+ )
+
+ expect(markdown.match(/^## Get started$/gm)).toHaveLength(1)
+ expect(markdown.match(/^Read these first\.$/gm)).toHaveLength(1)
+ expect(markdown).toBe(
+ '## Get started\n\nRead these first.\n\n- **[Connect](/docs/guides/database/connecting-to-postgres):** Connection strings.'
+ )
+ })
+
+ it('omits heading line when heading is not set', () => {
+ const markdown = serializeContentListingGroupToMarkdown(
+ {
+ id: 'get-started',
+ items: [
+ {
+ title: 'Connect',
+ href: '/guides/database/connecting-to-postgres',
+ description: 'Connection strings.',
+ },
+ ],
+ },
+ ''
+ )
+
+ expect(markdown).not.toMatch(/^#+\s/m)
+ expect(markdown).toContain('**[Connect]')
+ })
+})
+
+describe('ContentListings markdown handler', () => {
+ it('serializes the group looked up by id', () => {
+ const markdown = ContentListingsMarkdownHandler({ props: { id: 'storage-get-started' } })
+
+ expect(markdown).toContain('## Get started')
+ expect(markdown).toContain('Files buckets')
+ expect(markdown).toContain('/guides/storage/quickstart')
+ })
+
+ it('returns empty string when id is missing or unknown', () => {
+ expect(ContentListingsMarkdownHandler({ props: {} })).toBe('')
+ expect(ContentListingsMarkdownHandler({ props: { id: 'nonexistent-id' } })).toBe('')
+ })
+
+ it('matches direct serializeContentListingGroupToMarkdown for the same data', () => {
+ const handler = ContentListingsMarkdownHandler({ props: { id: 'storage-get-started' } })
+ const direct = serializeContentListingGroupToMarkdown(storageGetStarted, '')
+ expect(handler).toBe(direct)
+ })
+})
+
+describe('TelemetryEvent union', () => {
+ it('includes docs_content_listing_clicked', () => {
+ const event = {
+ action: 'docs_content_listing_clicked' as const,
+ properties: {
+ targetPath: '/guides/storage',
+ linkTitle: 'Storage',
+ },
+ }
+
+ const _typeCheck: import('common/telemetry-constants').TelemetryEvent = event
+ expect(_typeCheck.action).toBe('docs_content_listing_clicked')
+ })
+})
diff --git a/apps/docs/lib/content-listings.utils.ts b/apps/docs/lib/content-listings.utils.ts
new file mode 100644
index 00000000000..10c73b4d4f1
--- /dev/null
+++ b/apps/docs/lib/content-listings.utils.ts
@@ -0,0 +1,16 @@
+import { CONTENT_LISTINGS } from '~/data/content-listings'
+
+import type { ContentListingGroup } from './content-listings.schema'
+
+/** Label for telemetry — prefers heading, falls back to id. */
+export function getContentListingGroupLabel(group: ContentListingGroup): string {
+ return group.heading ?? group.id
+}
+
+export function isExternalContentListingHref(href: string): boolean {
+ return /^https?:\/\//i.test(href) || href.startsWith('//')
+}
+
+export function getContentListingById(id: string): ContentListingGroup | undefined {
+ return CONTENT_LISTINGS[id]
+}
diff --git a/apps/docs/lib/content-listings.zod.mjs b/apps/docs/lib/content-listings.zod.mjs
new file mode 100644
index 00000000000..4480879288e
--- /dev/null
+++ b/apps/docs/lib/content-listings.zod.mjs
@@ -0,0 +1,24 @@
+import { z } from 'zod'
+
+export const contentListingItemSchema = z.object({
+ title: z.string().min(1),
+ href: z.string().min(1),
+ description: z.string().min(1),
+ icon: z.string().min(1).optional(),
+})
+
+export const contentListingGroupTypeSchema = z.enum(['list', 'grid'])
+
+export const contentListingGridColumnsSchema = z.union([z.literal(2), z.literal(3), z.literal(4)])
+
+export const contentListingHeadingLevelSchema = z.enum(['h2', 'h3', 'h4'])
+
+export const contentListingGroupSchema = z.object({
+ id: z.string().min(1),
+ heading: z.string().min(1).optional(),
+ headingLevel: contentListingHeadingLevelSchema.optional(),
+ description: z.string().optional(),
+ type: contentListingGroupTypeSchema.optional(),
+ columns: contentListingGridColumnsSchema.optional(),
+ items: z.array(contentListingItemSchema).min(1),
+})
diff --git a/packages/common/telemetry-constants.ts b/packages/common/telemetry-constants.ts
index fee4d647002..6034b84443f 100644
--- a/packages/common/telemetry-constants.ts
+++ b/packages/common/telemetry-constants.ts
@@ -892,6 +892,22 @@ export interface AskAiClickedEvent {
}
}
+/**
+ * User clicked a curated orientation link from a content listings MDX component.
+ *
+ * @group Events
+ * @source docs
+ */
+export interface DocsContentListingClickedEvent {
+ action: 'docs_content_listing_clicked'
+ properties: {
+ targetPath: string
+ linkTitle: string
+ groupTitle?: string
+ listingId?: string
+ }
+}
+
/**
* User clicked a recommended page card shown on a docs "not found" page.
*
@@ -3553,6 +3569,7 @@ export type TelemetryEvent =
| DocsFeedbackClickedEvent
| CopyAsMarkdownClickedEvent
| AskAiClickedEvent
+ | DocsContentListingClickedEvent
| Docs404RecommendationClickedEvent
| HomepageFrameworkQuickstartClickedEvent
| HomepageProductCardClickedEvent