mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 09:25:06 +03:00
feat(www): emit BreadcrumbList JSON-LD on marketing surfaces (#45478)
## Summary
- Adds `breadcrumbListSchema(items)` helper to `apps/www/lib/json-ld.ts`
and a hand-curated `apps/www/lib/breadcrumbs.ts` route map.
- Wires inline `<script type="application/ld+json">` BreadcrumbList
blocks into 18 marketing surfaces: blog (index + slug), customers (index
+ slug), events (index + slug), 5 product pages (database, auth,
storage, edge-functions, realtime), 3 modules (vector, cron, queues),
pricing, careers, company, features.
- Pages router callers wrap the script in `<Head>`; app router callers
place it directly in JSX. Dynamic surfaces append a leaf at render time
using the page's title (`frontmatter.title` for blog, `meta_title ??
title` for customers, `event.meta_title ?? event.title` for events).
- Modules sit at `Home > {Name}` since no `/modules` index page exists;
products sit at `Home > {Product}` (no shared products parent). Absolute
`https://supabase.com` URLs match the existing `CANONICAL_ORIGIN`
convention so anchors stay stable across Vercel previews.
Linear:
[GROWTH-822](https://linear.app/supabase/issue/GROWTH-822/add-breadcrumblist-json-ld-to-www-marketing-surfaces)
(sub-issue under
[GROWTH-724](https://linear.app/supabase/issue/GROWTH-724)).
> **Note on branch name:** the branch is
`pamela/growth-820-www-breadcrumb-jsonld`; the actual Linear issue is
GROWTH-822. The branch was named before the sub-issue was created.
Ignore the `820` in the branch.
Explicitly deferred (separate PRs / low SEO ROI): `/launch-week/*`,
`/solutions/*`, `/partners/*`, `/alternatives/*`, `/changelog`,
`/legal/dpa`, `/aws-reinvent-2025`, `/wrapped`, `/contribute/*`,
`/brand-assets`, `/ga`, `/ga-week`, `/state-of-startups*`, and the
homepage (Organization + WebSite already cover homepage entity signals;
single-item BreadcrumbList is ignored by Google).
## Test plan
- [x] On the Vercel preview, `curl -s https://<preview>/database | grep
'"BreadcrumbList"'` returns the script block with `Home > Database`.
- [x] `curl -s https://<preview>/blog/<recent-slug> | grep
'"BreadcrumbList"'` returns `Home > Blog > {post title}`.
- [x] `curl -s https://<preview>/customers/<slug> | grep
'"BreadcrumbList"'` returns `Home > Customer Stories > {customer
title}`.
- [x] `curl -s https://<preview>/events/<slug> | grep
'"BreadcrumbList"'` returns `Home > Events > {event title}`.
- [x] `curl -s https://<preview>/modules/vector | grep
'"BreadcrumbList"'` returns `Home > Vector`.
This commit is contained in:
1 parent
2e904abebf
commit
c26b64a033
20 files changed
+277
-63
No files matched your search
@@ -9,8 +9,9 @@ import {
|
||||
getAbsoluteBlogSocialImage,
|
||||
toAbsoluteBlogImageUrl,
|
||||
} from '@/lib/blog-images'
|
||||
import { breadcrumbs } from '@/lib/breadcrumbs'
|
||||
import { SITE_ORIGIN } from '@/lib/constants'
|
||||
import { blogPostingSchema, serializeJsonLd } from '@/lib/json-ld'
|
||||
import { blogPostingSchema, breadcrumbListSchema, serializeJsonLd } from '@/lib/json-ld'
|
||||
import { getAllPostSlugs, getPostdata, getSortedPosts } from '@/lib/posts'
|
||||
import type { Blog, BlogData, PostReturnType } from '@/types/post'
|
||||
|
||||
@@ -171,6 +172,10 @@ export default async function BlogPostPage({ params }: { params: Promise<Params>
|
||||
datePublished: frontmatter.date,
|
||||
authors: blogAuthors.length > 0 ? blogAuthors : [{ name: 'Supabase' }],
|
||||
})
|
||||
const breadcrumbJsonLd = breadcrumbListSchema([
|
||||
...breadcrumbs.blogIndex,
|
||||
{ name: frontmatter.title, url: `https://supabase.com/blog/${slug}` },
|
||||
])
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -178,6 +183,10 @@ export default async function BlogPostPage({ params }: { params: Promise<Params>
|
||||
type="application/ld+json"
|
||||
dangerouslySetInnerHTML={{ __html: serializeJsonLd(blogJsonLd) }}
|
||||
/>
|
||||
<script
|
||||
type="application/ld+json"
|
||||
dangerouslySetInnerHTML={{ __html: serializeJsonLd(breadcrumbJsonLd) }}
|
||||
/>
|
||||
<BlogPostClient {...props} />
|
||||
</>
|
||||
)
|
||||
|
||||
+23
-11
@@ -4,6 +4,8 @@ import type { Metadata } from 'next'
|
||||
import type PostTypes from 'types/post'
|
||||
|
||||
import BlogClient from './BlogClient'
|
||||
import { breadcrumbs } from '@/lib/breadcrumbs'
|
||||
import { breadcrumbListSchema, serializeJsonLd } from '@/lib/json-ld'
|
||||
import { getSortedPosts } from '@/lib/posts'
|
||||
|
||||
export const revalidate = 30
|
||||
@@ -39,17 +41,27 @@ export default async function BlogPage() {
|
||||
const totalListPosts = Math.max(0, allPosts.length - 1)
|
||||
|
||||
return (
|
||||
<DefaultLayout>
|
||||
<h1 className="sr-only">Supabase blog</h1>
|
||||
<div className="container relative mx-auto px-4 py-4 md:py-8 xl:py-10 sm:px-16 xl:px-20">
|
||||
{featuredPost && <FeaturedThumb key={featuredPost.slug} {...(featuredPost as PostTypes)} />}
|
||||
</div>
|
||||
|
||||
<div className="border-default border-t">
|
||||
<div className="container mx-auto px-4 py-4 md:py-8 xl:py-10 sm:px-16 xl:px-20">
|
||||
<BlogClient initialBlogs={listPosts} totalPosts={totalListPosts} />
|
||||
<>
|
||||
<script
|
||||
type="application/ld+json"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: serializeJsonLd(breadcrumbListSchema(breadcrumbs.blogIndex)),
|
||||
}}
|
||||
/>
|
||||
<DefaultLayout>
|
||||
<h1 className="sr-only">Supabase blog</h1>
|
||||
<div className="container relative mx-auto px-4 py-4 md:py-8 xl:py-10 sm:px-16 xl:px-20">
|
||||
{featuredPost && (
|
||||
<FeaturedThumb key={featuredPost.slug} {...(featuredPost as PostTypes)} />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</DefaultLayout>
|
||||
|
||||
<div className="border-default border-t">
|
||||
<div className="container mx-auto px-4 py-4 md:py-8 xl:py-10 sm:px-16 xl:px-20">
|
||||
<BlogClient initialBlogs={listPosts} totalPosts={totalListPosts} />
|
||||
</div>
|
||||
</div>
|
||||
</DefaultLayout>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
import type { Metadata } from 'next'
|
||||
import { getNotionEvents } from '~/lib/events'
|
||||
import { EventClientRenderer } from '~/components/Events/new/EventClientRenderer'
|
||||
import { breadcrumbs } from '~/lib/breadcrumbs'
|
||||
import { getNotionEvents } from '~/lib/events'
|
||||
import { breadcrumbListSchema, serializeJsonLd } from '~/lib/json-ld'
|
||||
import type { Metadata } from 'next'
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'View all Supabase events and meetups.',
|
||||
@@ -11,5 +13,15 @@ export const metadata: Metadata = {
|
||||
export default async function EventsPage() {
|
||||
const notionEvents = await getNotionEvents()
|
||||
|
||||
return <EventClientRenderer notionEvents={notionEvents} />
|
||||
return (
|
||||
<>
|
||||
<script
|
||||
type="application/ld+json"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: serializeJsonLd(breadcrumbListSchema(breadcrumbs.eventsIndex)),
|
||||
}}
|
||||
/>
|
||||
<EventClientRenderer notionEvents={notionEvents} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
import type { Metadata } from 'next'
|
||||
|
||||
import PricingContent from './PricingContent'
|
||||
import { breadcrumbs } from '@/lib/breadcrumbs'
|
||||
import { breadcrumbListSchema, serializeJsonLd } from '@/lib/json-ld'
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Pricing & Fees | Supabase',
|
||||
@@ -25,5 +27,15 @@ export const metadata: Metadata = {
|
||||
}
|
||||
|
||||
export default function PricingPage() {
|
||||
return <PricingContent />
|
||||
return (
|
||||
<>
|
||||
<script
|
||||
type="application/ld+json"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: serializeJsonLd(breadcrumbListSchema(breadcrumbs.pricing)),
|
||||
}}
|
||||
/>
|
||||
<PricingContent />
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import type { BreadcrumbItem } from './json-ld'
|
||||
|
||||
const SITE = 'https://supabase.com'
|
||||
|
||||
const home: BreadcrumbItem = { name: 'Home', url: SITE }
|
||||
|
||||
export const breadcrumbs = {
|
||||
blogIndex: [home, { name: 'Blog', url: `${SITE}/blog` }],
|
||||
customersIndex: [home, { name: 'Customer Stories', url: `${SITE}/customers` }],
|
||||
eventsIndex: [home, { name: 'Events', url: `${SITE}/events` }],
|
||||
database: [home, { name: 'Database', url: `${SITE}/database` }],
|
||||
auth: [home, { name: 'Auth', url: `${SITE}/auth` }],
|
||||
storage: [home, { name: 'Storage', url: `${SITE}/storage` }],
|
||||
edgeFunctions: [home, { name: 'Edge Functions', url: `${SITE}/edge-functions` }],
|
||||
realtime: [home, { name: 'Realtime', url: `${SITE}/realtime` }],
|
||||
vector: [home, { name: 'Vector', url: `${SITE}/modules/vector` }],
|
||||
cron: [home, { name: 'Cron', url: `${SITE}/modules/cron` }],
|
||||
queues: [home, { name: 'Queues', url: `${SITE}/modules/queues` }],
|
||||
pricing: [home, { name: 'Pricing', url: `${SITE}/pricing` }],
|
||||
careers: [home, { name: 'Careers', url: `${SITE}/careers` }],
|
||||
company: [home, { name: 'Company', url: `${SITE}/company` }],
|
||||
features: [home, { name: 'Features', url: `${SITE}/features` }],
|
||||
} satisfies Record<string, BreadcrumbItem[]>
|
||||
@@ -83,6 +83,24 @@ export function softwareApplicationSchema(input: SoftwareApplicationSchemaInput)
|
||||
}
|
||||
}
|
||||
|
||||
export interface BreadcrumbItem {
|
||||
name: string
|
||||
url: string
|
||||
}
|
||||
|
||||
export function breadcrumbListSchema(items: BreadcrumbItem[]) {
|
||||
return {
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'BreadcrumbList',
|
||||
itemListElement: items.map((item, index) => ({
|
||||
'@type': 'ListItem',
|
||||
position: index + 1,
|
||||
name: item.name,
|
||||
item: item.url,
|
||||
})),
|
||||
}
|
||||
}
|
||||
|
||||
interface BlogPostingSchemaInput {
|
||||
url: string
|
||||
headline: string
|
||||
|
||||
@@ -19,7 +19,8 @@ import AuthProviders from '@/data/auth.json'
|
||||
import MainProducts from '@/data/MainProducts'
|
||||
import ApiExamples from '@/data/products/auth/auth-api-examples'
|
||||
import AuthSqlRulesExamples from '@/data/products/auth/auth-sql-rules-examples'
|
||||
import { serializeJsonLd, softwareApplicationSchema } from '@/lib/json-ld'
|
||||
import { breadcrumbs } from '@/lib/breadcrumbs'
|
||||
import { breadcrumbListSchema, serializeJsonLd, softwareApplicationSchema } from '@/lib/json-ld'
|
||||
|
||||
const SplitCodeBlockCarousel = dynamic(
|
||||
() => import('~/components/Carousels/SplitCodeBlockCarousel')
|
||||
@@ -70,6 +71,12 @@ function AuthPage() {
|
||||
),
|
||||
}}
|
||||
/>
|
||||
<script
|
||||
type="application/ld+json"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: serializeJsonLd(breadcrumbListSchema(breadcrumbs.auth)),
|
||||
}}
|
||||
/>
|
||||
</Head>
|
||||
<DefaultLayout>
|
||||
<ProductsNav activePage={PRODUCT_NAMES.AUTHENTICATION} />
|
||||
|
||||
@@ -3,10 +3,13 @@ import Globe from '~/components/Globe'
|
||||
import DefaultLayout from '~/components/Layouts/Default'
|
||||
import SectionContainer from '~/components/Layouts/SectionContainer'
|
||||
import career from '~/data/career.json'
|
||||
import { breadcrumbs } from '~/lib/breadcrumbs'
|
||||
import { filterGenericJob, groupJobsByTeam, JobItemProps, PLACEHOLDER_JOB_ID } from '~/lib/careers'
|
||||
import { breadcrumbListSchema, serializeJsonLd } from '~/lib/json-ld'
|
||||
import Styles from '~/styles/career.module.css'
|
||||
import { GetServerSideProps } from 'next'
|
||||
import { NextSeo } from 'next-seo'
|
||||
import Head from 'next/head'
|
||||
import Image from 'next/image'
|
||||
import Link from 'next/link'
|
||||
import { useRouter } from 'next/router'
|
||||
@@ -109,6 +112,14 @@ const CareerPage = ({ jobs, placeholderJob, contributors }: CareersPageProps) =>
|
||||
],
|
||||
}}
|
||||
/>
|
||||
<Head>
|
||||
<script
|
||||
type="application/ld+json"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: serializeJsonLd(breadcrumbListSchema(breadcrumbs.careers)),
|
||||
}}
|
||||
/>
|
||||
</Head>
|
||||
<DefaultLayout>
|
||||
<header className="container relative mx-auto px-6 pt-12 pb-8 lg:pt-24 lg:px-16 xl:px-20 text-center space-y-4">
|
||||
<h1 className="text-sm text-brand md:text-base">
|
||||
|
||||
+16
-10
@@ -1,22 +1,20 @@
|
||||
import { useRouter } from 'next/router'
|
||||
|
||||
import Layout from '~/components/Layouts/Default'
|
||||
|
||||
import SectionHeader from 'components/UI/SectionHeader'
|
||||
import SectionContainer from '~/components/Layouts/SectionContainer'
|
||||
import { breadcrumbs } from '~/lib/breadcrumbs'
|
||||
import { breadcrumbListSchema, serializeJsonLd } from '~/lib/json-ld'
|
||||
import CTABanner from 'components/CTABanner/index'
|
||||
import ImageGrid from 'components/ImageGrid'
|
||||
import SectionContainer from '~/components/Layouts/SectionContainer'
|
||||
|
||||
import PressData from 'data/Press'
|
||||
import SectionHeader from 'components/UI/SectionHeader'
|
||||
import CommunityData from 'data/Community'
|
||||
import CompaniesData from 'data/Companies'
|
||||
import InvestorData from 'data/Investors'
|
||||
|
||||
import PressData from 'data/Press'
|
||||
import { NextSeo } from 'next-seo'
|
||||
import Head from 'next/head'
|
||||
import Image from 'next/image'
|
||||
import Link from 'next/link'
|
||||
|
||||
import { useRouter } from 'next/router'
|
||||
import { Button, Card_legacy_, Space } from 'ui'
|
||||
import { NextSeo } from 'next-seo'
|
||||
|
||||
type Props = {}
|
||||
|
||||
@@ -43,6 +41,14 @@ const Index = ({}: Props) => {
|
||||
],
|
||||
}}
|
||||
/>
|
||||
<Head>
|
||||
<script
|
||||
type="application/ld+json"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: serializeJsonLd(breadcrumbListSchema(breadcrumbs.company)),
|
||||
}}
|
||||
/>
|
||||
</Head>
|
||||
<Layout>
|
||||
<Header />
|
||||
<Community />
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
import fs from 'fs'
|
||||
|
||||
import { useRouter } from 'next/router'
|
||||
import Head from 'next/head'
|
||||
|
||||
import { NextSeo } from 'next-seo'
|
||||
import { generateRss } from '~/lib/rss'
|
||||
import { getSortedPosts } from '~/lib/posts'
|
||||
|
||||
import DefaultLayout from '~/components/Layouts/Default'
|
||||
import { getSortedPosts } from '~/lib/posts'
|
||||
import { generateRss } from '~/lib/rss'
|
||||
import styles from '~/styles/customers.module.css'
|
||||
import type PostTypes from '~/types/post'
|
||||
import { motion } from 'framer-motion'
|
||||
import styles from '~/styles/customers.module.css'
|
||||
import { NextSeo } from 'next-seo'
|
||||
import Head from 'next/head'
|
||||
import Link from 'next/link'
|
||||
import { GlassPanel } from 'ui-patterns/GlassPanel'
|
||||
import CustomersFilters from '../components/CustomerStories/CustomersFilters'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useState } from 'react'
|
||||
import { Button, cn } from 'ui'
|
||||
import { GlassPanel } from 'ui-patterns/GlassPanel'
|
||||
|
||||
import CustomersFilters from '../components/CustomerStories/CustomersFilters'
|
||||
import { breadcrumbs } from '@/lib/breadcrumbs'
|
||||
import { breadcrumbListSchema, serializeJsonLd } from '@/lib/json-ld'
|
||||
|
||||
export async function getStaticProps() {
|
||||
const allPostsData: any[] = getSortedPosts({ directory: '_customers' })
|
||||
@@ -99,6 +99,12 @@ function CustomerStoriesPage(props: any) {
|
||||
title="RSS feed for customer stories"
|
||||
href={`${basePath}/customers-rss.xml`}
|
||||
/>
|
||||
<script
|
||||
type="application/ld+json"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: serializeJsonLd(breadcrumbListSchema(breadcrumbs.customersIndex)),
|
||||
}}
|
||||
/>
|
||||
</Head>
|
||||
<NextSeo
|
||||
title={meta.title}
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
import matter from 'gray-matter'
|
||||
import { ChevronLeft, ChevronRight, ExternalLink } from 'lucide-react'
|
||||
|
||||
import { MDXRemote } from 'next-mdx-remote'
|
||||
import { NextSeo } from 'next-seo'
|
||||
import Image from 'next/image'
|
||||
import Link from 'next/link'
|
||||
import { Button } from 'ui'
|
||||
import CTABanner from '~/components/CTABanner'
|
||||
import DefaultLayout from '~/components/Layouts/Default'
|
||||
import { breadcrumbs } from '~/lib/breadcrumbs'
|
||||
import { SITE_ORIGIN } from '~/lib/constants'
|
||||
import { breadcrumbListSchema, serializeJsonLd } from '~/lib/json-ld'
|
||||
import mdxComponents from '~/lib/mdx/mdxComponents'
|
||||
import { mdxSerialize } from '~/lib/mdx/mdxSerialize'
|
||||
import { getAllPostSlugs, getPostdata, getSortedPosts } from '~/lib/posts'
|
||||
import matter from 'gray-matter'
|
||||
import { ChevronLeft, ChevronRight, ExternalLink } from 'lucide-react'
|
||||
import { MDXRemote } from 'next-mdx-remote'
|
||||
import { NextSeo } from 'next-seo'
|
||||
import Head from 'next/head'
|
||||
import Image from 'next/image'
|
||||
import Link from 'next/link'
|
||||
import { Button } from 'ui'
|
||||
|
||||
// table of contents extractor
|
||||
const toc = require('markdown-toc')
|
||||
@@ -89,6 +91,11 @@ function CaseStudyPage(props: any) {
|
||||
url: `${SITE_ORIGIN}/customers/${slug}`,
|
||||
}
|
||||
|
||||
const breadcrumbItems = [
|
||||
...breadcrumbs.customersIndex,
|
||||
{ name: meta_title ?? title, url: `https://supabase.com/customers/${slug}` },
|
||||
]
|
||||
|
||||
return (
|
||||
<>
|
||||
<NextSeo
|
||||
@@ -112,6 +119,14 @@ function CaseStudyPage(props: any) {
|
||||
],
|
||||
}}
|
||||
/>
|
||||
<Head>
|
||||
<script
|
||||
type="application/ld+json"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: serializeJsonLd(breadcrumbListSchema(breadcrumbItems)),
|
||||
}}
|
||||
/>
|
||||
</Head>
|
||||
<DefaultLayout>
|
||||
<div
|
||||
className="
|
||||
|
||||
@@ -24,7 +24,8 @@ import ExtensionsExamplesData from '@/data/products/database/extensions-examples
|
||||
import HighlightsCards from '@/data/products/database/highlight-cards'
|
||||
import SqlViewCarouselData from '@/data/products/database/sql-view-carousel.json'
|
||||
import TableViewCarouselData from '@/data/products/database/table-view-carousel.json'
|
||||
import { serializeJsonLd, softwareApplicationSchema } from '@/lib/json-ld'
|
||||
import { breadcrumbs } from '@/lib/breadcrumbs'
|
||||
import { breadcrumbListSchema, serializeJsonLd, softwareApplicationSchema } from '@/lib/json-ld'
|
||||
|
||||
const NewFeatureCard = dynamic(() => import('~/components/NewFeatureCard'))
|
||||
const ImageCarousel = dynamic(() => import('~/components/Carousels/ImageCarousel'))
|
||||
@@ -88,6 +89,12 @@ function Database() {
|
||||
),
|
||||
}}
|
||||
/>
|
||||
<script
|
||||
type="application/ld+json"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: serializeJsonLd(breadcrumbListSchema(breadcrumbs.database)),
|
||||
}}
|
||||
/>
|
||||
</Head>
|
||||
<DefaultLayout>
|
||||
<ProductsNav activePage={PRODUCT_NAMES.DATABASE} />
|
||||
|
||||
@@ -13,7 +13,8 @@ import { useRouter } from 'next/router'
|
||||
import React from 'react'
|
||||
import { PRODUCT_NAMES, PRODUCT_SHORTNAMES } from 'shared-data/products'
|
||||
|
||||
import { serializeJsonLd, softwareApplicationSchema } from '@/lib/json-ld'
|
||||
import { breadcrumbs } from '@/lib/breadcrumbs'
|
||||
import { breadcrumbListSchema, serializeJsonLd, softwareApplicationSchema } from '@/lib/json-ld'
|
||||
|
||||
const ExamplesCarousel = dynamic(() => import('~/components/Examples/ExamplesCarousel'))
|
||||
const GlobalPresenceSection = dynamic(
|
||||
@@ -62,6 +63,12 @@ function EdgeFunctions() {
|
||||
),
|
||||
}}
|
||||
/>
|
||||
<script
|
||||
type="application/ld+json"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: serializeJsonLd(breadcrumbListSchema(breadcrumbs.edgeFunctions)),
|
||||
}}
|
||||
/>
|
||||
</Head>
|
||||
<DefaultLayout>
|
||||
<ProductsNav activePage={PRODUCT_NAMES.FUNCTIONS} />
|
||||
|
||||
@@ -15,6 +15,7 @@ import { ChevronLeft, X as XIcon } from 'lucide-react'
|
||||
import type { GetStaticProps, InferGetStaticPropsType } from 'next'
|
||||
import { MDXRemote } from 'next-mdx-remote'
|
||||
import { NextSeo } from 'next-seo'
|
||||
import Head from 'next/head'
|
||||
import NextImage from 'next/image'
|
||||
import Link from 'next/link'
|
||||
import { Button } from 'ui'
|
||||
@@ -24,7 +25,9 @@ import ShareArticleActions from '@/components/Blog/ShareArticleActions'
|
||||
import DefaultLayout from '@/components/Layouts/Default'
|
||||
import SectionContainer from '@/components/Layouts/SectionContainer'
|
||||
import authors from '@/lib/authors.json'
|
||||
import { breadcrumbs } from '@/lib/breadcrumbs'
|
||||
import { capitalize, isNotNullOrUndefined } from '@/lib/helpers'
|
||||
import { breadcrumbListSchema, serializeJsonLd } from '@/lib/json-ld'
|
||||
import mdxComponents from '@/lib/mdx/mdxComponents'
|
||||
import { mdxSerialize } from '@/lib/mdx/mdxSerialize'
|
||||
import { getAllPostSlugs, getPostdata } from '@/lib/posts'
|
||||
@@ -221,6 +224,22 @@ const EventPage = ({ event }: InferGetStaticPropsType<typeof getStaticProps>) =>
|
||||
},
|
||||
}}
|
||||
/>
|
||||
<Head>
|
||||
<script
|
||||
type="application/ld+json"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: serializeJsonLd(
|
||||
breadcrumbListSchema([
|
||||
...breadcrumbs.eventsIndex,
|
||||
{
|
||||
name: event.meta_title ?? event.title,
|
||||
url: `https://supabase.com/events/${event.slug}`,
|
||||
},
|
||||
])
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</Head>
|
||||
<DefaultLayout>
|
||||
<div className="flex flex-col w-full bg-alternative border-b border-muted">
|
||||
<SectionContainer className="py-2! flex items-start">
|
||||
|
||||
@@ -2,11 +2,14 @@ import DefaultLayout from '~/components/Layouts/Default'
|
||||
import SectionContainer from '~/components/Layouts/SectionContainer'
|
||||
import Panel from '~/components/Panel'
|
||||
import { features } from '~/data/features'
|
||||
import { breadcrumbs } from '~/lib/breadcrumbs'
|
||||
import { breadcrumbListSchema, serializeJsonLd } from '~/lib/json-ld'
|
||||
import { motion } from 'framer-motion'
|
||||
import { debounce } from 'lib/helpers'
|
||||
import { Search } from 'lucide-react'
|
||||
import { NextSeo } from 'next-seo'
|
||||
import { useRouter } from 'next/compat/router'
|
||||
import Head from 'next/head'
|
||||
import Link from 'next/link'
|
||||
import React, { useCallback, useEffect, useState } from 'react'
|
||||
import { Button, Checkbox, cn, InputGroup, InputGroupAddon, InputGroupInput } from 'ui'
|
||||
@@ -103,6 +106,14 @@ function FeaturesPage() {
|
||||
url: '/customers',
|
||||
}}
|
||||
/>
|
||||
<Head>
|
||||
<script
|
||||
type="application/ld+json"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: serializeJsonLd(breadcrumbListSchema(breadcrumbs.features)),
|
||||
}}
|
||||
/>
|
||||
</Head>
|
||||
<DefaultLayout>
|
||||
<SectionContainer className="py-0! sm:px-0!">
|
||||
<div className="border border-muted rounded-xl bg-alternative my-4 px-6 py-8 md:py-10 lg:px-16 lg:py-20 xl:px-20 bg-center bg-cover bg-[url('/images/features/features-cover-light.svg')] dark:bg-[url('/images/features/features-cover-dark.svg')]">
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import { NextSeo } from 'next-seo'
|
||||
import dynamic from 'next/dynamic'
|
||||
|
||||
import DefaultLayout from '~/components/Layouts/Default'
|
||||
import SectionContainer from '~/components/Layouts/SectionContainer'
|
||||
import ModulesNav from '~/components/Modules/ModulesNav'
|
||||
import ProductModulesHeader from '~/components/Sections/ProductModulesHeader'
|
||||
import SectionContainer from '~/components/Layouts/SectionContainer'
|
||||
|
||||
import { PRODUCT_MODULES_NAMES } from 'shared-data/products'
|
||||
import CronPageData from '~/data/products/modules/cron'
|
||||
import { breadcrumbs } from '~/lib/breadcrumbs'
|
||||
import { breadcrumbListSchema, serializeJsonLd } from '~/lib/json-ld'
|
||||
import { NextSeo } from 'next-seo'
|
||||
import dynamic from 'next/dynamic'
|
||||
import Head from 'next/head'
|
||||
import { PRODUCT_MODULES_NAMES } from 'shared-data/products'
|
||||
|
||||
const HighlightCards = dynamic(() => import('~/components/Sections/HighlightCards'))
|
||||
const CronSQLSection = dynamic(() => import('~/components/Modules/Cron/CronSQLSection'))
|
||||
@@ -33,6 +34,14 @@ function CronPage() {
|
||||
],
|
||||
}}
|
||||
/>
|
||||
<Head>
|
||||
<script
|
||||
type="application/ld+json"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: serializeJsonLd(breadcrumbListSchema(breadcrumbs.cron)),
|
||||
}}
|
||||
/>
|
||||
</Head>
|
||||
<DefaultLayout className="bg-alternative!" stickyNavbar={false}>
|
||||
<ModulesNav activePage={PRODUCT_MODULES_NAMES.CRON} docsUrl={pageData.docsUrl} />
|
||||
<ProductModulesHeader {...pageData.heroSection} />
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import { NextSeo } from 'next-seo'
|
||||
import dynamic from 'next/dynamic'
|
||||
|
||||
import DefaultLayout from '~/components/Layouts/Default'
|
||||
import SectionContainer from '~/components/Layouts/SectionContainer'
|
||||
import ModulesNav from '~/components/Modules/ModulesNav'
|
||||
import ProductModulesHeader from '~/components/Sections/ProductModulesHeader'
|
||||
import SectionContainer from '~/components/Layouts/SectionContainer'
|
||||
|
||||
import { PRODUCT_MODULES_NAMES } from 'shared-data/products'
|
||||
import QueuesPageData from '~/data/products/modules/queues'
|
||||
import { breadcrumbs } from '~/lib/breadcrumbs'
|
||||
import { breadcrumbListSchema, serializeJsonLd } from '~/lib/json-ld'
|
||||
import { NextSeo } from 'next-seo'
|
||||
import dynamic from 'next/dynamic'
|
||||
import Head from 'next/head'
|
||||
import { PRODUCT_MODULES_NAMES } from 'shared-data/products'
|
||||
|
||||
const HighlightCards = dynamic(() => import('~/components/Sections/HighlightCards'))
|
||||
const QueuesSQLSection = dynamic(() => import('~/components/Modules/Queues/QueuesSQLSection'))
|
||||
@@ -34,6 +35,14 @@ function CronPage() {
|
||||
],
|
||||
}}
|
||||
/>
|
||||
<Head>
|
||||
<script
|
||||
type="application/ld+json"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: serializeJsonLd(breadcrumbListSchema(breadcrumbs.queues)),
|
||||
}}
|
||||
/>
|
||||
</Head>
|
||||
<DefaultLayout className="bg-alternative!" stickyNavbar={false}>
|
||||
<ModulesNav activePage={PRODUCT_MODULES_NAMES.QUEUES} docsUrl={pageData.docsUrl} />
|
||||
<ProductModulesHeader {...pageData.heroSection} />
|
||||
|
||||
@@ -9,7 +9,8 @@ import dynamic from 'next/dynamic'
|
||||
import Head from 'next/head'
|
||||
import { PRODUCT_MODULES_NAMES, PRODUCT_MODULES_SHORTNAMES } from 'shared-data/products'
|
||||
|
||||
import { serializeJsonLd, softwareApplicationSchema } from '@/lib/json-ld'
|
||||
import { breadcrumbs } from '@/lib/breadcrumbs'
|
||||
import { breadcrumbListSchema, serializeJsonLd, softwareApplicationSchema } from '@/lib/json-ld'
|
||||
|
||||
const ProductModulesHeader = dynamic(() => import('~/components/Sections/ProductModulesHeader'))
|
||||
const HighlightCards = dynamic(() => import('~/components/Sections/HighlightCards'))
|
||||
@@ -61,6 +62,12 @@ function VectorPage() {
|
||||
),
|
||||
}}
|
||||
/>
|
||||
<script
|
||||
type="application/ld+json"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: serializeJsonLd(breadcrumbListSchema(breadcrumbs.vector)),
|
||||
}}
|
||||
/>
|
||||
</Head>
|
||||
<DefaultLayout className="bg-alternative!" stickyNavbar={false}>
|
||||
<ModulesNav activePage={PRODUCT_MODULES_NAMES.VECTOR} docsUrl={pageData.docsUrl} />
|
||||
|
||||
@@ -20,7 +20,8 @@ import { useRouter } from 'next/router'
|
||||
import { PRODUCT_NAMES } from 'shared-data/products'
|
||||
import { Button } from 'ui'
|
||||
|
||||
import { serializeJsonLd, softwareApplicationSchema } from '@/lib/json-ld'
|
||||
import { breadcrumbs } from '@/lib/breadcrumbs'
|
||||
import { breadcrumbListSchema, serializeJsonLd, softwareApplicationSchema } from '@/lib/json-ld'
|
||||
|
||||
const SingleQuote = dynamic(() => import('~/components/Sections/SingleQuote'))
|
||||
|
||||
@@ -82,6 +83,12 @@ function RealtimePage() {
|
||||
),
|
||||
}}
|
||||
/>
|
||||
<script
|
||||
type="application/ld+json"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: serializeJsonLd(breadcrumbListSchema(breadcrumbs.realtime)),
|
||||
}}
|
||||
/>
|
||||
</Head>
|
||||
<DefaultLayout>
|
||||
<ProductsNav activePage={PRODUCT_NAMES.REALTIME} />
|
||||
|
||||
@@ -19,7 +19,8 @@ import Solutions from '@/data/MainProducts'
|
||||
import ApiExamples from '@/data/products/storage/api-examples'
|
||||
import DashboardViewData from '@/data/products/storage/dashboard-carousel.json'
|
||||
import StoragePermissionsData from '@/data/products/storage/permissions-examples'
|
||||
import { serializeJsonLd, softwareApplicationSchema } from '@/lib/json-ld'
|
||||
import { breadcrumbs } from '@/lib/breadcrumbs'
|
||||
import { breadcrumbListSchema, serializeJsonLd, softwareApplicationSchema } from '@/lib/json-ld'
|
||||
|
||||
const APISection = dynamic(() => import('~/components/Sections/APISection'))
|
||||
const SingleQuote = dynamic(() => import('~/components/Sections/SingleQuote'))
|
||||
@@ -69,6 +70,12 @@ function StoragePage() {
|
||||
),
|
||||
}}
|
||||
/>
|
||||
<script
|
||||
type="application/ld+json"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: serializeJsonLd(breadcrumbListSchema(breadcrumbs.storage)),
|
||||
}}
|
||||
/>
|
||||
</Head>
|
||||
<DefaultLayout>
|
||||
<ProductsNav activePage={PRODUCT_NAMES.STORAGE} />
|
||||
|
||||
Reference in new issue
Block a user